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) {

View file

@ -23,7 +23,7 @@
/* Apple's ulock (part by Cosmo futexes) is an internal API, but:
1. Unlike GCD it's cancellable, i.e. can be EINTR'd by signals
2. We currently always use ulock anyway for joining threads */
#define PREFER_GCD_OVER_ULOCK 0
#define PREFER_GCD_OVER_ULOCK 1
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\

View file

@ -20,6 +20,7 @@
#include "libc/errno.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/runtime/clktck.h"
#include "libc/runtime/syslib.internal.h"
#include "libc/str/str.h"
#include "libc/thread/posixthread.internal.h"
@ -116,8 +117,7 @@ errno_t nsync_mu_semaphore_p_with_deadline_gcd (nsync_semaphore *s,
(pt->pt_flags & PT_NOCANCEL)) {
result = nsync_dispatch_semaphore_wait (s, abs_deadline);
} else {
struct timespec now, until, slice;
slice = timespec_frommillis (__SIG_LOCK_INTERVAL_MS);
struct timespec now, until, slice = {0, 1000000000 / CLK_TCK};
for (;;) {
if (_weaken (pthread_testcancel_np) () == ECANCELED) {
result = ECANCELED;

View file

@ -17,12 +17,17 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/str/str.h"
#include "libc/errno.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/runtime/runtime.h"
#include "third_party/nsync/common.internal.h"
// clang-format off
/* Aborts after printing the nul-terminated string s[]. */
void nsync_panic_ (const char *s) {
tinyprint (2, "nsync panic: ", s, NULL);
notpossible;
tinyprint(2, "error: nsync panic: ", s, "\n",
"cosmoaddr2line ", program_invocation_name, " ",
DescribeBacktrace (__builtin_frame_address (0)), "\n",
NULL);
_Exit (44);
}