Improve threading and i/o routines

- On Windows connect() can now be interrupted by a signal; connect() w/
  O_NONBLOCK will now raise EINPROGRESS; and connect() with SO_SNDTIMEO
  will raise ETIMEDOUT after the interval has elapsed.

- We now get the AcceptEx(), ConnectEx(), and TransmitFile() functions
  from the WIN32 API the officially blessed way, using WSAIoctl().

- Do nothing on Windows when fsync() is called on a directory handle.
  This was raising EACCES earlier becaues GENERIC_WRITE is required on
  the handle. It's possible to FlushFileBuffers() a directory handle if
  it's opened with write access but MSDN doesn't document what it does.
  If you have any idea, please let us know!

- Prefer manual reset event objects for read() and write() on Windows.

- Do some code cleanup on our dlmalloc customizations.

- Fix errno type error in Windows blocking routines.

- Make the futex polyfill simpler and faster.
This commit is contained in:
Justine Tunney 2023-10-12 18:53:17 -07:00
parent f7343319cc
commit 49b0eaa69f
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
43 changed files with 528 additions and 425 deletions

View file

@ -38,6 +38,7 @@
#include "libc/nexgen32e/vendor.internal.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/runtime/clktck.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/timer.h"
#include "libc/sysv/errfuns.h"
@ -129,46 +130,19 @@ static void nsync_futex_init_ (void) {
}
static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *abstime) {
int rc;
int64_t nanos, maxnanos;
struct timespec now, wait, remain, deadline;
if (!abstime) {
deadline = timespec_max;
} else {
deadline = *abstime;
}
nanos = 100;
maxnanos = __SIG_LOCK_INTERVAL_MS * 1000L * 1000;
for (;;) {
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
return 0;
}
now = timespec_real ();
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
return 0;
if (_weaken (pthread_testcancel_np) &&
_weaken (pthread_testcancel_np) ()) {
return -ETIMEDOUT;
}
if (timespec_cmp (now, deadline) >= 0) {
break;
}
wait = timespec_fromnanos (nanos);
remain = timespec_sub (deadline, now);
if (timespec_cmp(wait, remain) > 0) {
wait = remain;
}
if ((rc = clock_nanosleep (CLOCK_REALTIME, 0, &wait, 0))) {
return -rc;
}
if (nanos < maxnanos) {
nanos <<= 1;
if (nanos > maxnanos) {
nanos = maxnanos;
}
if (abstime && timespec_cmp (timespec_real (), *abstime) >= 0) {
return -ETIMEDOUT;
}
pthread_yield ();
}
return -ETIMEDOUT;
}
static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
@ -189,7 +163,7 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
return etimedout();
}
remain = timespec_sub (deadline, now);
interval = timespec_frommillis (__SIG_LOCK_INTERVAL_MS);
interval = timespec_frommillis (5000);
wait = timespec_cmp (remain, interval) > 0 ? interval : remain;
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
return 0;
@ -274,7 +248,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time
us = -1u;
}
rc = ulock_wait (op, w, expect, us);
if (rc > 0) rc = 0; // TODO(jart): What does it mean?
if (rc > 0) rc = 0; // don't care about #waiters
} else if (IsFreebsd ()) {
rc = sys_umtx_timedwait_uint (w, expect, pshare, timeout);
} else {
@ -356,6 +330,7 @@ int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
op |= ULF_WAKE_ALL;
}
rc = ulock_wake (op, w, 0);
ASSERT (!rc || rc == -ENOENT);
if (!rc) {
rc = 1;
} else if (rc == -ENOENT) {