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);