Subj : Win32: the PREFER_POLL code paths call WSAPoll() in ways it rejects (W To : GitLab note in main/sbbs From : Rob Swindell Date : Sun Aug 02 2026 19:57:21 https://gitlab.synchro.net/main/sbbs/-/issues/1212#note_9899 The semantics point is right, and it's why this issue recommends against flipping the define rather than for it — `WSAPoll(NULL, 0, t)` returns `WSAEINVAL` where POSIX `poll()` treats it as a sleep, and `POLLPRI` is rejected outright. On the capacity point I couldn't reproduce a 64-descriptor limit. Probe built with the **default** `FD_SETSIZE` (64), polling 512 descriptors where the only readable one is placed last: ``` FD_SETSIZE = 64 (default) WSAPoll(512 fds) = 1 err=0 first ready index = 511 (listener is 511) ``` If `WSAPoll()` were backed by a 64-entry `fd_set`, index 511 could not be reported at all. Whatever it does internally, its observable capacity isn't `FD_SETSIZE`. Tested only on Windows 11 26200 (MSVC 19.44) — if the recollection is from Win7/Win8-era `WSAPoll()`, it may well have been accurate then and changed since. That's its own argument for keeping Windows on `select()` while older versions are supported, which is where this issue lands anyway. Worth noting the linked page is also what sanctions the `FD_SETSIZE` fix that just landed in 031df44f62: > If an application is designed to be capable of working with more than 64 sockets using the **select** and **WSAPoll** functions, the implementor should define the manifest **FD_SETSIZE** in every source file before including the *Winsock2.h* header file. One way of doing this may be to include the definition within the compiler options in the makefile. For example, you could add "-DFD_SETSIZE=128" as an option to the compiler command line for Microsoft C++. and that it puts the provider's own ceiling at available memory, with `FD_SETSIZE` affecting "only … the **FD_XXX** macros". The `FD_SET and select` heading grouping `WSAPoll` in with `select` is probably the source of the confusion — `WSAPoll()` doesn't take an `fd_set` at all.
wsapoll_scale.c — the 512-descriptor probe ```c /* Is WSAPoll() itself limited to FD_SETSIZE (64) descriptors, as it would be if it were implemented over Windows select()? Built WITHOUT redefining FD_SETSIZE, so FD_SETSIZE is the default 64. */ #include #include #include #pragma comment(lib, "ws2_32.lib") #define N 512 int main(void) { WSADATA wd; WSAPOLLFD fds[N]; SOCKET pad[N], lsock, csock; struct sockaddr_in sa; u_long nb = 1; int i, len, r, ready_idx = -1; WSAStartup(MAKEWORD(2, 2), &wd); printf("FD_SETSIZE = %d (default)\n", (int)FD_SETSIZE); /* N-1 permanently-quiet UDP sockets */ for (i = 0; i < N - 1; i++) { pad[i] = socket(AF_INET, SOCK_DGRAM, 0); memset(&sa, 0, sizeof sa); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = 0; if (bind(pad[i], (struct sockaddr *)&sa, sizeof sa) != 0) { printf("bind failed at %d, err=%d\n", i, WSAGetLastError()); return 1; } fds[i].fd = pad[i]; fds[i].events = POLLIN; fds[i].revents = 0; } /* the ONLY readable descriptor, placed last - far beyond 64 */ lsock = socket(AF_INET, SOCK_STREAM, 0); memset(&sa, 0, sizeof sa); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = 0; bind(lsock, (struct sockaddr *)&sa, sizeof sa); listen(lsock, 5); len = sizeof sa; getsockname(lsock, (struct sockaddr *)&sa, &len); csock = socket(AF_INET, SOCK_STREAM, 0); ioctlsocket(csock, FIONBIO, &nb); connect(csock, (struct sockaddr *)&sa, sizeof sa); Sleep(300); fds[N - 1].fd = lsock; fds[N - 1].events = POLLIN; fds[N - 1].revents = 0; r = WSAPoll(fds, N, 2000); for (i = 0; i < N; i++) { if (fds[i].revents != 0) { ready_idx = i; break; } } printf("WSAPoll(%d fds) = %d err=%d first ready index = %d (listener is %d)\n", N, r, WSAGetLastError(), ready_idx, N - 1); return 0; } ```
— *Authored by Claude (Claude Code), on behalf of @rswindell* --- SBBSecho 3.37-Linux * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705) .