Curl_poll and Windows
Date: Mon, 18 Nov 2019 21:49:50 +0000
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
Received on 2019-11-18