Make forking off threads reliable on Windows

This change makes posix_spawn_test no longer flaky on Windows, by (1)
fixing a race condition in wait(), and (2) removing a misguided vfork
implementation which was letting Windows bypass pthread_atfork().
This commit is contained in:
Justine Tunney 2023-07-30 08:55:01 -07:00
parent 2ebc5781a1
commit 58352df0a4
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
30 changed files with 230 additions and 187 deletions

View file

@ -19,6 +19,7 @@
#include "ape/sections.internal.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigaction.h"
@ -75,12 +76,14 @@ textwindows int __sig_is_applicable(struct Signal *s) {
* Dequeues signal that isn't masked.
* @return signal or null if empty or none unmasked
*/
static textwindows struct Signal *__sig_remove(void) {
static textwindows struct Signal *__sig_remove(int sigops) {
struct Signal *prev, *res;
if (__sig.queue) {
__sig_lock();
for (prev = 0, res = __sig.queue; res; prev = res, res = res->next) {
if (__sig_is_applicable(res) && !__sig_is_masked(res->sig)) {
if (__sig_is_applicable(res) && //
!__sig_is_masked(res->sig) && //
!((sigops & kSigOpNochld) && res->sig == SIGCHLD)) {
if (res == __sig.queue) {
__sig.queue = res->next;
} else if (prev) {
@ -102,8 +105,7 @@ static textwindows struct Signal *__sig_remove(void) {
* @note called from main thread
* @return true if EINTR should be returned by caller
*/
static bool __sig_deliver(bool restartable, int sig, int si_code,
ucontext_t *ctx) {
static bool __sig_deliver(int sigops, int sig, int si_code, ucontext_t *ctx) {
unsigned rva, flags;
siginfo_t info, *infop;
STRACE("delivering %G", sig);
@ -145,7 +147,7 @@ static bool __sig_deliver(bool restartable, int sig, int si_code,
}
}
if (!restartable) {
if (!(sigops & kSigOpRestartable)) {
return true; // always send EINTR for wait4(), poll(), etc.
} else if (flags & SA_RESTART) {
STRACE("restarting syscall on %G", sig);
@ -168,11 +170,9 @@ static textwindows bool __sig_is_fatal(int sig) {
/**
* Handles signal.
*
* @param restartable can be used to suppress true return if SA_RESTART
* @return true if signal was delivered
*/
bool __sig_handle(bool restartable, int sig, int si_code, ucontext_t *ctx) {
bool __sig_handle(int sigops, int sig, int si_code, ucontext_t *ctx) {
bool delivered;
switch (__sighandrvas[sig]) {
case (intptr_t)SIG_DFL:
@ -186,7 +186,7 @@ bool __sig_handle(bool restartable, int sig, int si_code, ucontext_t *ctx) {
delivered = false;
break;
default:
delivered = __sig_deliver(restartable, sig, si_code, ctx);
delivered = __sig_deliver(sigops, sig, si_code, ctx);
break;
}
return delivered;
@ -226,7 +226,9 @@ textwindows int __sig_add(int tid, int sig, int si_code) {
int rc;
struct Signal *mem;
if (1 <= sig && sig <= 64) {
if (__sighandrvas[sig] == (unsigned)(intptr_t)SIG_IGN) {
if (__sighandrvas[sig] == (unsigned)(uintptr_t)SIG_IGN ||
(__sighandrvas[sig] == (unsigned)(uintptr_t)SIG_DFL &&
!__sig_is_fatal(sig))) {
STRACE("ignoring %G", sig);
rc = 0;
} else {
@ -253,19 +255,17 @@ textwindows int __sig_add(int tid, int sig, int si_code) {
/**
* Checks for unblocked signals and delivers them on New Technology.
*
* @param restartable is for functions like read() but not poll()
* @return true if EINTR should be returned by caller
* @note called from main thread
* @threadsafe
*/
textwindows bool __sig_check(bool restartable) {
textwindows bool __sig_check(int sigops) {
unsigned rva;
bool delivered;
struct Signal *sig;
delivered = false;
while ((sig = __sig_remove())) {
delivered |= __sig_handle(restartable, sig->sig, sig->si_code, 0);
while ((sig = __sig_remove(sigops))) {
delivered |= __sig_handle(sigops, sig->sig, sig->si_code, 0);
__sig_free(sig);
}
return delivered;

View file

@ -34,7 +34,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
for (;;) {
if (sys_clock_gettime_nt(clock, &now)) return -1;
if (timespec_cmp(now, abs) >= 0) return 0;
if (_check_interrupts(false, g_fds.p)) return -1;
if (_check_interrupts(0, g_fds.p)) return -1;
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
timespec_tomillis(timespec_sub(abs, now))),
false);
@ -45,7 +45,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
for (;;) {
sys_clock_gettime_nt(clock, &now);
if (timespec_cmp(now, abs) >= 0) return 0;
if (_check_interrupts(false, g_fds.p)) {
if (_check_interrupts(0, g_fds.p)) {
if (rem) *rem = timespec_sub(abs, now);
return -1;
}

View file

@ -23,6 +23,6 @@
textwindows int sys_fdatasync_nt(int fd) {
// TODO(jart): what should we do with worker pipes?
if (!__isfdkind(fd, kFdFile)) return ebadf();
if (_check_interrupts(false, 0)) return -1;
if (_check_interrupts(0, 0)) return -1;
return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : -1;
}

View file

@ -7,6 +7,9 @@
#define kSigactionMinRva 8 /* >SIG_{ERR,DFL,IGN,...} */
#define kSigOpRestartable 1
#define kSigOpNochld 2
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -35,7 +38,7 @@ forceinline bool __isfdkind(int fd, int kind) {
}
int sys_close_nt(struct Fd *, int);
int _check_interrupts(bool, struct Fd *);
int _check_interrupts(int, struct Fd *);
int sys_openat_metal(int, const char *, int, unsigned);
COSMOPOLITAN_C_END_

View file

@ -33,7 +33,7 @@
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
textwindows int _check_interrupts(bool restartable, struct Fd *fd) {
textwindows int _check_interrupts(int sigops, struct Fd *fd) {
int e, rc;
e = errno;
if (_weaken(pthread_testcancel_np) &&
@ -45,14 +45,14 @@ textwindows int _check_interrupts(bool restartable, struct Fd *fd) {
_weaken(_check_sigalrm)();
}
if (!__tls_enabled || !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
if (_weaken(_check_sigchld)) {
if (!(sigops & kSigOpNochld) && _weaken(_check_sigchld)) {
_weaken(_check_sigchld)();
}
if (fd && _weaken(_check_sigwinch)) {
_weaken(_check_sigwinch)(fd);
}
}
if (_weaken(__sig_check) && _weaken(__sig_check)(restartable)) {
if (_weaken(__sig_check) && _weaken(__sig_check)(sigops)) {
return eintr();
}
errno = e;

View file

@ -88,10 +88,9 @@ textwindows int sys_kill_nt(int pid, int sig) {
// since windows can't execve we need to kill the grandchildren
// TODO(jart): should we just kill the whole tree too? there's
// no obvious way to tell if it's the execve shell
int64_t hSnap, hProc, hChildProc;
struct NtProcessEntry32 pe = {.dwSize = sizeof(struct NtProcessEntry32)};
ntpid = GetProcessId(g_fds.p[pid].handle);
hSnap = CreateToolhelp32Snapshot(kNtTh32csSnapprocess, 0);
int64_t hSnap = CreateToolhelp32Snapshot(kNtTh32csSnapprocess, 0);
if (Process32First(hSnap, &pe)) {
do {
if (pe.th32ParentProcessID == ntpid) {
@ -102,6 +101,7 @@ textwindows int sys_kill_nt(int pid, int sig) {
}
} while (Process32Next(hSnap, &pe));
}
CloseHandle(hSnap);
ok = TerminateProcess(g_fds.p[pid].handle, 128 + sig);
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
return 0;

View file

@ -28,7 +28,7 @@
textwindows int sys_pause_nt(void) {
for (;;) {
if (_check_interrupts(false, g_fds.p)) {
if (_check_interrupts(0, g_fds.p)) {
return -1;
}

View file

@ -65,7 +65,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
if (sigmask) {
__sig_mask(SIG_SETMASK, sigmask, &oldmask);
}
if ((rc = _check_interrupts(false, g_fds.p))) {
if ((rc = _check_interrupts(0, g_fds.p))) {
goto ReturnPath;
}
@ -190,7 +190,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
}
// otherwise loop limitlessly for timeout to elapse while
// checking for signal delivery interrupts, along the way
if ((rc = _check_interrupts(false, g_fds.p))) {
if ((rc = _check_interrupts(0, g_fds.p))) {
goto ReturnPath;
}
}

View file

@ -53,7 +53,7 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
if (fd->flags & O_NONBLOCK) {
return eagain();
}
if (_check_interrupts(true, g_fds.p)) {
if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
POLLTRACE("sys_read_nt interrupted");
return -1;
}
@ -105,7 +105,7 @@ textwindows ssize_t sys_read_nt(struct Fd *fd, const struct iovec *iov,
uint32_t size;
size_t i, total;
if (opt_offset < -1) return einval();
if (_check_interrupts(true, fd)) return -1;
if (_check_interrupts(kSigOpRestartable, fd)) return -1;
while (iovlen && !iov[0].iov_len) iov++, iovlen--;
if (iovlen) {
for (total = i = 0; i < iovlen; ++i) {

View file

@ -28,8 +28,8 @@ struct Signals {
extern struct Signals __sig;
extern atomic_long __sig_count;
bool __sig_check(bool);
bool __sig_handle(bool, int, int, ucontext_t *);
bool __sig_check(int);
bool __sig_handle(int, int, int, ucontext_t *);
int __sig_add(int, int, int);
int __sig_mask(int, const sigset_t *, sigset_t *);
int __sig_raise(int, int);

View file

@ -24,7 +24,6 @@
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
@ -34,40 +33,37 @@
#ifdef __x86_64__
static textwindows bool CheckForExitedChildProcess(void) {
int pids[64];
uint32_t i, n;
int64_t handles[64];
if (!(n = __sample_pids(pids, handles, true))) return false;
i = WaitForMultipleObjects(n, handles, false, 0);
if (i == kNtWaitFailed) return false;
if (i == kNtWaitTimeout) return false;
if ((__sighandrvas[SIGCHLD] >= kSigactionMinRva) &&
(__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) {
CloseHandle(handles[i]);
__releasefd(pids[i]);
} else {
g_fds.p[pids[i]].zombie = true;
}
return true;
}
/**
* Checks to see if SIGCHLD should be raised on Windows.
* @return true if a signal was raised
* @note yoinked by fork-nt.c
*/
void _check_sigchld(void) {
siginfo_t si;
int pids[64];
uint32_t i, n;
int64_t handles[64];
textwindows void _check_sigchld(void) {
bool should_signal;
__fds_lock();
n = __sample_pids(pids, handles, true);
should_signal = CheckForExitedChildProcess();
__fds_unlock();
if (!n) return;
i = WaitForMultipleObjects(n, handles, false, 0);
if (i == kNtWaitTimeout) return;
if (i == kNtWaitFailed) {
NTTRACE("%s failed %u", "WaitForMultipleObjects", GetLastError());
return;
if (should_signal) {
__sig_add(0, SIGCHLD, CLD_EXITED);
}
if (__sighandrvas[SIGCHLD] == (intptr_t)SIG_IGN ||
__sighandrvas[SIGCHLD] == (intptr_t)SIG_DFL) {
NTTRACE("new zombie fd=%d handle=%ld", pids[i], handles[i]);
return;
}
if (__sighandflags[SIGCHLD] & SA_NOCLDWAIT) {
NTTRACE("SIGCHILD SA_NOCLDWAIT fd=%d handle=%ld", pids[i], handles[i]);
CloseHandle(handles[i]);
__releasefd(pids[i]);
}
__fds_lock();
g_fds.p[pids[i]].zombie = true;
__fds_unlock();
__sig_add(0, SIGCHLD, CLD_EXITED);
}
#endif /* __x86_64__ */

View file

@ -61,7 +61,7 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
} else if (IsMetal() || IsWindows()) {
rc = __sig_mask(how, opt_set, &old);
if (_weaken(__sig_check)) {
_weaken(__sig_check)(true);
_weaken(__sig_check)(kSigOpRestartable);
}
} else {
rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0);

View file

@ -77,7 +77,7 @@ int sigsuspend(const sigset_t *ignore) {
long totoms = 0;
#endif
do {
if ((rc = _check_interrupts(false, g_fds.p))) {
if ((rc = _check_interrupts(0, g_fds.p))) {
break;
}
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {

View file

@ -32,7 +32,7 @@
static dontinline textwindows int sys_tcdrain_nt(int fd) {
if (!__isfdopen(fd)) return ebadf();
if (_check_interrupts(false, g_fds.p)) return -1;
if (_check_interrupts(0, g_fds.p)) return -1;
if (!FlushFileBuffers(g_fds.p[fd].handle)) return __winerr();
return 0;
}

View file

@ -24,16 +24,19 @@
#include "libc/calls/struct/rusage.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/nt/accounting.h"
#include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/processaccess.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/enum/th32cs.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/filetime.h"
#include "libc/nt/struct/processentry32.h"
#include "libc/nt/struct/processmemorycounters.h"
#include "libc/nt/synchronization.h"
#include "libc/runtime/runtime.h"
@ -46,105 +49,101 @@
#ifdef __x86_64__
static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus,
static textwindows void AddProcessStats(int64_t h, struct rusage *ru) {
struct NtProcessMemoryCountersEx memcount = {
.cb = sizeof(struct NtProcessMemoryCountersEx)};
if (GetProcessMemoryInfo(h, &memcount, sizeof(memcount))) {
ru->ru_maxrss = MAX(ru->ru_maxrss, memcount.PeakWorkingSetSize / 1024);
ru->ru_majflt += memcount.PageFaultCount;
} else {
STRACE("%s failed %u", "GetProcessMemoryInfo", GetLastError());
}
struct NtFileTime createfiletime, exitfiletime;
struct NtFileTime kernelfiletime, userfiletime;
if (GetProcessTimes(h, &createfiletime, &exitfiletime, &kernelfiletime,
&userfiletime)) {
ru->ru_utime = timeval_add(
ru->ru_utime, WindowsDurationToTimeVal(ReadFileTime(userfiletime)));
ru->ru_stime = timeval_add(
ru->ru_stime, WindowsDurationToTimeVal(ReadFileTime(kernelfiletime)));
} else {
STRACE("%s failed %u", "GetProcessTimes", GetLastError());
}
}
static textwindows int sys_wait4_nt_impl(int *pid, int *opt_out_wstatus,
int options,
struct rusage *opt_out_rusage) {
int64_t handle;
int rc, pids[64];
int64_t handles[64];
uint32_t dwExitCode;
bool shouldinterrupt;
uint32_t i, j, base, count, timeout;
struct NtProcessMemoryCountersEx memcount;
struct NtFileTime createfiletime, exitfiletime, kernelfiletime, userfiletime;
if (_check_interrupts(true, g_fds.p)) return -1;
__fds_lock();
if (pid != -1 && pid != 0) {
if (pid < 0) {
/* XXX: this is sloppy */
pid = -pid;
uint32_t i, j, count;
if (*pid != -1 && *pid != 0) {
if (*pid < 0) {
// XXX: this is sloppy
*pid = -*pid;
}
if (!__isfdkind(pid, kFdProcess)) {
/* XXX: this is sloppy (see fork-nt.c) */
if (!__isfdopen(pid) &&
if (!__isfdkind(*pid, kFdProcess)) {
// XXX: this is sloppy (see fork-nt.c)
if (!__isfdopen(*pid) &&
(handle = OpenProcess(kNtSynchronize | kNtProcessQueryInformation,
true, pid))) {
if ((pid = __reservefd_unlocked(-1)) != -1) {
g_fds.p[pid].kind = kFdProcess;
g_fds.p[pid].handle = handle;
g_fds.p[pid].flags = O_CLOEXEC;
true, *pid))) {
if ((*pid = __reservefd_unlocked(-1)) != -1) {
g_fds.p[*pid].kind = kFdProcess;
g_fds.p[*pid].handle = handle;
g_fds.p[*pid].flags = O_CLOEXEC;
} else {
__fds_unlock();
CloseHandle(handle);
return echild();
}
} else {
__fds_unlock();
return echild();
}
}
handles[0] = g_fds.p[pid].handle;
pids[0] = pid;
handles[0] = g_fds.p[*pid].handle;
pids[0] = *pid;
count = 1;
} else {
count = __sample_pids(pids, handles, false);
if (!count) {
__fds_unlock();
return echild();
}
}
__fds_unlock();
for (;;) {
if (_check_interrupts(true, 0)) return -1;
dwExitCode = kNtStillActive;
if (options & WNOHANG) {
i = WaitForMultipleObjects(count, handles, false, 0);
if (i == kNtWaitTimeout) {
return 0;
}
} else {
i = WaitForMultipleObjects(count, handles, false,
__SIG_POLLING_INTERVAL_MS);
if (i == kNtWaitTimeout) {
continue;
}
dwExitCode = kNtStillActive;
if (options & WNOHANG) {
i = WaitForMultipleObjects(count, handles, false, 0);
if (i == kNtWaitTimeout) {
return 0;
}
if (i == kNtWaitFailed) {
STRACE("%s failed %u", "WaitForMultipleObjects", GetLastError());
return __winerr();
} else {
i = WaitForMultipleObjects(count, handles, false,
__SIG_POLLING_INTERVAL_MS);
if (i == kNtWaitTimeout) {
return -2;
}
if (!GetExitCodeProcess(handles[i], &dwExitCode)) {
STRACE("%s failed %u", "GetExitCodeProcess", GetLastError());
return __winerr();
}
if (dwExitCode == kNtStillActive) continue;
if (opt_out_wstatus) { /* @see WEXITSTATUS() */
*opt_out_wstatus = (dwExitCode & 0xff) << 8;
}
if (opt_out_rusage) {
bzero(opt_out_rusage, sizeof(*opt_out_rusage));
bzero(&memcount, sizeof(memcount));
memcount.cb = sizeof(struct NtProcessMemoryCountersEx);
if (GetProcessMemoryInfo(handles[i], &memcount, sizeof(memcount))) {
opt_out_rusage->ru_maxrss = memcount.PeakWorkingSetSize / 1024;
opt_out_rusage->ru_majflt = memcount.PageFaultCount;
} else {
STRACE("%s failed %u", "GetProcessMemoryInfo", GetLastError());
}
if (GetProcessTimes(handles[i], &createfiletime, &exitfiletime,
&kernelfiletime, &userfiletime)) {
opt_out_rusage->ru_utime =
WindowsDurationToTimeVal(ReadFileTime(userfiletime));
opt_out_rusage->ru_stime =
WindowsDurationToTimeVal(ReadFileTime(kernelfiletime));
} else {
STRACE("%s failed %u", "GetProcessTimes", GetLastError());
}
}
CloseHandle(handles[i]);
__releasefd(pids[i]);
return pids[i];
}
if (i == kNtWaitFailed) {
STRACE("%s failed %u", "WaitForMultipleObjects", GetLastError());
return __winerr();
}
if (!GetExitCodeProcess(handles[i], &dwExitCode)) {
STRACE("%s failed %u", "GetExitCodeProcess", GetLastError());
return __winerr();
}
if (dwExitCode == kNtStillActive) {
return -2;
}
if (opt_out_wstatus) { // @see WEXITSTATUS()
*opt_out_wstatus = (dwExitCode & 0xff) << 8;
}
if (opt_out_rusage) {
bzero(opt_out_rusage, sizeof(*opt_out_rusage));
AddProcessStats(handles[i], opt_out_rusage);
}
CloseHandle(handles[i]);
__releasefd(pids[i]);
return pids[i];
}
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
@ -153,7 +152,13 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
sigset_t oldmask, mask = {0};
sigaddset(&mask, SIGCHLD);
__sig_mask(SIG_BLOCK, &mask, &oldmask);
rc = sys_wait4_nt_impl(pid, opt_out_wstatus, options, opt_out_rusage);
do {
rc = _check_interrupts(kSigOpRestartable | kSigOpNochld, 0);
if (rc == -1) break;
__fds_lock();
rc = sys_wait4_nt_impl(&pid, opt_out_wstatus, options, opt_out_rusage);
__fds_unlock();
} while (rc == -2);
__sig_mask(SIG_SETMASK, &oldmask, 0);
return rc;
}

View file

@ -55,7 +55,9 @@ int wait4(int pid, int *opt_out_wstatus, int options,
} else {
rc = sys_wait4_nt(pid, &ws, options, opt_out_rusage);
}
if (rc != -1 && opt_out_wstatus) *opt_out_wstatus = ws;
if (rc != -1 && opt_out_wstatus) {
*opt_out_wstatus = ws;
}
END_CANCELLATION_POINT;
STRACE("wait4(%d, [%#x], %d, %p) → %d% m", pid, ws, options, opt_out_rusage,

View file

@ -56,8 +56,18 @@
vfork:
.ftrace2
#ifdef __SANITIZE_ADDRESS__
jmp fork
#endif
#ifdef __x86_64__
#if SupportsWindows()
// these platforms disagree with vfork
testb $_HOSTXNU|_HOSTOPENBSD|_HOSTWINDOWS,__hostos(%rip)
jnz fork
#endif
#if !IsTiny()
push %rbp
mov %rsp,%rbp
@ -69,21 +79,6 @@ vfork:
pop %rbp
#endif
mov %fs:0,%r9 // get thread information block
#if SupportsWindows()
testb IsWindows()
jnz 6f // and we're lucky to have that
#endif
#ifdef __SANITIZE_ADDRESS__
jmp 5f // TODO: asan and vfork don't mix?
#endif
#if SupportsXnu()
testb IsXnu()
jnz 5f
#endif
#if SupportsOpenbsd()
testb IsOpenbsd()
jnz 5f // fake vfork plus msyscall issues
#endif
mov 0x3c(%r9),%r8d // avoid question of @vforksafe errno
pop %rsi // saves return address in a register
mov __NR_vfork(%rip),%eax
@ -106,29 +101,6 @@ vfork:
ret
.Lpar: andb $~TIB_FLAG_VFORKED,0x40(%r9)
ret
#if SupportsXnu() || SupportsOpenbsd() || defined(__SANITIZE_ADDRESS__)
5: push %rbp
mov %rsp,%rbp
push %r9
push %r9
call sys_fork
pop %r9
pop %r9
pop %rbp
jmp 1b
#endif
#if SupportsWindows()
6: push %rbp
mov %rsp,%rbp
push %r9
push %r9
xor %edi,%edi // dwCreationFlags
call sys_fork_nt
pop %r9
pop %r9
pop %rbp
jmp 1b
#endif
#elif defined(__aarch64__)

View file

@ -67,7 +67,7 @@ textwindows int sys_accept_nt(struct Fd *fd, struct sockaddr_storage *addr,
if (!AcceptEx(fd->handle, handle, &buffer, 0, sizeof(buffer.local),
sizeof(buffer.remote), &bytes_received, &overlapped)) {
sockfd = (struct SockFd *)fd->extra;
if (__wsablock(fd, &overlapped, &completion_flags, true,
if (__wsablock(fd, &overlapped, &completion_flags, kSigOpRestartable,
sockfd->rcvtimeo) == -1) {
WSACloseEvent(overlapped.hEvent);
__sys_closesocket_nt(handle);

View file

@ -47,7 +47,8 @@ textwindows ssize_t sys_recv_nt(struct Fd *fd, const struct iovec *iov,
} else {
errno = err;
sockfd = (struct SockFd *)fd->extra;
rc = __wsablock(fd, &overlapped, &flags, true, sockfd->rcvtimeo);
rc = __wsablock(fd, &overlapped, &flags, kSigOpRestartable,
sockfd->rcvtimeo);
}
unassert(WSACloseEvent(overlapped.hEvent));
return rc;

View file

@ -46,7 +46,8 @@ textwindows ssize_t sys_recvfrom_nt(struct Fd *fd, const struct iovec *iov,
} else {
errno = err;
sockfd = (struct SockFd *)fd->extra;
rc = __wsablock(fd, &overlapped, &flags, true, sockfd->rcvtimeo);
rc = __wsablock(fd, &overlapped, &flags, kSigOpRestartable,
sockfd->rcvtimeo);
}
WSACloseEvent(overlapped.hEvent);
return rc;

View file

@ -41,7 +41,8 @@ textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen,
}
} else {
sockfd = (struct SockFd *)g_fds.p[fd].extra;
rc = __wsablock(g_fds.p + fd, &overlapped, &flags, true, sockfd->sndtimeo);
rc = __wsablock(g_fds.p + fd, &overlapped, &flags, kSigOpRestartable,
sockfd->sndtimeo);
}
WSACloseEvent(overlapped.hEvent);
return rc;

View file

@ -57,7 +57,7 @@ static textwindows int SendfileBlock(int64_t handle,
NTTRACE("WSAWaitForMultipleEvents failed %lm");
return __winsockerr();
} else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) {
if (_check_interrupts(true, g_fds.p)) return -1;
if (_check_interrupts(kSigOpRestartable, g_fds.p)) return -1;
#if _NTTRACE
POLLTRACE("WSAWaitForMultipleEvents...");
#endif

View file

@ -42,7 +42,8 @@ textwindows ssize_t sys_sendto_nt(int fd, const struct iovec *iov,
}
} else {
sockfd = (struct SockFd *)g_fds.p[fd].extra;
rc = __wsablock(g_fds.p + fd, &overlapped, &flags, true, sockfd->sndtimeo);
rc = __wsablock(g_fds.p + fd, &overlapped, &flags, kSigOpRestartable,
sockfd->sndtimeo);
}
WSACloseEvent(overlapped.hEvent);
return rc;

View file

@ -20,7 +20,7 @@ int sys_shutdown_nt(struct Fd *, int);
ssize_t sys_recv_nt(struct Fd *, const struct iovec *, size_t, uint32_t);
ssize_t sys_recvfrom_nt(struct Fd *, const struct iovec *, size_t, uint32_t,
void *, uint32_t *);
int __wsablock(struct Fd *, struct NtOverlapped *, uint32_t *, bool, uint32_t);
int __wsablock(struct Fd *, struct NtOverlapped *, uint32_t *, int, uint32_t);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -37,8 +37,7 @@
#include "libc/sysv/errfuns.h"
textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped,
uint32_t *flags, bool restartable,
uint32_t timeout) {
uint32_t *flags, int sigops, uint32_t timeout) {
int e, rc;
uint32_t i, got;
if (WSAGetLastError() != kNtErrorIoPending) {
@ -51,7 +50,7 @@ textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped,
WSAGetLastError() == kNtErrorNotFound);
errno = e;
} else {
if (_check_interrupts(restartable, g_fds.p)) {
if (_check_interrupts(sigops, g_fds.p)) {
return -1;
}
}
@ -62,7 +61,7 @@ textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped,
NTTRACE("WSAWaitForMultipleEvents failed %lm");
return __winsockerr();
} else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) {
if (_check_interrupts(restartable, g_fds.p)) {
if (_check_interrupts(sigops, g_fds.p)) {
return -1;
}
if (timeout) {

View file

@ -176,7 +176,9 @@ static ssize_t GetDevUrandom(char *p, size_t n) {
ssize_t __getrandom(void *p, size_t n, unsigned f) {
ssize_t rc;
if (IsWindows()) {
if (_check_interrupts(true, 0)) return -1;
if (_check_interrupts(kSigOpRestartable, 0)) {
return -1;
}
rc = RtlGenRandom(p, n) ? n : __winerr();
} else if (have_getrandom) {
if (IsXnu() || IsOpenbsd()) {