Skip to content

Commit acd6c32

Browse files
committed
Rewrite Windows accept()
This change should fix the Windows issues Qt Creator has been having, by ensuring accept() and accept4() work in O_NONBLOCK mode. I switched away from AcceptEx() which is buggy, back to using WSAAccept(). This requires making a tradeoff where we have to accept a busy loop. However it is low latency in nature, just like our new and improved Windows poll() code. I was furthermore able to eliminate a bunch of Windows-related test todos.
1 parent 6f868fe commit acd6c32

20 files changed

+623
-210
lines changed

libc/calls/checkcancel.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include "libc/intrin/weaken.h"
2222
#include "libc/thread/posixthread.internal.h"
2323

24-
int _check_cancel(void) {
24+
textwindows int _check_cancel(void) {
2525
if (_weaken(_pthread_cancel_ack) && //
2626
_pthread_self() && !(_pthread_self()->pt_flags & PT_NOCANCEL) &&
2727
atomic_load_explicit(&_pthread_self()->pt_canceled,

libc/calls/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ int __ensurefds(int);
2525
uint32_t sys_getuid_nt(void);
2626
int __ensurefds_unlocked(int);
2727
void __printfds(struct Fd *, size_t);
28+
int __sigcheck(sigset_t, bool);
2829
int CountConsoleInputBytes(void);
2930
int FlushConsoleInputBytes(void);
3031
int64_t GetConsoleInputHandle(void);

libc/calls/park.c

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,43 +17,28 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/internal.h"
20-
#include "libc/calls/sig.internal.h"
20+
#include "libc/calls/struct/sigset.h"
2121
#include "libc/intrin/atomic.h"
22-
#include "libc/intrin/weaken.h"
2322
#include "libc/nt/synchronization.h"
24-
#include "libc/sysv/consts/sicode.h"
25-
#include "libc/sysv/errfuns.h"
2623
#include "libc/thread/posixthread.internal.h"
2724
#ifdef __x86_64__
2825

2926
// returns 0 on timeout or spurious wakeup
3027
// raises EINTR if a signal delivery interrupted wait operation
3128
// raises ECANCELED if this POSIX thread was canceled in masked mode
32-
static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
29+
textwindows static int _park_thread(uint32_t msdelay, sigset_t waitmask,
3330
bool restartable) {
34-
int sig, handler_was_called;
35-
if (_check_cancel() == -1)
31+
if (__sigcheck(waitmask, restartable) == -1)
3632
return -1;
37-
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))
38-
goto HandleSignal;
3933
int expect = 0;
4034
atomic_int futex = 0;
4135
struct PosixThread *pt = _pthread_self();
4236
pt->pt_blkmask = waitmask;
4337
atomic_store_explicit(&pt->pt_blocker, &futex, memory_order_release);
4438
bool32 ok = WaitOnAddress(&futex, &expect, sizeof(int), msdelay);
4539
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
46-
if (ok && _weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
47-
HandleSignal:
48-
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
49-
if (_check_cancel() == -1)
50-
return -1;
51-
if (handler_was_called & SIG_HANDLED_NO_RESTART)
52-
return eintr();
53-
if (handler_was_called & SIG_HANDLED_SA_RESTART)
54-
if (!restartable)
55-
return eintr();
56-
}
40+
if (ok && __sigcheck(waitmask, restartable) == -1)
41+
return -1;
5742
return 0;
5843
}
5944

libc/calls/poll-nt.c

Lines changed: 56 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,15 @@
1616
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
19-
#include "libc/assert.h"
20-
#include "libc/calls/calls.h"
2119
#include "libc/calls/internal.h"
22-
#include "libc/calls/sig.internal.h"
2320
#include "libc/calls/state.internal.h"
24-
#include "libc/calls/struct/sigaction.h"
21+
#include "libc/calls/struct/sigset.h"
2522
#include "libc/calls/struct/sigset.internal.h"
2623
#include "libc/calls/struct/timespec.h"
2724
#include "libc/calls/syscall_support-nt.internal.h"
28-
#include "libc/dce.h"
29-
#include "libc/errno.h"
3025
#include "libc/intrin/atomic.h"
3126
#include "libc/intrin/fds.h"
32-
#include "libc/intrin/strace.h"
33-
#include "libc/intrin/weaken.h"
3427
#include "libc/macros.h"
35-
#include "libc/mem/mem.h"
3628
#include "libc/nt/console.h"
3729
#include "libc/nt/enum/filetype.h"
3830
#include "libc/nt/enum/wait.h"
@@ -42,22 +34,13 @@
4234
#include "libc/nt/runtime.h"
4335
#include "libc/nt/struct/pollfd.h"
4436
#include "libc/nt/synchronization.h"
45-
#include "libc/nt/thread.h"
46-
#include "libc/nt/thunk/msabi.h"
4737
#include "libc/nt/time.h"
4838
#include "libc/nt/winsock.h"
49-
#include "libc/runtime/runtime.h"
5039
#include "libc/sock/internal.h"
5140
#include "libc/sock/struct/pollfd.h"
52-
#include "libc/sock/struct/pollfd.internal.h"
53-
#include "libc/stdio/sysparam.h"
5441
#include "libc/sysv/consts/o.h"
55-
#include "libc/sysv/consts/poll.h"
56-
#include "libc/sysv/consts/sicode.h"
57-
#include "libc/sysv/consts/sig.h"
5842
#include "libc/sysv/errfuns.h"
5943
#include "libc/thread/posixthread.internal.h"
60-
#include "libc/thread/tls.h"
6144
#ifdef __x86_64__
6245

6346
#define POLL_INTERVAL_MS 10
@@ -75,47 +58,40 @@
7558
#define POLLPRI_ 0x0400 // MSDN unsupported
7659
// </sync libc/sysv/consts.sh>
7760

78-
textwindows static dontinline struct timespec sys_poll_nt_now(void) {
61+
textwindows dontinline static struct timespec sys_poll_nt_now(void) {
7962
uint64_t hectons;
8063
QueryUnbiasedInterruptTimePrecise(&hectons);
8164
return timespec_fromnanos(hectons * 100);
8265
}
8366

84-
textwindows static int sys_poll_nt_sigcheck(sigset_t sigmask) {
85-
int sig, handler_was_called;
86-
if (_check_cancel() == -1)
87-
return -1;
88-
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(sigmask))) {
89-
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, sigmask);
90-
if (_check_cancel() == -1)
91-
return -1;
92-
if (handler_was_called)
93-
return eintr();
67+
textwindows static uint32_t sys_poll_nt_waitms(struct timespec deadline) {
68+
struct timespec now = sys_poll_nt_now();
69+
if (timespec_cmp(now, deadline) < 0) {
70+
struct timespec remain = timespec_sub(deadline, now);
71+
int64_t millis = timespec_tomillis(remain);
72+
uint32_t waitfor = MIN(millis, 0xffffffffu);
73+
return MIN(waitfor, POLL_INTERVAL_MS);
74+
} else {
75+
return 0; // we timed out
9476
}
95-
return 0;
9677
}
9778

9879
// Polls on the New Technology.
9980
//
10081
// This function is used to implement poll() and select(). You may poll
10182
// on sockets, files and the console at the same time. We also poll for
10283
// both signals and posix thread cancelation, while the poll is polling
103-
textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
104-
uint32_t *ms, sigset_t sigmask) {
105-
bool ok;
106-
uint64_t millis;
84+
textwindows static int sys_poll_nt_actual(struct pollfd *fds, uint64_t nfds,
85+
struct timespec deadline,
86+
sigset_t waitmask) {
10787
int fileindices[64];
10888
int sockindices[64];
10989
int64_t filehands[64];
11090
struct PosixThread *pt;
11191
int i, rc, ev, kind, gotsocks;
11292
struct sys_pollfd_nt sockfds[64];
113-
struct timespec deadline, remain, now;
11493
uint32_t cm, fi, wi, sn, pn, avail, waitfor, already_slept;
11594

116-
waitfor = ms ? *ms : -1u;
117-
deadline = timespec_add(sys_poll_nt_now(), timespec_frommillis(waitfor));
118-
11995
// ensure revents is cleared
12096
for (i = 0; i < nfds; ++i)
12197
fds[i].revents = 0;
@@ -171,7 +147,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
171147
rc += !!fds[i].revents;
172148
}
173149
__fds_unlock();
174-
if (rc)
150+
if (rc == -1)
175151
return rc;
176152

177153
// perform poll operation
@@ -191,10 +167,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
191167
if ((ev & POLLWRNORM_) && !(ev & POLLRDNORM_)) {
192168
fds[fi].revents = fds[fi].events & (POLLRDNORM_ | POLLWRNORM_);
193169
} else if (GetFileType(filehands[i]) == kNtFileTypePipe) {
194-
ok = PeekNamedPipe(filehands[i], 0, 0, 0, &avail, 0);
195-
POLLTRACE("PeekNamedPipe(%ld, 0, 0, 0, [%'u], 0) → {%hhhd, %d}",
196-
filehands[i], avail, ok, GetLastError());
197-
if (ok) {
170+
if (PeekNamedPipe(filehands[i], 0, 0, 0, &avail, 0)) {
198171
if (avail)
199172
fds[fi].revents = POLLRDNORM_;
200173
} else if (GetLastError() == kNtErrorHandleEof ||
@@ -222,23 +195,15 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
222195
}
223196

224197
// determine how long to wait
225-
now = sys_poll_nt_now();
226-
if (timespec_cmp(now, deadline) < 0) {
227-
remain = timespec_sub(deadline, now);
228-
millis = timespec_tomillis(remain);
229-
waitfor = MIN(millis, 0xffffffffu);
230-
waitfor = MIN(waitfor, POLL_INTERVAL_MS);
231-
} else {
232-
waitfor = 0; // we timed out
233-
}
198+
waitfor = sys_poll_nt_waitms(deadline);
234199

235200
// check for events and/or readiness on sockets
236201
// we always do this due to issues with POLLOUT
237202
if (sn) {
238203
// if we need to wait, then we prefer to wait inside WSAPoll()
239204
// this ensures network events are received in ~10µs not ~10ms
240205
if (!rc && waitfor) {
241-
if (sys_poll_nt_sigcheck(sigmask))
206+
if (__sigcheck(waitmask, false))
242207
return -1;
243208
already_slept = waitfor;
244209
} else {
@@ -253,7 +218,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
253218
++rc;
254219
}
255220
} else if (already_slept) {
256-
if (sys_poll_nt_sigcheck(sigmask))
221+
if (__sigcheck(waitmask, false))
257222
return -1;
258223
}
259224
} else {
@@ -269,7 +234,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
269234
// this ensures low latency for apps like emacs which with no sock
270235
// here we shall actually report that something can be written too
271236
if (!already_slept) {
272-
if (sys_poll_nt_sigcheck(sigmask))
237+
if (__sigcheck(waitmask, false))
273238
return -1;
274239
pt = _pthread_self();
275240
filehands[pn] = pt->pt_semaphore = CreateSemaphore(0, 0, 1, 0);
@@ -283,7 +248,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
283248
return __winerr();
284249
} else if (wi == pn) {
285250
// our semaphore was signalled
286-
if (sys_poll_nt_sigcheck(sigmask))
251+
if (__sigcheck(waitmask, false))
287252
return -1;
288253
} else if ((wi ^ kNtWaitAbandoned) < pn) {
289254
// this is possibly because a process or thread was killed
@@ -328,7 +293,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
328293
} else {
329294
// should only be possible on kNtWaitTimeout or semaphore abandoned
330295
// keep looping for events and we'll catch timeout when appropriate
331-
if (sys_poll_nt_sigcheck(sigmask))
296+
if (__sigcheck(waitmask, false))
332297
return -1;
333298
}
334299
}
@@ -341,11 +306,44 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
341306
return rc;
342307
}
343308

309+
textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
310+
struct timespec deadline,
311+
const sigset_t waitmask) {
312+
uint32_t waitms;
313+
int i, n, rc, got = 0;
314+
315+
// fast path
316+
if (nfds <= 63)
317+
return sys_poll_nt_actual(fds, nfds, deadline, waitmask);
318+
319+
// clumsy path
320+
for (;;) {
321+
for (i = 0; i < nfds; i += 64) {
322+
n = nfds - i;
323+
n = n > 64 ? 64 : n;
324+
rc = sys_poll_nt_actual(fds + i, n, timespec_zero, waitmask);
325+
if (rc == -1)
326+
return -1;
327+
got += rc;
328+
}
329+
if (got)
330+
return got;
331+
if (!(waitms = sys_poll_nt_waitms(deadline)))
332+
return 0;
333+
if (_park_norestart(waitms, waitmask) == -1)
334+
return -1;
335+
}
336+
}
337+
344338
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
345339
const sigset_t *sigmask) {
346340
int rc;
341+
struct timespec now, timeout, deadline;
347342
BLOCK_SIGNALS;
348-
rc = sys_poll_nt_impl(fds, nfds, ms, sigmask ? *sigmask : 0);
343+
now = ms ? sys_poll_nt_now() : timespec_zero;
344+
timeout = ms ? timespec_frommillis(*ms) : timespec_max;
345+
deadline = timespec_add(now, timeout);
346+
rc = sys_poll_nt_impl(fds, nfds, deadline, sigmask ? *sigmask : _SigMask);
349347
ALLOW_SIGNALS;
350348
return rc;
351349
}

libc/calls/poll.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@
2626
* should just create a separate thread for each client. poll() isn't a
2727
* scalable i/o solution on any platform.
2828
*
29-
* On Windows it's only possible to poll 64 file descriptors at a time.
30-
* This is a limitation imposed by WSAPoll(). Cosmopolitan Libc's poll()
31-
* polyfill can go higher in some cases. For example, you can actually
32-
* poll 64 sockets and 63 non-sockets at the same time. Furthermore,
33-
* elements whose fd field is set to a negative number are ignored and
34-
* will not count against this limit.
35-
*
3629
* One of the use cases for poll() is to quickly check if a number of
3730
* file descriptors are valid. The canonical way to do this is to set
3831
* events to 0 which prevents blocking and causes only the invalid,
@@ -46,6 +39,12 @@
4639
* When XNU and BSD OSes report POLLHUP, they will always set POLLIN too
4740
* when POLLIN is requested, even in cases when there isn't unread data.
4841
*
42+
* Your poll() function will check the status of all file descriptors
43+
* before returning. This function won't block unless none of the fds
44+
* had had any reportable status.
45+
*
46+
* The impact shutdown() will have on poll() is a dice roll across OSes.
47+
*
4948
* @param fds[𝑖].fd should be a socket, input pipe, or conosle input
5049
* and if it's a negative number then the entry is ignored, plus
5150
* revents will be set to zero

libc/calls/ppoll.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,14 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
156156
}
157157
} else {
158158
uint32_t ms;
159-
if (!timeout ||
160-
ckd_add(&ms, timeout->tv_sec, (timeout->tv_nsec + 999999) / 1000000)) {
161-
ms = -1u;
159+
uint32_t *msp;
160+
if (timeout &&
161+
!ckd_add(&ms, timeout->tv_sec, (timeout->tv_nsec + 999999) / 1000000)) {
162+
msp = &ms;
163+
} else {
164+
msp = 0;
162165
}
163-
fdcount = sys_poll_nt(fds, nfds, &ms, sigmask);
166+
fdcount = sys_poll_nt(fds, nfds, msp, sigmask);
164167
}
165168

166169
if (IsOpenbsd() && fdcount != -1) {

libc/calls/read-nt.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -384,12 +384,14 @@ textwindows static int ProcessMouseEvent(const struct NtInputRecord *r,
384384
kNtLeftAltPressed | kNtRightAltPressed))) {
385385
// we disable mouse highlighting when the tty is put in raw mode
386386
// to mouse wheel events with widely understood vt100 arrow keys
387-
*p++ = 033;
388-
*p++ = !__keystroke.ohno_decckm ? '[' : 'O';
389-
if (isup) {
390-
*p++ = 'A';
391-
} else {
392-
*p++ = 'B';
387+
for (int i = 0; i < 3; ++i) {
388+
*p++ = 033;
389+
*p++ = !__keystroke.ohno_decckm ? '[' : 'O';
390+
if (isup) {
391+
*p++ = 'A';
392+
} else {
393+
*p++ = 'B';
394+
}
393395
}
394396
}
395397
} else if ((bs || currentbs) && (__ttyconf.magic & kTtyXtMouse)) {

0 commit comments

Comments
 (0)