Here is an updated job with easier to read comments:
https://testbot.winehq.org/JobDetails.pl?Key=97235
Le jeudi 2 septembre 2021, 19:08:43 CEST Guillaume Charifi-Hoareau a écrit :
> Thank you for your answer!
>
>
> I spent a lot of time testing a whole lot of different combinations and the
> results still confused me...
>
> > There's also a part of me that would like to know what can possibly be
> > the point of this API; it's hard to even think of what "reasonable"
> > behaviour is otherwise. Do you happen to know?
> >
> > My best guess is that it's meant to explicitly interrupt poll requests
> > somehow (what, I/O cancellation wasn't good enough for them?), in which
> > case it'd be good to explicitly confirm or refute that the "exclusive"
> > poll really does act like a normal poll, and doesn't e.g. return
> > immediately.
>
> I discovered this behavior while investigating an app that seemed to rely on
> them to interrupt some polls, and so far it seems to be its sole purpose.
> There is only one reason I can imagine a use for this instead of CancelIo():
> As the NtDeviceIoControlFile()'s file handle is independent of the socket
> in params, it provides a way to interrupt another poll that waits on the
> same socket without the need to know the file handle that was originally
> given to it.
>
> Regardless its behaviour is kind of crazy.
> I tried to restrict to a subset of the tests to avoid polluting test_poll()
> too much, but indeed it requires an entire function to test it properly.
>
> > I think these tests could be more extensive. For example:
> >
> > * What happens if you try to perform a non-exclusive wait while an
> > exclusive wait is in progress? i.e. the inverse of the test added here.
>
> So after some extensive testing, I'm rather confident that the logic for a
> new poll on a socket is:
> (The main poll is an arbitrary designation for one of the poll waiting on
> the socket) If there is no main poll on the socket, then the new poll
> becomes the main poll. If the main poll is terminated, then there is no
> main poll anymore. If the main poll is exclusive and the new poll is
> exclusive, then the main poll is terminated and the new poll becomes the
> main poll.
>
>
> The flag does not seem to do anything more than this (apart from requiring
> 0x7fffffff00000000 <= timeout <= 0x7fffffffffffffff).
>
> > * Can we verify that exclusive waits are specific to a given socket
> > (i.e. you can simultaneously do an exclusive wait on two different
> > sockets?)
> Yes, the mechanism described earlier seems to be done on a per-socket basis.
> > * Can we test that exclusive waits apply if two unequal sets overlap?
>
> Yes, it works the same way (a single common socket between exclusive polls
> is enough to wake the poll)
>
> > * Can you do an exclusive wait on the same socket from two different
> > threads?
>
> Can yes, didn't want to, but did it anyway :)
> I get the expected result: It works exactly the same as the single threaded
> version.
>
> So I guess I now have enough insight to write a proper implementation.
> The patch for the tests is attached to this job:
> https://testbot.winehq.org/JobDetails.pl? Key=97234[1]
> Feel free to tell me if other tests come to your mind.
>
> Le mercredi 1 septembre 2021, 17:52:14 CEST Guillaume Charifi a écrit :
> > Signed-off-by: Guillaume Charifi <guillaume.charifi@sfr.fr>
> > ---
> >
> > dlls/ws2_32/tests/afd.c | 73 ++++++++++++++++++++++++++++++++++++++++-
> > 1 file changed, 72 insertions(+), 1 deletion(-)
> >
> > diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c
> > index eb7c8ee50de..2568f251b6f 100644
> > --- a/dlls/ws2_32/tests/afd.c
> > +++ b/dlls/ws2_32/tests/afd.c
> > @@ -148,8 +148,10 @@ static void test_poll(void)
> >
> > const struct sockaddr_in bind_addr = {.sin_family = AF_INET,
> >
> > .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; char in_buffer[offsetof(struct
> > afd_poll_params, sockets[3])]; char out_buffer[offsetof(struct
> > afd_poll_params, sockets[3])]; + char out_buffer1[offsetof(struct
> > afd_poll_params, sockets[3])]; struct afd_poll_params *in_params = (struct
> > afd_poll_params *)in_buffer; struct afd_poll_params *out_params = (struct
> > afd_poll_params *)out_buffer; + struct afd_poll_params *out_params1 =
> > (struct afd_poll_params *)out_buffer1; int large_buffer_size = 1024 *
> > 1024;
> >
> > SOCKET client, server, listener;
> > struct sockaddr_in addr;
> >
> > @@ -157,13 +159,14 @@ static void test_poll(void)
> >
> > IO_STATUS_BLOCK io;
> > LARGE_INTEGER now;
> > ULONG params_size;
> >
> > - HANDLE event;
> > + HANDLE event, event1;
> >
> > int ret, len;
> >
> > large_buffer = malloc(large_buffer_size);
> > memset(in_buffer, 0, sizeof(in_buffer));
> > memset(out_buffer, 0, sizeof(out_buffer));
> > event = CreateEventW(NULL, TRUE, FALSE, NULL);
> >
> > + event1 = CreateEventW(NULL, TRUE, FALSE, NULL);
> >
> > listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> > ret = bind(listener, (const struct sockaddr *)&bind_addr,
> >
> > sizeof(bind_addr)); @@ -208,10 +211,77 @@ static void test_poll(void)
> >
> > IOCTL_AFD_POLL, in_params, params_size, out_params,
> >
> > params_size); ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret);