I just noticed the function Curl_poll in libcurl.  It has some things that 
could be improved for usage on Windows.

First, Windows Vista and later have an API called WSAPoll.  It works almost the 
same as UNIX poll.

https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll

I haven't checked whether it fails with zero sockets like select() does in 
Windows.

The second thing is that using select() in Windows can be optimized even 
without WSAPoll.  Unlike in POSIX, fd_set is not a bitmap.  It's actually just 
an array of entries and a length field:

typedef struct fd_set {
        u_int fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

In fact, nothing in WinSock requires that what you pass as an fd_set is the C 
type fd_set.  You can just build your own array of whatever size, including 
possibly larger than FD_SETSIZE (*), and pass it to select, provided that this 
structure has the correct alignment and such (-> 4 bytes of padding between 
fd_count and fd_array in Win64, because SOCKET is uintptr_t).  Windows's 
select() is therefore more like poll() than traditional select().

FD_SET is really slow in Windows.  In order to maintain compatibility with a 
bitmap, FD_SET must be idempotent.  FD_SET has to scan the list to see whether 
the socket is already there so it can do nothing in that case.  So you end up 
with O(n^2) behavior in Windows.  The solution is again to build the array 
yourself.  In C++, I've used std::vector<SOCKET> to do this, static_asserting 
that offsetof(fd_set, fd_array) == sizeof(SOCKET), which it is, and memcpying a 
u_int to [0] for fd_count.  It's a hack, but it works.  A custom vector class 
could also be used.

Finally, a minor technical detail: Curl_wait_ms is not equivalent to a select() 
on a socket that doesn't fire and hits the timeout.  This is because select() 
and WSAPoll() are an "alertable wait state" in Windows.  A correct Windows 
implementation of Curl_wait_ms that is intended to mimic select() would use 
SleepEx with the second parameter TRUE, in a loop because unlike select(), 
SleepEx will abort if an APC occurs.  (SleepEx with the second parameter TRUE 
is akin to UNIX usleep with its EINTR return value.)  It's hard to say whether 
this has caused any problems before.

* This works because FD_SETSIZE can be #defined by the application before the 
WinSock headers, just as in UNIX.  Therefore, the API must support applications 
making their own array of whatever size.

-------------------------------------------------------------------
Unsubscribe: https://cool.haxx.se/list/listinfo/curl-library
Etiquette:   https://curl.haxx.se/mail/etiquette.html

Reply via email to