Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make SO_POLL complete asynchronously in IOS_NET SO #8813

Merged
merged 1 commit into from Jun 25, 2020
Merged

Make SO_POLL complete asynchronously in IOS_NET SO #8813

merged 1 commit into from Jun 25, 2020

Conversation

nbouteme
Copy link
Contributor

@nbouteme nbouteme commented May 16, 2020

This is to make the behavior more consistent with the behavior on console.

Currently, SO_POLL blocks the PPC until poll() completes, so this prevents PPC threads on DolphinOS and libogc's LWP from running while waiting for the call to complete, while on a real console, IOS lets the PPC run, as with most other calls, and notifies the PPC through the usual mechanism.

I don't know if how I implemented it is the preferred way in regard to Dolphin's internals: Basically each SO_POLL operation gets queued into a poll table with the associated request, and when an event happens on a socket that is referenced in a poll table in WiiSockMan::Update, an IPC reply is generated for the request.

Copy link
Contributor

@sepalani sepalani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR sounds fine to me but there are things that should be addressed.

  • You should probably squashed your 2 commits together.
  • Please, have a look at Contributing.md.
  • Then, check with clang-format that the formatting is correct.

Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
const int write_event = (POLLOUT | POLLWRBAND);
const int read_event = (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI);
ufds.push_back({sock.fd, read_event | write_event, 0});
nfds = std::max(nfds, sock.wii_fd + 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, the wii_fd should be used here.

Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
@delroth
Copy link
Member

delroth commented May 17, 2020

This will leave an unanswered/hanging IPC when saving/loading states.

@nbouteme
Copy link
Contributor Author

nbouteme commented May 17, 2020

Thanks for the comments. I'll fix these ASAP.

This will leave an unanswered/hanging IPC when saving/loading states.

This means I should add a DoState method to WiiSockMan, and answer IPC calls with errors when loading from a save state, right?

EDIT: Instead of making SO_POLL return an error, I made it return the events POLLHUP | POLLERR which made more sense.

@delroth
Copy link
Member

delroth commented May 17, 2020

I think that would make the most sense, yes.

Source/Core/Core/IOS/Network/Socket.h Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
auto socket_iter = WiiSockets.begin();
auto end_socks = WiiSockets.end();
ufds.clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't the ufds variable be local to this function rather than a class member? It doesn't seem to be used elsewhere.

Copy link
Contributor Author

@nbouteme nbouteme May 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really appropriate to allocate and deallocate a vector in an update function? clear() preserves the allocated capacity.

Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
n++;
}

auto mid = std::partition(pending_polls.begin(), pending_polls.end(), [&](auto& pcmd) {
Copy link
Contributor

@sepalani sepalani May 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't a similar logic like WiiSocket::Update be used here or is there a performance issue?

auto it = pending_polls.begin();
while (it != pending_polls.end())
{
  // SOME CODE
  if (shall_return || has_timeout || whatever_exit_code)
  {
    // Write IPC Reply
    it = pending_polls.erase(it);
  }
  else
  {
    ++it;
  }

This logic might be able to get rid of the finished_polls and failed_polls member variables.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just replaced that with an equivalent erase-remove idiom.
It still allowed me to remove these two member variables. The only performance benefit to doing it this way instead of iterating is to do only one erase operation, which doesn't really matter in typical cases (IOS doesn't allow polling more than a couple tens of file descriptors). The other benefits in using the stdlib for container manipulations are purely theoretical, but are (maybe) still there. But mostly being less error prone.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only concern about doing it this way, is that it will call begin()/end()/remove_if()/erase() every time regardless of the pending_polls size. I also think that the whole update poll logic should belong to its own method: "UpdatePollCommands" or something like that.

Copy link
Contributor Author

@nbouteme nbouteme May 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most (if not all - can't speak for MSVC) of these calls are inlined, the only external reference (on g++) is a realloc_insert call, probably for the push_back on ufds, which will probably never get called since its size is preallocated before the loop. I'll move all that logic into a separate function, though, it's true that it's too big to leech off another method.

Screenshot from 2020-05-19 19-49-48

Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
@nbouteme
Copy link
Contributor Author

Hm, this type error on the poll() call didn't happen on the previous windows builds. And MSVC doesn't seem to like the implicit constexpr capture from outside the scope of a lambda, even though the standard allows it. Or maybe I'm reading the error wrong.

@JosJuice
Copy link
Member

And MSVC doesn't seem to like the implicit constexpr capture from outside the scope of a lambda, even though the standard allows it.

I've had that problem as well (though only when building with CMake, for some reason). I'd suggest just capturing it to make MSVC happy.

{
int native;
int wii;
} mapping[] = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this could (or even should) be static (and maybe const), perhaps even constexpr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constexpr seems to work

@nbouteme
Copy link
Contributor Author

nbouteme commented May 20, 2020

I set error_event as a constexpr static since on MSVC, explicitly capturing a constexpr seem to make the compiler disregard the constexpr qualifier? But this makes GCC 10 on Linux print a warning about the variable being set but unused despite being really used...

And about poll, I had to cast to an u32 since Windows doesn't provide the posix nfds_t type...


void WiiSockMan::UpdatePollCommands()
{
constexpr static int error_event = (POLLHUP | POLLERR);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The static isn't necessary here. Given it's a trivial primitive type, this will be constant folded into expressions anyway (especially given it's constexpr).

Our convention is also to place the lifetime specifiers before mutability ones, just to keep in mind for the future.

Copy link
Contributor Author

@nbouteme nbouteme May 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know, but it won't build on Windows without it due to a compiler bug. Other alternative is capturing it explicitly but this implicitly discards the constexpr qualifier

Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.h Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.h Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.h Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.h Outdated Show resolved Hide resolved
Copy link
Contributor

@sepalani sepalani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed a few things here and there.

Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
@@ -685,25 +686,23 @@ s32 WiiSockMan::DeleteSocket(s32 s)

void WiiSockMan::Update()
{
s32 nfds = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't call poll if WiiSockets is empty. Otherwise, it spams error logs on Windows. Regardless, you still need to clear previous ufds values and update poll commands.

@sepalani
Copy link
Contributor

It seems Windows is still having issues, I'll have look to see what is going on.

Copy link
Contributor

@sepalani sepalani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please squash all your commits into a single one?

@@ -13,6 +13,13 @@
#include <ws2tcpip.h>
#endif

// WSAPoll doesn't support POLLPRI and POLLWRBAND flags
#ifdef _WIN32
#define UNSUPPORTED_WSAPOLL POLLPRI | POLLWRBAND
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that was an oversight of #3316. It was missing parentheses.

Can you please change it to a constexpr constant instead

constexpr int UNSUPPORTED_WSAPOLL = POLLPRI | POLLWRBAND;

@sepalani
Copy link
Contributor

This PR introduces a regression in DJ Hero. I'll try to see where it's from.

On master

Network - DemonWare::SetAuthenticationAddress AUTH IP = 185.34.107.68   LSG IP = 185.34.107.72
BeginAccountStage - Success, ENETWORK_MANAGER_ACCOUNT.
InternalAccountUpdate - Success, ENETWORK_MANAGER_LOBBY.
InternalLobbyConnectUpdate - Success, ENETWORK_MANAGER_READY.
**** Lobby connected successfully ****

On this PR

Network - DemonWare::SetAuthenticationAddress AUTH IP = 185.34.107.68   LSG IP = 185.34.107.72
BeginAccountStage - Success, ENETWORK_MANAGER_ACCOUNT.
CNetworkAuthenticateAccountState - Authorization server failed in an unexpected manner.
Failed to connect to lobby service.
CNetworkManager::Fail_InternalAccount()
****** Global Network Error: 12.

@nbouteme
Copy link
Contributor Author

Does it require real nand certificates to reproduce?
What are the required steps?

@sepalani
Copy link
Contributor

You'll need a real NAND and find the logging function (OSReport, ___blank, etc.) which should be found using "Generate symbols from > Signature database" if you're lucky. After, that these messages should appear on the log console if OSReport logs are enabled.

At first glance, it seems to be a timing-issue since I can reproduce the issue on master if I put a breakpoint on the SOConnect function and wait about 3~5 seconds before resuming.

  • On master, the game says that the DJ Hero servers are unavailable.
  • On your PR, it says that the game servers are unavailable.

@nbouteme
Copy link
Contributor Author

Unfortunately I don't have access to a real nand at the moment.
Does IOCTL_SO_POLL appear in the log at debug level?

@sepalani
Copy link
Contributor

No, it doesn't. I just hope, that's not yet another issue with WSAPoll implementation, otherwise, it will be a blocker for Windows.

@nbouteme
Copy link
Contributor Author

nbouteme commented May 28, 2020

I managed to reproduce the in-game error message without certificates under Linux. OSReport is detected without generating symbols from the signature database, but I don't see these messages.

@sepalani
Copy link
Contributor

There are multiple logging functions, so you might need to generate symbols or do a bit of reverse-engineering to find this one.

@nbouteme
Copy link
Contributor Author

nbouteme commented May 28, 2020

It actually does multiple SO_POLL calls, see attached log.
dolphin.log

@nbouteme
Copy link
Contributor Author

nbouteme commented May 28, 2020

Ok, I've figured it out.

It seems like when you do a poll with both POLLOUT and POLLWRNORM, the OS won't set POLLWRNORM even though they are equivalent. I think the most obvious solution is to re poll with a 0 timeout with the correct events.

Well, at least doing so lets me get the "DJ Hero servers are unavailable" error on Linux. But I don't know if Windows will behave the same, and how nand certs may influence the login process.

@sepalani
Copy link
Contributor

sepalani commented May 29, 2020

IMHO, that is not the best way to address this issue since:

  • Some FDs might become invalid (after a close for instance) on the second poll call and it might fail.
  • Some poll implementations might clear or check revents before assigning new values to it.
  • WSAPoll seems to shadow some error codes.

Unfortunately, the best way I can see to address the issue would be to revert back to select on Update and call poll for each pending_polls with the appropriate events set on UpdatePollCommands.

I expect small performance impacts on games using poll but at the very list it won't be blocking anymore.

@nbouteme
Copy link
Contributor Author

Shouldn't most of these issues be solved simply by lazily pruning closed sockets from each pending_commands ? I don't see what reverting back to select will accomplish. The remaining issue is that poll may get called on pending_commands after a savestate load, so I probably should just clear the revents fields before ending the update, and not poll commands with non-zero revents.

@sepalani
Copy link
Contributor

Reverting back to select will fix Update regressions on Windows since WSAPoll introduces issues due to its poll implementation being different.

On top of that you pointed out an issue forcing Dolphin to use poll twice while monitoring all events. In other word, I'm pretty sure that relying on such behaviours will cause other issues since not all poll implementations behave the same way.

In sum, it's safer and less error-prone (and probably more accurate) to store poll events in the PollCommand structure and call each requested poll in an async manner in UpdatePollCommands.

Copy link
Contributor

@sepalani sepalani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll write some hardware tests to confirm some of these.

auto& pfds = pcmd.wii_fds;
// Happens only on savestate load
int count;
if (pcmd.wii_fds[0].revents & error_event)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you shouldn't bother with the pollfd structures when a savestate is loaded. Just EnqueueIPCReply with -SO_ENETRESET.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't games get confused from receiving SO_ENETRESET after they initialized the SO module? I think the worst they are expected to handle is every socket hanging up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. Technically, I assume that could happens if you loose your Wi-Fi/Ethernet connection. Anyway, feel free to disregard this comment as your approach is valid.

Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
@sepalani
Copy link
Contributor

Here is an hardware test:
https://gist.github.com/sepalani/21db2c0d703f0350590cab76062360b5

Regarding the results:

  • The valid nfds are 1 <= nfds <= 24.
  • Negative and invalid fds are "ignored" but ...
  • revents is always clear/set to zero.
  • The pollfd order doesn't seem to matter since they are all set properly if an event occurs.

Run the commented tests at your own risk. The result is the one expected, poll will work when the fds become valid BUT it seems to break most network features afterward. Basically, you'll have IOS_IOctl(v) returning successful return values, however, you can't interact with the socket properly. Listening on a port appears to work (based on the return values from IOS) but doesn't when you try to connect to it.

Copy link
Contributor

@sepalani sepalani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your patience. After these changes based on hardware tests, I believe this PR should be ready.

Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Source/Core/Core/IOS/Network/IP/Top.cpp Outdated Show resolved Hide resolved
auto& pfds = pcmd.wii_fds;
// Happens only on savestate load
int count;
if (pcmd.wii_fds[0].revents & error_event)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. Technically, I assume that could happens if you loose your Wi-Fi/Ethernet connection. Anyway, feel free to disregard this comment as your approach is valid.

Source/Core/Core/IOS/Network/Socket.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@sepalani sepalani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please squash your commits into a single one.

// Negative timeout indicates wait forever
const s64 timeout = static_cast<s64>(Memory::Read_U64(request.buffer_in));

const unsigned nfds = request.buffer_out_size / 0xc;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const u32 nfds


std::vector<pollfd_t> ufds(nfds);

for (int i = 0; i < nfds; ++i)
for (unsigned i = 0; i < nfds; ++i)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

u32 i = 0;

}
else
{
// Update FDs to a negative FD in case they were closed:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment needs to be changed to something more fitting like:

// Make the behavior of poll consistent across platforms by not passing:
//  - Set with invalid fds, revents is set to 0 (Linux) or POLLNVAL (Windows)
//  - Set without a valid socket, raises an error on Windows

return GetHostSocket(Memory::Read_U32(pcmd.buffer_out + 0xc * i)) > 0;
});
// move all the valid pollfds to the front of the vector
for (auto i = 0; i < mid - original_order.begin(); ++i)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't mid - original_order.begin() be replaced with n_valid?

auto mid = std::partition(original_order.begin(), original_order.end(), [&](auto i) {
return GetHostSocket(Memory::Read_U32(pcmd.buffer_out + 0xc * i)) > 0;
});
// move all the valid pollfds to the front of the vector
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Move

if (count < 0)
count = GetNetErrorCode(count, "UpdatePollCommands", false);

// move everything back to where they were
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Move

[this](auto& pcmd) {
const auto request = Request(pcmd.request_addr);
auto& pfds = pcmd.wii_fds;
int count = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be renamed ret as it might not return the count but a network error code.

@sepalani
Copy link
Contributor

sepalani commented Jun 1, 2020

Could you please change/remove the commit description:

This also replaces the select() call in WiiSockMan::Update by a poll call ...

Do you have any games or homebrew that use SOPoll so we can test this PR against?

@nbouteme
Copy link
Contributor Author

nbouteme commented Jun 1, 2020

Well besides DJ Hero and your test suite, no, I don't. I'll try to look around if I find some larger homebrew.

I'd suspect online games with a large active modding community like MKWii to be very likely to use it.

@nbouteme
Copy link
Contributor Author

nbouteme commented Jun 1, 2020

On the homebrew side:
WiiRadio
WiiFlow Lite
Quake GX

Copy link
Contributor

@sepalani sepalani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit description is still not amended.

I haven't found issues with this PR while doing some testing. I haven't tried multiplayer games yet.

int ret = 0;

// Happens only on savestate load
if (pcmd.wii_fds[0].revents & error_event)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (pfds[0].revents & error_event)

// Happens only on savestate load
if (pcmd.wii_fds[0].revents & error_event)
{
ret = static_cast<int>(pcmd.wii_fds.size());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ret = static_cast<int>(pfds.size());

@nbouteme
Copy link
Contributor Author

nbouteme commented Jun 1, 2020

I'm sorry, I'm not much proficient with git. Isn't amending just git commit --amend and force push, or am I forgetting something here? Or is the description still not what you wanted?

@sepalani
Copy link
Contributor

sepalani commented Jun 2, 2020

@nbouteme No worries. Feel free to ask or go over IRC if you're having troubles with git.

In fact, you're amending your commit but not the commit message/description.
https://help.github.com/en/github/committing-changes-to-your-project/changing-a-commit-message

During the amend process, you need to change/remove the commit description (i.e. the sentences under the commit message) with you text editor. Alternatively, you can do it directly from command-line:

git commit --amend -m "IOS/Network: Make poll complete asynchronously"

If you want to change the commit description you'll need to pass an extra -m flag:

git commit --amend -m "commit message" -m "commit description"

You can check your commit history using git log and see if amending worked. After that, you can force push.

Copy link
Contributor

@sepalani sepalani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise, the logic LGTM. I'm going to test it against some multiplayer games later today to confirm it doesn't introduce regressions.

const s64 timeout = static_cast<s64>(Memory::Read_U64(request.buffer_in));

const u32 nfds = request.buffer_out_size / 0xc;
if (nfds <= 0 || nfds > WII_SOCKET_FD_MAX)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (nfds == 0 || nfds > WII_SOCKET_FD_MAX)

std::iota(original_order.begin(), original_order.end(), 0);
// Select indices with valid fds
auto mid = std::partition(original_order.begin(), original_order.end(), [&](auto i) {
return GetHostSocket(Memory::Read_U32(pcmd.buffer_out + 0xc * i)) > 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, 0 is a valid socket fd, so it should be >= 0.

const u32 nfds = request.buffer_out_size / 0xc;
if (nfds <= 0 || nfds > WII_SOCKET_FD_MAX)
{
ERROR_LOG(IOS_NET, "IOCTL_SO_POLL failed: Invalid array size %d, ret= %d", nfds, -SO_EINVAL);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ret=%d (without the space)

Copy link
Contributor

@sepalani sepalani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Tested on Windows 10 with Super Smash Bros. Brawl, Mario Strikers Charged Football, DJHero. I didn't find regression with them. I tested SSBB & MSC multiplayer modes using altwfc servers.

@nbouteme nbouteme requested a review from BhaaLseN June 10, 2020 05:15
@nbouteme nbouteme requested a review from lioncash June 19, 2020 19:04
@sepalani
Copy link
Contributor

@nbouteme Could you please rebase your PR to the latest master and update the STATE_VERSION?

@JMC47 JMC47 merged commit 502ab78 into dolphin-emu:master Jun 25, 2020
@JMC47
Copy link
Contributor

JMC47 commented Jun 26, 2020

Is there any particular game or case that this helps so I could properly make a demonstration of how it affects things? If it's all internally changing things, that's fine too, I just wanted to know if I could make some media for the Progress Report.

Thanks!

@nbouteme
Copy link
Contributor Author

Thanks for merging, and also thanks a lot to sepalani who helped a lot to make this a proper PR.

Unfortunately I don't think this change would be really visible to end users : it theoretically eliminates stutters, or possible deadlocks causing games to hang, when a game uses SO_POLL with a non-zero timeout, but I'm don't really know whether wii games do that often. I don't think they do, or else the stutter would have probably broken some games and this would have been fixed way earlier. On the other hand, it may also help with some homebrews but I don't think this is most users use cases. My use case was a mod to add some online functionnality to a game that lacked it, but using poll with a timeout of 0 didn't sit right with me... It's not as efficient as I'd like it to be because in the end, poll and select still need to be called with a 0 timeout, since IOS HLE runs on the emulation thread. This still allows people to write mods and have a behavior that's more in line with what would happen on a real wii. A thread waiting on a SO_POLL call to return will remain dormant instead of being immediately woken up then immediately sleep again, so it reduces CPU usage on PPC.

If IOS HLE ran on a separate thread, then the socket polling mechanism wouldn't be executed as often and would reduce CPU usage in HLE code by a bit, but this poses a lot of harder challenges. But if/when this will eventually be done, then anything that uses non-zero timeouts on SO_POLL will use a bit less CPU. So yeah, it's not that big of a deal in the end, it's more like a convenience for mod developers.

@JMC47
Copy link
Contributor

JMC47 commented Jun 26, 2020

Yeah, this mixed with other async PRs seem to have eliminated some of the stuttering when connecting to servers online.

If there's no great demonstration yet, once more of it is done I'll definitely be able to show off a bigger change. Mario Kart Wii in particular used to be a mess of 3 - 4 second stutters while connecting, and it's slowly been getting better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
7 participants