mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 13:52:28 +00:00
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:
parent
2ebc5781a1
commit
58352df0a4
30 changed files with 230 additions and 187 deletions
|
@ -19,6 +19,7 @@
|
||||||
#include "ape/sections.internal.h"
|
#include "ape/sections.internal.h"
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/sig.internal.h"
|
#include "libc/calls/sig.internal.h"
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
#include "libc/calls/struct/sigaction.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.
|
* Dequeues signal that isn't masked.
|
||||||
* @return signal or null if empty or none unmasked
|
* @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;
|
struct Signal *prev, *res;
|
||||||
if (__sig.queue) {
|
if (__sig.queue) {
|
||||||
__sig_lock();
|
__sig_lock();
|
||||||
for (prev = 0, res = __sig.queue; res; prev = res, res = res->next) {
|
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) {
|
if (res == __sig.queue) {
|
||||||
__sig.queue = res->next;
|
__sig.queue = res->next;
|
||||||
} else if (prev) {
|
} else if (prev) {
|
||||||
|
@ -102,8 +105,7 @@ static textwindows struct Signal *__sig_remove(void) {
|
||||||
* @note called from main thread
|
* @note called from main thread
|
||||||
* @return true if EINTR should be returned by caller
|
* @return true if EINTR should be returned by caller
|
||||||
*/
|
*/
|
||||||
static bool __sig_deliver(bool restartable, int sig, int si_code,
|
static bool __sig_deliver(int sigops, int sig, int si_code, ucontext_t *ctx) {
|
||||||
ucontext_t *ctx) {
|
|
||||||
unsigned rva, flags;
|
unsigned rva, flags;
|
||||||
siginfo_t info, *infop;
|
siginfo_t info, *infop;
|
||||||
STRACE("delivering %G", sig);
|
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.
|
return true; // always send EINTR for wait4(), poll(), etc.
|
||||||
} else if (flags & SA_RESTART) {
|
} else if (flags & SA_RESTART) {
|
||||||
STRACE("restarting syscall on %G", sig);
|
STRACE("restarting syscall on %G", sig);
|
||||||
|
@ -168,11 +170,9 @@ static textwindows bool __sig_is_fatal(int sig) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles signal.
|
* Handles signal.
|
||||||
*
|
|
||||||
* @param restartable can be used to suppress true return if SA_RESTART
|
|
||||||
* @return true if signal was delivered
|
* @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;
|
bool delivered;
|
||||||
switch (__sighandrvas[sig]) {
|
switch (__sighandrvas[sig]) {
|
||||||
case (intptr_t)SIG_DFL:
|
case (intptr_t)SIG_DFL:
|
||||||
|
@ -186,7 +186,7 @@ bool __sig_handle(bool restartable, int sig, int si_code, ucontext_t *ctx) {
|
||||||
delivered = false;
|
delivered = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
delivered = __sig_deliver(restartable, sig, si_code, ctx);
|
delivered = __sig_deliver(sigops, sig, si_code, ctx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return delivered;
|
return delivered;
|
||||||
|
@ -226,7 +226,9 @@ textwindows int __sig_add(int tid, int sig, int si_code) {
|
||||||
int rc;
|
int rc;
|
||||||
struct Signal *mem;
|
struct Signal *mem;
|
||||||
if (1 <= sig && sig <= 64) {
|
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);
|
STRACE("ignoring %G", sig);
|
||||||
rc = 0;
|
rc = 0;
|
||||||
} else {
|
} 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.
|
* 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
|
* @return true if EINTR should be returned by caller
|
||||||
* @note called from main thread
|
* @note called from main thread
|
||||||
* @threadsafe
|
* @threadsafe
|
||||||
*/
|
*/
|
||||||
textwindows bool __sig_check(bool restartable) {
|
textwindows bool __sig_check(int sigops) {
|
||||||
unsigned rva;
|
unsigned rva;
|
||||||
bool delivered;
|
bool delivered;
|
||||||
struct Signal *sig;
|
struct Signal *sig;
|
||||||
delivered = false;
|
delivered = false;
|
||||||
while ((sig = __sig_remove())) {
|
while ((sig = __sig_remove(sigops))) {
|
||||||
delivered |= __sig_handle(restartable, sig->sig, sig->si_code, 0);
|
delivered |= __sig_handle(sigops, sig->sig, sig->si_code, 0);
|
||||||
__sig_free(sig);
|
__sig_free(sig);
|
||||||
}
|
}
|
||||||
return delivered;
|
return delivered;
|
||||||
|
|
|
@ -34,7 +34,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (sys_clock_gettime_nt(clock, &now)) return -1;
|
if (sys_clock_gettime_nt(clock, &now)) return -1;
|
||||||
if (timespec_cmp(now, abs) >= 0) return 0;
|
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,
|
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
|
||||||
timespec_tomillis(timespec_sub(abs, now))),
|
timespec_tomillis(timespec_sub(abs, now))),
|
||||||
false);
|
false);
|
||||||
|
@ -45,7 +45,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
|
||||||
for (;;) {
|
for (;;) {
|
||||||
sys_clock_gettime_nt(clock, &now);
|
sys_clock_gettime_nt(clock, &now);
|
||||||
if (timespec_cmp(now, abs) >= 0) return 0;
|
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);
|
if (rem) *rem = timespec_sub(abs, now);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,6 @@
|
||||||
textwindows int sys_fdatasync_nt(int fd) {
|
textwindows int sys_fdatasync_nt(int fd) {
|
||||||
// TODO(jart): what should we do with worker pipes?
|
// TODO(jart): what should we do with worker pipes?
|
||||||
if (!__isfdkind(fd, kFdFile)) return ebadf();
|
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;
|
return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
|
|
||||||
#define kSigactionMinRva 8 /* >SIG_{ERR,DFL,IGN,...} */
|
#define kSigactionMinRva 8 /* >SIG_{ERR,DFL,IGN,...} */
|
||||||
|
|
||||||
|
#define kSigOpRestartable 1
|
||||||
|
#define kSigOpNochld 2
|
||||||
|
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
@ -35,7 +38,7 @@ forceinline bool __isfdkind(int fd, int kind) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int sys_close_nt(struct Fd *, int);
|
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);
|
int sys_openat_metal(int, const char *, int, unsigned);
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "libc/thread/tls.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;
|
int e, rc;
|
||||||
e = errno;
|
e = errno;
|
||||||
if (_weaken(pthread_testcancel_np) &&
|
if (_weaken(pthread_testcancel_np) &&
|
||||||
|
@ -45,14 +45,14 @@ textwindows int _check_interrupts(bool restartable, struct Fd *fd) {
|
||||||
_weaken(_check_sigalrm)();
|
_weaken(_check_sigalrm)();
|
||||||
}
|
}
|
||||||
if (!__tls_enabled || !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
|
if (!__tls_enabled || !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
|
||||||
if (_weaken(_check_sigchld)) {
|
if (!(sigops & kSigOpNochld) && _weaken(_check_sigchld)) {
|
||||||
_weaken(_check_sigchld)();
|
_weaken(_check_sigchld)();
|
||||||
}
|
}
|
||||||
if (fd && _weaken(_check_sigwinch)) {
|
if (fd && _weaken(_check_sigwinch)) {
|
||||||
_weaken(_check_sigwinch)(fd);
|
_weaken(_check_sigwinch)(fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_weaken(__sig_check) && _weaken(__sig_check)(restartable)) {
|
if (_weaken(__sig_check) && _weaken(__sig_check)(sigops)) {
|
||||||
return eintr();
|
return eintr();
|
||||||
}
|
}
|
||||||
errno = e;
|
errno = e;
|
||||||
|
|
|
@ -88,10 +88,9 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
||||||
// since windows can't execve we need to kill the grandchildren
|
// since windows can't execve we need to kill the grandchildren
|
||||||
// TODO(jart): should we just kill the whole tree too? there's
|
// TODO(jart): should we just kill the whole tree too? there's
|
||||||
// no obvious way to tell if it's the execve shell
|
// no obvious way to tell if it's the execve shell
|
||||||
int64_t hSnap, hProc, hChildProc;
|
|
||||||
struct NtProcessEntry32 pe = {.dwSize = sizeof(struct NtProcessEntry32)};
|
struct NtProcessEntry32 pe = {.dwSize = sizeof(struct NtProcessEntry32)};
|
||||||
ntpid = GetProcessId(g_fds.p[pid].handle);
|
ntpid = GetProcessId(g_fds.p[pid].handle);
|
||||||
hSnap = CreateToolhelp32Snapshot(kNtTh32csSnapprocess, 0);
|
int64_t hSnap = CreateToolhelp32Snapshot(kNtTh32csSnapprocess, 0);
|
||||||
if (Process32First(hSnap, &pe)) {
|
if (Process32First(hSnap, &pe)) {
|
||||||
do {
|
do {
|
||||||
if (pe.th32ParentProcessID == ntpid) {
|
if (pe.th32ParentProcessID == ntpid) {
|
||||||
|
@ -102,6 +101,7 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
||||||
}
|
}
|
||||||
} while (Process32Next(hSnap, &pe));
|
} while (Process32Next(hSnap, &pe));
|
||||||
}
|
}
|
||||||
|
CloseHandle(hSnap);
|
||||||
ok = TerminateProcess(g_fds.p[pid].handle, 128 + sig);
|
ok = TerminateProcess(g_fds.p[pid].handle, 128 + sig);
|
||||||
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
|
if (!ok && GetLastError() == kNtErrorAccessDenied) ok = true;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
textwindows int sys_pause_nt(void) {
|
textwindows int sys_pause_nt(void) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
if (_check_interrupts(false, g_fds.p)) {
|
if (_check_interrupts(0, g_fds.p)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
||||||
if (sigmask) {
|
if (sigmask) {
|
||||||
__sig_mask(SIG_SETMASK, sigmask, &oldmask);
|
__sig_mask(SIG_SETMASK, sigmask, &oldmask);
|
||||||
}
|
}
|
||||||
if ((rc = _check_interrupts(false, g_fds.p))) {
|
if ((rc = _check_interrupts(0, g_fds.p))) {
|
||||||
goto ReturnPath;
|
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
|
// otherwise loop limitlessly for timeout to elapse while
|
||||||
// checking for signal delivery interrupts, along the way
|
// 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;
|
goto ReturnPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
|
||||||
if (fd->flags & O_NONBLOCK) {
|
if (fd->flags & O_NONBLOCK) {
|
||||||
return eagain();
|
return eagain();
|
||||||
}
|
}
|
||||||
if (_check_interrupts(true, g_fds.p)) {
|
if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
|
||||||
POLLTRACE("sys_read_nt interrupted");
|
POLLTRACE("sys_read_nt interrupted");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ textwindows ssize_t sys_read_nt(struct Fd *fd, const struct iovec *iov,
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
size_t i, total;
|
size_t i, total;
|
||||||
if (opt_offset < -1) return einval();
|
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--;
|
while (iovlen && !iov[0].iov_len) iov++, iovlen--;
|
||||||
if (iovlen) {
|
if (iovlen) {
|
||||||
for (total = i = 0; i < iovlen; ++i) {
|
for (total = i = 0; i < iovlen; ++i) {
|
||||||
|
|
|
@ -28,8 +28,8 @@ struct Signals {
|
||||||
extern struct Signals __sig;
|
extern struct Signals __sig;
|
||||||
extern atomic_long __sig_count;
|
extern atomic_long __sig_count;
|
||||||
|
|
||||||
bool __sig_check(bool);
|
bool __sig_check(int);
|
||||||
bool __sig_handle(bool, int, int, ucontext_t *);
|
bool __sig_handle(int, int, int, ucontext_t *);
|
||||||
int __sig_add(int, int, int);
|
int __sig_add(int, int, int);
|
||||||
int __sig_mask(int, const sigset_t *, sigset_t *);
|
int __sig_mask(int, const sigset_t *, sigset_t *);
|
||||||
int __sig_raise(int, int);
|
int __sig_raise(int, int);
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include "libc/calls/struct/siginfo.h"
|
#include "libc/calls/struct/siginfo.h"
|
||||||
#include "libc/calls/syscall_support-nt.internal.h"
|
#include "libc/calls/syscall_support-nt.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
|
||||||
#include "libc/nt/enum/wait.h"
|
#include "libc/nt/enum/wait.h"
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
|
@ -34,40 +33,37 @@
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#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.
|
* Checks to see if SIGCHLD should be raised on Windows.
|
||||||
* @return true if a signal was raised
|
* @return true if a signal was raised
|
||||||
* @note yoinked by fork-nt.c
|
* @note yoinked by fork-nt.c
|
||||||
*/
|
*/
|
||||||
void _check_sigchld(void) {
|
textwindows void _check_sigchld(void) {
|
||||||
siginfo_t si;
|
bool should_signal;
|
||||||
int pids[64];
|
|
||||||
uint32_t i, n;
|
|
||||||
int64_t handles[64];
|
|
||||||
__fds_lock();
|
__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 (__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();
|
__fds_unlock();
|
||||||
|
if (should_signal) {
|
||||||
__sig_add(0, SIGCHLD, CLD_EXITED);
|
__sig_add(0, SIGCHLD, CLD_EXITED);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __x86_64__ */
|
#endif /* __x86_64__ */
|
||||||
|
|
|
@ -61,7 +61,7 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
|
||||||
} else if (IsMetal() || IsWindows()) {
|
} else if (IsMetal() || IsWindows()) {
|
||||||
rc = __sig_mask(how, opt_set, &old);
|
rc = __sig_mask(how, opt_set, &old);
|
||||||
if (_weaken(__sig_check)) {
|
if (_weaken(__sig_check)) {
|
||||||
_weaken(__sig_check)(true);
|
_weaken(__sig_check)(kSigOpRestartable);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0);
|
rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0);
|
||||||
|
|
|
@ -77,7 +77,7 @@ int sigsuspend(const sigset_t *ignore) {
|
||||||
long totoms = 0;
|
long totoms = 0;
|
||||||
#endif
|
#endif
|
||||||
do {
|
do {
|
||||||
if ((rc = _check_interrupts(false, g_fds.p))) {
|
if ((rc = _check_interrupts(0, g_fds.p))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {
|
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
static dontinline textwindows int sys_tcdrain_nt(int fd) {
|
static dontinline textwindows int sys_tcdrain_nt(int fd) {
|
||||||
if (!__isfdopen(fd)) return ebadf();
|
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();
|
if (!FlushFileBuffers(g_fds.p[fd].handle)) return __winerr();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,16 +24,19 @@
|
||||||
#include "libc/calls/struct/rusage.h"
|
#include "libc/calls/struct/rusage.h"
|
||||||
#include "libc/calls/syscall_support-nt.internal.h"
|
#include "libc/calls/syscall_support-nt.internal.h"
|
||||||
#include "libc/fmt/conv.h"
|
#include "libc/fmt/conv.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/nt/accounting.h"
|
#include "libc/nt/accounting.h"
|
||||||
#include "libc/nt/enum/accessmask.h"
|
#include "libc/nt/enum/accessmask.h"
|
||||||
#include "libc/nt/enum/processaccess.h"
|
#include "libc/nt/enum/processaccess.h"
|
||||||
#include "libc/nt/enum/status.h"
|
#include "libc/nt/enum/status.h"
|
||||||
|
#include "libc/nt/enum/th32cs.h"
|
||||||
#include "libc/nt/enum/wait.h"
|
#include "libc/nt/enum/wait.h"
|
||||||
#include "libc/nt/process.h"
|
#include "libc/nt/process.h"
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/struct/filetime.h"
|
#include "libc/nt/struct/filetime.h"
|
||||||
|
#include "libc/nt/struct/processentry32.h"
|
||||||
#include "libc/nt/struct/processmemorycounters.h"
|
#include "libc/nt/struct/processmemorycounters.h"
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
@ -46,56 +49,67 @@
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#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,
|
int options,
|
||||||
struct rusage *opt_out_rusage) {
|
struct rusage *opt_out_rusage) {
|
||||||
int64_t handle;
|
int64_t handle;
|
||||||
int rc, pids[64];
|
int rc, pids[64];
|
||||||
int64_t handles[64];
|
int64_t handles[64];
|
||||||
uint32_t dwExitCode;
|
uint32_t dwExitCode;
|
||||||
bool shouldinterrupt;
|
uint32_t i, j, count;
|
||||||
uint32_t i, j, base, count, timeout;
|
if (*pid != -1 && *pid != 0) {
|
||||||
struct NtProcessMemoryCountersEx memcount;
|
if (*pid < 0) {
|
||||||
struct NtFileTime createfiletime, exitfiletime, kernelfiletime, userfiletime;
|
// XXX: this is sloppy
|
||||||
if (_check_interrupts(true, g_fds.p)) return -1;
|
*pid = -*pid;
|
||||||
__fds_lock();
|
|
||||||
if (pid != -1 && pid != 0) {
|
|
||||||
if (pid < 0) {
|
|
||||||
/* XXX: this is sloppy */
|
|
||||||
pid = -pid;
|
|
||||||
}
|
}
|
||||||
if (!__isfdkind(pid, kFdProcess)) {
|
if (!__isfdkind(*pid, kFdProcess)) {
|
||||||
/* XXX: this is sloppy (see fork-nt.c) */
|
// XXX: this is sloppy (see fork-nt.c)
|
||||||
if (!__isfdopen(pid) &&
|
if (!__isfdopen(*pid) &&
|
||||||
(handle = OpenProcess(kNtSynchronize | kNtProcessQueryInformation,
|
(handle = OpenProcess(kNtSynchronize | kNtProcessQueryInformation,
|
||||||
true, pid))) {
|
true, *pid))) {
|
||||||
if ((pid = __reservefd_unlocked(-1)) != -1) {
|
if ((*pid = __reservefd_unlocked(-1)) != -1) {
|
||||||
g_fds.p[pid].kind = kFdProcess;
|
g_fds.p[*pid].kind = kFdProcess;
|
||||||
g_fds.p[pid].handle = handle;
|
g_fds.p[*pid].handle = handle;
|
||||||
g_fds.p[pid].flags = O_CLOEXEC;
|
g_fds.p[*pid].flags = O_CLOEXEC;
|
||||||
} else {
|
} else {
|
||||||
__fds_unlock();
|
|
||||||
CloseHandle(handle);
|
CloseHandle(handle);
|
||||||
return echild();
|
return echild();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
__fds_unlock();
|
|
||||||
return echild();
|
return echild();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handles[0] = g_fds.p[pid].handle;
|
handles[0] = g_fds.p[*pid].handle;
|
||||||
pids[0] = pid;
|
pids[0] = *pid;
|
||||||
count = 1;
|
count = 1;
|
||||||
} else {
|
} else {
|
||||||
count = __sample_pids(pids, handles, false);
|
count = __sample_pids(pids, handles, false);
|
||||||
if (!count) {
|
if (!count) {
|
||||||
__fds_unlock();
|
|
||||||
return echild();
|
return echild();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
__fds_unlock();
|
|
||||||
for (;;) {
|
|
||||||
if (_check_interrupts(true, 0)) return -1;
|
|
||||||
dwExitCode = kNtStillActive;
|
dwExitCode = kNtStillActive;
|
||||||
if (options & WNOHANG) {
|
if (options & WNOHANG) {
|
||||||
i = WaitForMultipleObjects(count, handles, false, 0);
|
i = WaitForMultipleObjects(count, handles, false, 0);
|
||||||
|
@ -106,7 +120,7 @@ static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus,
|
||||||
i = WaitForMultipleObjects(count, handles, false,
|
i = WaitForMultipleObjects(count, handles, false,
|
||||||
__SIG_POLLING_INTERVAL_MS);
|
__SIG_POLLING_INTERVAL_MS);
|
||||||
if (i == kNtWaitTimeout) {
|
if (i == kNtWaitTimeout) {
|
||||||
continue;
|
return -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == kNtWaitFailed) {
|
if (i == kNtWaitFailed) {
|
||||||
|
@ -117,35 +131,20 @@ static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus,
|
||||||
STRACE("%s failed %u", "GetExitCodeProcess", GetLastError());
|
STRACE("%s failed %u", "GetExitCodeProcess", GetLastError());
|
||||||
return __winerr();
|
return __winerr();
|
||||||
}
|
}
|
||||||
if (dwExitCode == kNtStillActive) continue;
|
if (dwExitCode == kNtStillActive) {
|
||||||
if (opt_out_wstatus) { /* @see WEXITSTATUS() */
|
return -2;
|
||||||
|
}
|
||||||
|
if (opt_out_wstatus) { // @see WEXITSTATUS()
|
||||||
*opt_out_wstatus = (dwExitCode & 0xff) << 8;
|
*opt_out_wstatus = (dwExitCode & 0xff) << 8;
|
||||||
}
|
}
|
||||||
if (opt_out_rusage) {
|
if (opt_out_rusage) {
|
||||||
bzero(opt_out_rusage, sizeof(*opt_out_rusage));
|
bzero(opt_out_rusage, sizeof(*opt_out_rusage));
|
||||||
bzero(&memcount, sizeof(memcount));
|
AddProcessStats(handles[i], opt_out_rusage);
|
||||||
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]);
|
CloseHandle(handles[i]);
|
||||||
__releasefd(pids[i]);
|
__releasefd(pids[i]);
|
||||||
return pids[i];
|
return pids[i];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
||||||
struct rusage *opt_out_rusage) {
|
struct rusage *opt_out_rusage) {
|
||||||
|
@ -153,7 +152,13 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
||||||
sigset_t oldmask, mask = {0};
|
sigset_t oldmask, mask = {0};
|
||||||
sigaddset(&mask, SIGCHLD);
|
sigaddset(&mask, SIGCHLD);
|
||||||
__sig_mask(SIG_BLOCK, &mask, &oldmask);
|
__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);
|
__sig_mask(SIG_SETMASK, &oldmask, 0);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,9 @@ int wait4(int pid, int *opt_out_wstatus, int options,
|
||||||
} else {
|
} else {
|
||||||
rc = sys_wait4_nt(pid, &ws, options, opt_out_rusage);
|
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;
|
END_CANCELLATION_POINT;
|
||||||
STRACE("wait4(%d, [%#x], %d, %p) → %d% m", pid, ws, options, opt_out_rusage,
|
STRACE("wait4(%d, [%#x], %d, %p) → %d% m", pid, ws, options, opt_out_rusage,
|
||||||
|
|
|
@ -56,8 +56,18 @@
|
||||||
vfork:
|
vfork:
|
||||||
.ftrace2
|
.ftrace2
|
||||||
|
|
||||||
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
|
jmp fork
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
|
#if SupportsWindows()
|
||||||
|
// these platforms disagree with vfork
|
||||||
|
testb $_HOSTXNU|_HOSTOPENBSD|_HOSTWINDOWS,__hostos(%rip)
|
||||||
|
jnz fork
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !IsTiny()
|
#if !IsTiny()
|
||||||
push %rbp
|
push %rbp
|
||||||
mov %rsp,%rbp
|
mov %rsp,%rbp
|
||||||
|
@ -69,21 +79,6 @@ vfork:
|
||||||
pop %rbp
|
pop %rbp
|
||||||
#endif
|
#endif
|
||||||
mov %fs:0,%r9 // get thread information block
|
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
|
mov 0x3c(%r9),%r8d // avoid question of @vforksafe errno
|
||||||
pop %rsi // saves return address in a register
|
pop %rsi // saves return address in a register
|
||||||
mov __NR_vfork(%rip),%eax
|
mov __NR_vfork(%rip),%eax
|
||||||
|
@ -106,29 +101,6 @@ vfork:
|
||||||
ret
|
ret
|
||||||
.Lpar: andb $~TIB_FLAG_VFORKED,0x40(%r9)
|
.Lpar: andb $~TIB_FLAG_VFORKED,0x40(%r9)
|
||||||
ret
|
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__)
|
#elif defined(__aarch64__)
|
||||||
|
|
||||||
|
|
|
@ -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),
|
if (!AcceptEx(fd->handle, handle, &buffer, 0, sizeof(buffer.local),
|
||||||
sizeof(buffer.remote), &bytes_received, &overlapped)) {
|
sizeof(buffer.remote), &bytes_received, &overlapped)) {
|
||||||
sockfd = (struct SockFd *)fd->extra;
|
sockfd = (struct SockFd *)fd->extra;
|
||||||
if (__wsablock(fd, &overlapped, &completion_flags, true,
|
if (__wsablock(fd, &overlapped, &completion_flags, kSigOpRestartable,
|
||||||
sockfd->rcvtimeo) == -1) {
|
sockfd->rcvtimeo) == -1) {
|
||||||
WSACloseEvent(overlapped.hEvent);
|
WSACloseEvent(overlapped.hEvent);
|
||||||
__sys_closesocket_nt(handle);
|
__sys_closesocket_nt(handle);
|
||||||
|
|
|
@ -47,7 +47,8 @@ textwindows ssize_t sys_recv_nt(struct Fd *fd, const struct iovec *iov,
|
||||||
} else {
|
} else {
|
||||||
errno = err;
|
errno = err;
|
||||||
sockfd = (struct SockFd *)fd->extra;
|
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));
|
unassert(WSACloseEvent(overlapped.hEvent));
|
||||||
return rc;
|
return rc;
|
||||||
|
|
|
@ -46,7 +46,8 @@ textwindows ssize_t sys_recvfrom_nt(struct Fd *fd, const struct iovec *iov,
|
||||||
} else {
|
} else {
|
||||||
errno = err;
|
errno = err;
|
||||||
sockfd = (struct SockFd *)fd->extra;
|
sockfd = (struct SockFd *)fd->extra;
|
||||||
rc = __wsablock(fd, &overlapped, &flags, true, sockfd->rcvtimeo);
|
rc = __wsablock(fd, &overlapped, &flags, kSigOpRestartable,
|
||||||
|
sockfd->rcvtimeo);
|
||||||
}
|
}
|
||||||
WSACloseEvent(overlapped.hEvent);
|
WSACloseEvent(overlapped.hEvent);
|
||||||
return rc;
|
return rc;
|
||||||
|
|
|
@ -41,7 +41,8 @@ textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sockfd = (struct SockFd *)g_fds.p[fd].extra;
|
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);
|
WSACloseEvent(overlapped.hEvent);
|
||||||
return rc;
|
return rc;
|
||||||
|
|
|
@ -57,7 +57,7 @@ static textwindows int SendfileBlock(int64_t handle,
|
||||||
NTTRACE("WSAWaitForMultipleEvents failed %lm");
|
NTTRACE("WSAWaitForMultipleEvents failed %lm");
|
||||||
return __winsockerr();
|
return __winsockerr();
|
||||||
} else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) {
|
} 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
|
#if _NTTRACE
|
||||||
POLLTRACE("WSAWaitForMultipleEvents...");
|
POLLTRACE("WSAWaitForMultipleEvents...");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -42,7 +42,8 @@ textwindows ssize_t sys_sendto_nt(int fd, const struct iovec *iov,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sockfd = (struct SockFd *)g_fds.p[fd].extra;
|
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);
|
WSACloseEvent(overlapped.hEvent);
|
||||||
return rc;
|
return rc;
|
||||||
|
|
|
@ -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_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,
|
ssize_t sys_recvfrom_nt(struct Fd *, const struct iovec *, size_t, uint32_t,
|
||||||
void *, 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_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -37,8 +37,7 @@
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped,
|
textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped,
|
||||||
uint32_t *flags, bool restartable,
|
uint32_t *flags, int sigops, uint32_t timeout) {
|
||||||
uint32_t timeout) {
|
|
||||||
int e, rc;
|
int e, rc;
|
||||||
uint32_t i, got;
|
uint32_t i, got;
|
||||||
if (WSAGetLastError() != kNtErrorIoPending) {
|
if (WSAGetLastError() != kNtErrorIoPending) {
|
||||||
|
@ -51,7 +50,7 @@ textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped,
|
||||||
WSAGetLastError() == kNtErrorNotFound);
|
WSAGetLastError() == kNtErrorNotFound);
|
||||||
errno = e;
|
errno = e;
|
||||||
} else {
|
} else {
|
||||||
if (_check_interrupts(restartable, g_fds.p)) {
|
if (_check_interrupts(sigops, g_fds.p)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +61,7 @@ textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped,
|
||||||
NTTRACE("WSAWaitForMultipleEvents failed %lm");
|
NTTRACE("WSAWaitForMultipleEvents failed %lm");
|
||||||
return __winsockerr();
|
return __winsockerr();
|
||||||
} else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) {
|
} else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) {
|
||||||
if (_check_interrupts(restartable, g_fds.p)) {
|
if (_check_interrupts(sigops, g_fds.p)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
|
|
|
@ -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 __getrandom(void *p, size_t n, unsigned f) {
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
if (IsWindows()) {
|
if (IsWindows()) {
|
||||||
if (_check_interrupts(true, 0)) return -1;
|
if (_check_interrupts(kSigOpRestartable, 0)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
rc = RtlGenRandom(p, n) ? n : __winerr();
|
rc = RtlGenRandom(p, n) ? n : __winerr();
|
||||||
} else if (have_getrandom) {
|
} else if (have_getrandom) {
|
||||||
if (IsXnu() || IsOpenbsd()) {
|
if (IsXnu() || IsOpenbsd()) {
|
||||||
|
|
56
test/libc/stdio/fds_torture_test.c
Normal file
56
test/libc/stdio/fds_torture_test.c
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/runtime/syslib.internal.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
|
#include "libc/sysv/consts/o.h"
|
||||||
|
#include "libc/testlib/testlib.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
#define THREADS 5
|
||||||
|
#define FDS 10
|
||||||
|
|
||||||
|
void *Torturer(void *arg) {
|
||||||
|
int i;
|
||||||
|
int fd[FDS];
|
||||||
|
for (i = 0; i < FDS; ++i) {
|
||||||
|
ASSERT_NE(-1, (fd[i] = open("/dev/null", O_WRONLY)));
|
||||||
|
}
|
||||||
|
for (i = 0; i < FDS; ++i) {
|
||||||
|
ASSERT_EQ(2, write(fd[i], "hi", 2));
|
||||||
|
if (!fork()) _Exit(0);
|
||||||
|
wait(0);
|
||||||
|
}
|
||||||
|
for (i = 0; i < FDS; ++i) {
|
||||||
|
ASSERT_NE(-1, close(fd[i]));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(fds_torture, test) {
|
||||||
|
int i;
|
||||||
|
pthread_t t[THREADS];
|
||||||
|
for (i = 0; i < THREADS; ++i) {
|
||||||
|
ASSERT_EQ(0, pthread_create(t + i, 0, Torturer, 0));
|
||||||
|
}
|
||||||
|
for (i = 0; i < THREADS; ++i) {
|
||||||
|
ASSERT_EQ(0, pthread_join(t[i], 0));
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,14 +28,17 @@
|
||||||
#include "libc/mem/gc.h"
|
#include "libc/mem/gc.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/internal.h"
|
#include "libc/runtime/internal.h"
|
||||||
|
#include "libc/runtime/memtrack.internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/auxv.h"
|
#include "libc/sysv/consts/auxv.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/testlib/ezbench.h"
|
#include "libc/testlib/ezbench.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
#include "third_party/nsync/mu.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
char testlib_enable_tmp_setup_teardown;
|
char testlib_enable_tmp_setup_teardown;
|
||||||
|
|
|
@ -43,6 +43,7 @@ TEST_LIBC_STDIO_DIRECTDEPS = \
|
||||||
THIRD_PARTY_MBEDTLS \
|
THIRD_PARTY_MBEDTLS \
|
||||||
THIRD_PARTY_MUSL \
|
THIRD_PARTY_MUSL \
|
||||||
THIRD_PARTY_TR \
|
THIRD_PARTY_TR \
|
||||||
|
THIRD_PARTY_NSYNC \
|
||||||
THIRD_PARTY_ZLIB \
|
THIRD_PARTY_ZLIB \
|
||||||
THIRD_PARTY_ZLIB_GZ
|
THIRD_PARTY_ZLIB_GZ
|
||||||
|
|
||||||
|
|
2
third_party/nsync/futex.c
vendored
2
third_party/nsync/futex.c
vendored
|
@ -174,7 +174,7 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare, stru
|
||||||
deadline = timespec_max;
|
deadline = timespec_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!(rc = _check_interrupts (false, 0))) {
|
while (!(rc = _check_interrupts (0, 0))) {
|
||||||
now = timespec_real ();
|
now = timespec_real ();
|
||||||
if (timespec_cmp (now, deadline) > 0) {
|
if (timespec_cmp (now, deadline) > 0) {
|
||||||
rc = etimedout();
|
rc = etimedout();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue