mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Put more thought into i/o polyfills
wait4() is now solid enough to run `make -j100` on Windows. You can now use MSG_DONTWAIT on Windows. There was a handle leak in accept() that's been fixed. Our WIN32 overlapped i/o code has been simplified. Priority class now inherits into subprocesses, so the verynice command will work and the signal mask will now be inherited by execve() and posix_spawn()
This commit is contained in:
parent
736fdb757a
commit
e961385e55
52 changed files with 679 additions and 487 deletions
|
@ -164,6 +164,6 @@ int __getcwd(char *buf, size_t size) {
|
|||
} else {
|
||||
rc = sys_getcwd_metal(buf, size);
|
||||
}
|
||||
STRACE("__getcwd([%#hhs], %'zu) → %d% m", rc != -1 ? buf : "n/a", size, rc);
|
||||
STRACE("getcwd([%#hhs], %'zu) → %d% m", rc != -1 ? buf : "n/a", size, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -126,7 +126,8 @@ textwindows int ntspawn(
|
|||
if (CreateProcess(sb->path, sb->cmdline, 0, 0, true,
|
||||
dwCreationFlags | kNtCreateUnicodeEnvironment |
|
||||
kNtExtendedStartupinfoPresent |
|
||||
kNtInheritParentAffinity,
|
||||
kNtInheritParentAffinity |
|
||||
GetPriorityClass(GetCurrentProcess()),
|
||||
sb->envblock, opt_lpCurrentDirectory,
|
||||
&info.StartupInfo, opt_out_lpProcessInformation)) {
|
||||
rc = 0;
|
||||
|
|
|
@ -32,11 +32,7 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
|
|||
bool restartable) {
|
||||
int sig;
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if ((sig = __sig_get(waitmask))) {
|
||||
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (!restartable || handler_was_called == 1) return eintr();
|
||||
}
|
||||
if ((sig = __sig_get(waitmask))) goto HandleSignal;
|
||||
int expect = 0;
|
||||
atomic_int futex = 0;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
|
@ -45,9 +41,12 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
|
|||
bool32 ok = WaitOnAddress(&futex, &expect, sizeof(int), msdelay);
|
||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||
if (ok && (sig = __sig_get(waitmask))) {
|
||||
HandleSignal:
|
||||
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (!restartable || handler_was_called == 1) return eintr();
|
||||
if (!restartable || (handler_was_called & SIG_HANDLED_NO_RESTART)) {
|
||||
return eintr();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -55,6 +55,8 @@
|
|||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define POLL_INTERVAL_MS 10
|
||||
|
||||
// Polls on the New Technology.
|
||||
//
|
||||
// This function is used to implement poll() and select(). You may poll
|
||||
|
@ -184,7 +186,7 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
|||
remain = timespec_sub(deadline, now);
|
||||
millis = timespec_tomillis(remain);
|
||||
waitfor = MIN(millis, 0xffffffffu);
|
||||
waitfor = MIN(waitfor, __SIG_POLL_INTERVAL_MS);
|
||||
waitfor = MIN(waitfor, POLL_INTERVAL_MS);
|
||||
if (waitfor) {
|
||||
POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor,
|
||||
timespec_tomillis(remain));
|
||||
|
|
|
@ -723,8 +723,6 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
|
|||
int sig;
|
||||
int64_t sem;
|
||||
uint32_t wi, ms = -1;
|
||||
int handler_was_called;
|
||||
struct PosixThread *pt;
|
||||
if (!__ttyconf.vmin) {
|
||||
if (!__ttyconf.vtime) {
|
||||
return 0; // non-blocking w/o raising eagain
|
||||
|
@ -732,31 +730,24 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
|
|||
ms = __ttyconf.vtime * 100;
|
||||
}
|
||||
}
|
||||
if (f->flags & _O_NONBLOCK) {
|
||||
return eagain(); // standard unix non-blocking
|
||||
}
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if ((sig = __sig_get(waitmask))) {
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (handler_was_called != 1) return -2;
|
||||
return eintr();
|
||||
}
|
||||
pt = _pthread_self();
|
||||
pt->pt_semaphore = sem = CreateSemaphore(0, 0, 1, 0);
|
||||
pthread_cleanup_push((void *)CloseHandle, (void *)sem);
|
||||
if (f->flags & _O_NONBLOCK) return eagain();
|
||||
if ((sig = __sig_get(waitmask))) goto DeliverSignal;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->pt_blkmask = waitmask;
|
||||
pt->pt_semaphore = sem = CreateSemaphore(0, 0, 1, 0);
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM, memory_order_release);
|
||||
wi = WaitForMultipleObjects(2, (int64_t[2]){__keystroke.cin, sem}, 0, ms);
|
||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||
pthread_cleanup_pop(true);
|
||||
CloseHandle(sem);
|
||||
if (wi == kNtWaitTimeout) return 0; // vtime elapsed
|
||||
if (wi == 0) return -2; // console data
|
||||
if (wi != 1) return __winerr(); // wait failed
|
||||
if (!(sig = __sig_get(waitmask))) return eintr();
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
DeliverSignal:
|
||||
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (handler_was_called != 1) return -2;
|
||||
if (!(handler_was_called & SIG_HANDLED_NO_RESTART)) return -2;
|
||||
return eintr();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,15 +16,12 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/createfileflags.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/events.h"
|
||||
|
@ -34,28 +31,10 @@
|
|||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
struct ReadwriteResources {
|
||||
int64_t handle;
|
||||
struct NtOverlapped *overlap;
|
||||
};
|
||||
|
||||
static void UnwindReadwrite(void *arg) {
|
||||
uint32_t got;
|
||||
struct ReadwriteResources *rwc = arg;
|
||||
CancelIoEx(rwc->handle, rwc->overlap);
|
||||
GetOverlappedResult(rwc->handle, rwc->overlap, &got, true);
|
||||
CloseHandle(rwc->overlap->hEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs code that's common to read/write/pread/pwrite/etc on Windows.
|
||||
*
|
||||
|
@ -64,17 +43,11 @@ static void UnwindReadwrite(void *arg) {
|
|||
*/
|
||||
textwindows ssize_t
|
||||
sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
|
||||
int64_t handle, uint64_t waitmask,
|
||||
int64_t handle, sigset_t waitmask,
|
||||
bool32 ReadOrWriteFile(int64_t, void *, uint32_t, uint32_t *,
|
||||
struct NtOverlapped *)) {
|
||||
bool32 ok;
|
||||
int sig = 0;
|
||||
int sig;
|
||||
uint32_t exchanged;
|
||||
int olderror = errno;
|
||||
bool eagained = false;
|
||||
bool canceled = false;
|
||||
int handler_was_called;
|
||||
struct PosixThread *pt;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
|
||||
// win32 i/o apis generally take 32-bit values thus we implicitly
|
||||
|
@ -106,33 +79,26 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
|
|||
}
|
||||
|
||||
RestartOperation:
|
||||
bool eagained = false;
|
||||
// check for signals and cancelation
|
||||
if (_check_cancel() == -1) return -1; // ECANCELED
|
||||
if ((sig = __sig_get(waitmask))) goto HandleInterrupt;
|
||||
|
||||
// signals have already been fully blocked by caller
|
||||
// perform i/o operation with atomic signal/cancel checking
|
||||
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 1, 0, 0),
|
||||
.Pointer = offset};
|
||||
struct ReadwriteResources rwc = {handle, &overlap};
|
||||
pthread_cleanup_push(UnwindReadwrite, &rwc);
|
||||
ok = ReadOrWriteFile(handle, data, size, 0, &overlap);
|
||||
bool32 ok = ReadOrWriteFile(handle, data, size, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) {
|
||||
// win32 says this i/o operation needs to block
|
||||
if (f->flags & _O_NONBLOCK) {
|
||||
// abort the i/o operation if file descriptor is in non-blocking mode
|
||||
CancelIoEx(handle, &overlap);
|
||||
eagained = true;
|
||||
} else if (_check_cancel()) {
|
||||
// _check_cancel() can go three ways:
|
||||
// 1. it'll return 0 if we're fine and no thread cancelation happened
|
||||
// 2. it'll pthread_exit() and cleanup, when cancelation was deferred
|
||||
// 3. it'll return -1 and raise ECANCELED if a cancelation was masked
|
||||
CancelIoEx(handle, &overlap);
|
||||
canceled = true;
|
||||
} else if ((sig = __sig_get(waitmask))) {
|
||||
// we've dequeued a signal that was pending per caller's old sigmask
|
||||
// we can't call the signal handler until we release win32 resources
|
||||
CancelIoEx(handle, &overlap);
|
||||
} else {
|
||||
// wait until i/o either completes or is canceled by another thread
|
||||
// we avoid a race condition by having a second mask for unblocking
|
||||
struct PosixThread *pt;
|
||||
pt = _pthread_self();
|
||||
pt->pt_blkmask = waitmask;
|
||||
pt->pt_iohandle = handle;
|
||||
|
@ -147,30 +113,13 @@ RestartOperation:
|
|||
if (ok) {
|
||||
ok = GetOverlappedResult(handle, &overlap, &exchanged, true);
|
||||
}
|
||||
pthread_cleanup_pop(false);
|
||||
CloseHandle(overlap.hEvent);
|
||||
|
||||
// if we acknowledged a pending masked mode cancelation request then
|
||||
// we must pass it to the caller immediately now that cleanup's done
|
||||
if (canceled) {
|
||||
return ecanceled();
|
||||
}
|
||||
|
||||
// if we removed a pending signal then we must raise it
|
||||
// it's now safe to call a signal handler that longjmps
|
||||
if (sig) {
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
} else {
|
||||
handler_was_called = 0;
|
||||
}
|
||||
|
||||
// if i/o succeeded then return its result
|
||||
if (ok) {
|
||||
if (!pwriting && seekable) {
|
||||
f->pointer = offset + exchanged;
|
||||
}
|
||||
errno = olderror;
|
||||
return exchanged;
|
||||
}
|
||||
|
||||
|
@ -180,18 +129,16 @@ RestartOperation:
|
|||
if (eagained) {
|
||||
return eagain();
|
||||
}
|
||||
// at this point the i/o must have been canceled due to a signal.
|
||||
// this could be because we found the signal earlier and canceled
|
||||
// ourself. otherwise it's due to a kill from another thread that
|
||||
// added something to our mask and canceled our i/o, so we check.
|
||||
if (!handler_was_called && (sig = __sig_get(waitmask))) {
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
}
|
||||
// otherwise it must be due to a kill() via __sig_cancel()
|
||||
if ((sig = __sig_get(waitmask))) {
|
||||
HandleInterrupt:
|
||||
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1; // possible if we SIGTHR'd
|
||||
// read() is @restartable unless non-SA_RESTART hands were called
|
||||
if (handler_was_called != 1) {
|
||||
if (!(handler_was_called & SIG_HANDLED_NO_RESTART)) {
|
||||
goto RestartOperation;
|
||||
}
|
||||
}
|
||||
return eintr();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "ape/sections.internal.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
|
@ -30,34 +28,26 @@
|
|||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/bsf.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/popcnt.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/context.h"
|
||||
#include "libc/nt/enum/exceptionhandleractions.h"
|
||||
#include "libc/nt/enum/signal.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/signals.h"
|
||||
#include "libc/nt/struct/context.h"
|
||||
#include "libc/nt/struct/ntexceptionpointers.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/ss.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
/**
|
||||
|
@ -206,16 +196,19 @@ textwindows int __sig_raise(volatile int sig, int sic) {
|
|||
memory_order_acquire);
|
||||
|
||||
// call the user's signal handler
|
||||
char ssbuf[2][128];
|
||||
char ssbuf[128];
|
||||
siginfo_t si = {.si_signo = sig, .si_code = sic};
|
||||
STRACE("__sig_raise(%G, %t) mask %s → %s", sig, __sig_handler(rva),
|
||||
(DescribeSigset)(ssbuf[0], 0, &ctx.uc_sigmask),
|
||||
(DescribeSigset)(ssbuf[1], 0, (sigset_t *)&pt->tib->tib_sigmask));
|
||||
STRACE("__sig_raise(%G, %t) mask %s", sig, __sig_handler(rva),
|
||||
(DescribeSigset)(ssbuf, 0, (sigset_t *)&pt->tib->tib_sigmask));
|
||||
__sig_handler(rva)(sig, &si, &ctx);
|
||||
(void)ssbuf;
|
||||
|
||||
// record this handler
|
||||
handler_was_called |= (flags & SA_RESTART) ? 2 : 1;
|
||||
if (flags & SA_RESTART) {
|
||||
handler_was_called |= SIG_HANDLED_SA_RESTART;
|
||||
} else {
|
||||
handler_was_called |= SIG_HANDLED_NO_RESTART;
|
||||
}
|
||||
}
|
||||
|
||||
// restore sigmask
|
||||
|
@ -320,12 +313,18 @@ static int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
|||
__sig_terminate(sig);
|
||||
}
|
||||
|
||||
// ignore signals already pending
|
||||
uintptr_t th = _pthread_syshand(pt);
|
||||
if (atomic_load_explicit(&pt->tib->tib_sigpending, memory_order_acquire) &
|
||||
(1ull << (sig - 1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// take control of thread
|
||||
// suspending the thread happens asynchronously
|
||||
// however getting the context blocks until it's frozen
|
||||
static pthread_spinlock_t killer_lock;
|
||||
pthread_spin_lock(&killer_lock);
|
||||
uintptr_t th = _pthread_syshand(pt);
|
||||
if (SuspendThread(th) == -1u) {
|
||||
STRACE("SuspendThread failed w/ %d", GetLastError());
|
||||
pthread_spin_unlock(&killer_lock);
|
||||
|
@ -414,6 +413,10 @@ textwindows void __sig_generate(int sig, int sic) {
|
|||
STRACE("terminating on %G due to no handler", sig);
|
||||
__sig_terminate(sig);
|
||||
}
|
||||
if (atomic_load_explicit(&__sig.pending, memory_order_acquire) &
|
||||
(1ull << (sig - 1))) {
|
||||
return;
|
||||
}
|
||||
BLOCK_SIGNALS;
|
||||
_pthread_lock();
|
||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
||||
|
@ -428,7 +431,6 @@ textwindows void __sig_generate(int sig, int sic) {
|
|||
// choose this thread if it isn't masking sig
|
||||
if (!(atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
|
||||
(1ull << (sig - 1)))) {
|
||||
STRACE("generating %G by killing %d", sig, _pthread_tid(pt));
|
||||
_pthread_ref(pt);
|
||||
mark = pt;
|
||||
break;
|
||||
|
@ -439,7 +441,6 @@ textwindows void __sig_generate(int sig, int sic) {
|
|||
if (atomic_load_explicit(&pt->pt_blocker, memory_order_acquire) &&
|
||||
!(atomic_load_explicit(&pt->pt_blkmask, memory_order_relaxed) &
|
||||
(1ull << (sig - 1)))) {
|
||||
STRACE("generating %G by unblocking %d", sig, _pthread_tid(pt));
|
||||
_pthread_ref(pt);
|
||||
mark = pt;
|
||||
break;
|
||||
|
@ -450,7 +451,6 @@ textwindows void __sig_generate(int sig, int sic) {
|
|||
__sig_killer(mark, sig, sic);
|
||||
_pthread_unref(mark);
|
||||
} else {
|
||||
STRACE("all threads block %G so adding to pending signals of process", sig);
|
||||
atomic_fetch_or_explicit(&__sig.pending, 1ull << (sig - 1),
|
||||
memory_order_relaxed);
|
||||
}
|
||||
|
|
|
@ -3,13 +3,8 @@
|
|||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
#define __SIG_LOCK_INTERVAL_MS 1000 /* semaphore synchronization: solid */
|
||||
#define __SIG_SIG_INTERVAL_MS 1000 /* posix signal polyfill also solid */
|
||||
#define __SIG_PROC_INTERVAL_MS 1000 /* process waiting also pretty good */
|
||||
#define __SIG_IO_INTERVAL_MS 1000 /* read/write cancel/notify is good */
|
||||
#define __SIG_POLL_INTERVAL_MS 20 /* poll on windows is dumpster fire */
|
||||
#define __SIG_LOGGING_INTERVAL_MS 1700
|
||||
#define __SIG_QUEUE_LENGTH 32
|
||||
#define SIG_HANDLED_NO_RESTART 1
|
||||
#define SIG_HANDLED_SA_RESTART 2
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
|
|
@ -41,10 +41,10 @@ int sys_futex(int *, int, int, const struct timespec *, int *);
|
|||
static inline struct timespec timespec_fromseconds(int64_t __x) {
|
||||
return (struct timespec){__x};
|
||||
}
|
||||
static inline bool timespec_iszero(struct timespec __ts) {
|
||||
static inline int timespec_iszero(struct timespec __ts) {
|
||||
return !(__ts.tv_sec | __ts.tv_nsec);
|
||||
}
|
||||
static inline bool timespec_isvalid(struct timespec __ts) {
|
||||
static inline int timespec_isvalid(struct timespec __ts) {
|
||||
return __ts.tv_sec >= 0 && __ts.tv_nsec + 0ull < 1000000000ull;
|
||||
}
|
||||
#endif /* _COSMO_SOURCE */
|
||||
|
|
|
@ -39,10 +39,10 @@ static inline struct timeval timeval_fromseconds(int64_t __x) {
|
|||
static inline struct timespec timeval_totimespec(struct timeval __tv) {
|
||||
return (struct timespec){__tv.tv_sec, __tv.tv_usec * 1000};
|
||||
}
|
||||
static inline bool timeval_iszero(struct timeval __tv) {
|
||||
static inline int timeval_iszero(struct timeval __tv) {
|
||||
return !(__tv.tv_sec | __tv.tv_usec);
|
||||
}
|
||||
static inline bool timeval_isvalid(struct timeval __tv) {
|
||||
static inline int timeval_isvalid(struct timeval __tv) {
|
||||
return __tv.tv_sec >= 0 && __tv.tv_usec + 0ull < 1000000ull;
|
||||
}
|
||||
#endif /* _COSMO_SOURCE */
|
||||
|
|
|
@ -17,17 +17,14 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
int __tailcontext(const ucontext_t *);
|
||||
|
||||
static int __contextmask(const sigset_t *opt_set, sigset_t *opt_out_oldset) {
|
||||
// now context switching needs to go 14x slower
|
||||
return sigprocmask(SIG_SETMASK, opt_set, opt_out_oldset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets machine context.
|
||||
*
|
||||
|
@ -37,14 +34,31 @@ static int __contextmask(const sigset_t *opt_set, sigset_t *opt_out_oldset) {
|
|||
* @see getcontext()
|
||||
*/
|
||||
int setcontext(const ucontext_t *uc) {
|
||||
if (__contextmask(&uc->uc_sigmask, 0)) return -1;
|
||||
if (IsWindows()) {
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, uc->uc_sigmask,
|
||||
memory_order_release);
|
||||
} else {
|
||||
sys_sigprocmask(SIG_SETMASK, &uc->uc_sigmask, 0);
|
||||
}
|
||||
return __tailcontext(uc);
|
||||
}
|
||||
|
||||
int __getcontextsig(ucontext_t *uc) {
|
||||
return __contextmask(0, &uc->uc_sigmask);
|
||||
if (IsWindows()) {
|
||||
uc->uc_sigmask =
|
||||
atomic_load_explicit(&__get_tls()->tib_sigmask, memory_order_acquire);
|
||||
return 0;
|
||||
} else {
|
||||
return sys_sigprocmask(SIG_SETMASK, 0, &uc->uc_sigmask);
|
||||
}
|
||||
}
|
||||
|
||||
int __swapcontextsig(ucontext_t *x, const ucontext_t *y) {
|
||||
return __contextmask(&y->uc_sigmask, &x->uc_sigmask);
|
||||
if (IsWindows()) {
|
||||
x->uc_sigmask = atomic_exchange_explicit(
|
||||
&__get_tls()->tib_sigmask, y->uc_sigmask, memory_order_acquire);
|
||||
return 0;
|
||||
} else {
|
||||
return sys_sigprocmask(SIG_SETMASK, &y->uc_sigmask, &x->uc_sigmask);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,8 @@ imaxdiv_t imaxdiv(intmax_t, intmax_t) pureconst;
|
|||
#endif
|
||||
|
||||
#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 406 || defined(__llvm__)) && \
|
||||
!defined(__STRICT_ANSI__) && defined(_COSMO_SOURCE)
|
||||
!defined(__STRICT_ANSI__) && defined(_COSMO_SOURCE) && \
|
||||
!defined(__COSMOCC__)
|
||||
int128_t i128abs(int128_t)
|
||||
libcesque pureconst;
|
||||
int128_t strtoi128(const char *, char **, int) paramsnonnull((1));
|
||||
|
|
|
@ -71,7 +71,7 @@ TryAgain:
|
|||
switch (__imp_GetLastError()) {
|
||||
case kNtErrorPipeBusy:
|
||||
if (micros >= 1024) __imp_Sleep(micros / 1024);
|
||||
if (micros / 1024 < __SIG_IO_INTERVAL_MS) micros <<= 1;
|
||||
if (micros < 1024 * 1024) micros <<= 1;
|
||||
goto TryAgain;
|
||||
case kNtErrorAccessDenied:
|
||||
// GetNtOpenFlags() always greedily requests execute permissions
|
||||
|
|
|
@ -50,7 +50,7 @@ TryAgain:
|
|||
nDefaultTimeOutMs, opt_lpSecurityAttributes);
|
||||
if (hServer == -1 && __imp_GetLastError() == kNtErrorPipeBusy) {
|
||||
if (micros >= 1024) __imp_Sleep(micros / 1024);
|
||||
if (micros / 1024 < __SIG_IO_INTERVAL_MS) micros <<= 1;
|
||||
if (micros < 1024 * 1024) micros <<= 1;
|
||||
goto TryAgain;
|
||||
}
|
||||
if (hServer == -1) __winerr();
|
||||
|
|
|
@ -23,9 +23,7 @@
|
|||
|
||||
static bool _pthread_deref(struct PosixThread *pt) {
|
||||
int refs = atomic_load_explicit(&pt->pt_refs, memory_order_acquire);
|
||||
if (!refs) return true;
|
||||
unassert(refs > 0);
|
||||
return !atomic_fetch_sub(&pt->pt_refs, 1);
|
||||
return !refs || !atomic_fetch_sub(&pt->pt_refs, 1);
|
||||
}
|
||||
|
||||
void _pthread_unref(struct PosixThread *pt) {
|
||||
|
|
|
@ -42,7 +42,7 @@ textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) {
|
|||
if (how == SIG_BLOCK) {
|
||||
oldmask = atomic_fetch_or_explicit(mask, *neu, memory_order_acq_rel);
|
||||
} else if (how == SIG_UNBLOCK) {
|
||||
oldmask = atomic_fetch_and_explicit(mask, *neu, memory_order_acq_rel);
|
||||
oldmask = atomic_fetch_and_explicit(mask, ~*neu, memory_order_acq_rel);
|
||||
} else { // SIG_SETMASK
|
||||
oldmask = atomic_exchange_explicit(mask, *neu, memory_order_acq_rel);
|
||||
}
|
||||
|
|
|
@ -30,9 +30,7 @@ __msabi extern typeof(WSARecv) *const __imp_WSARecv;
|
|||
|
||||
/**
|
||||
* Receives data from Windows socket.
|
||||
*
|
||||
* @return 0 on success, or -1 on failure
|
||||
* @note this wrapper takes care of ABI, STRACE(), and __winerr()
|
||||
*/
|
||||
textwindows int WSARecv(
|
||||
uint64_t s, const struct NtIovec *inout_lpBuffers, uint32_t dwBufferCount,
|
||||
|
@ -57,9 +55,6 @@ textwindows int WSARecv(
|
|||
if (opt_out_lpNumberOfBytesRecvd) {
|
||||
*opt_out_lpNumberOfBytesRecvd = NumberOfBytesRecvd;
|
||||
}
|
||||
if (rc == -1) {
|
||||
__winsockerr();
|
||||
}
|
||||
if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) {
|
||||
kprintf(STRACE_PROLOGUE "WSARecv(%lu, [", s);
|
||||
DescribeIovNt(inout_lpBuffers, dwBufferCount,
|
||||
|
@ -73,9 +68,6 @@ textwindows int WSARecv(
|
|||
rc = __imp_WSARecv(s, inout_lpBuffers, dwBufferCount,
|
||||
opt_out_lpNumberOfBytesRecvd, inout_lpFlags,
|
||||
opt_inout_lpOverlapped, opt_lpCompletionRoutine);
|
||||
if (rc == -1) {
|
||||
__winsockerr();
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -29,9 +29,7 @@ __msabi extern typeof(WSARecvFrom) *const __imp_WSARecvFrom;
|
|||
|
||||
/**
|
||||
* Receives data from Windows socket.
|
||||
*
|
||||
* @return 0 on success, or -1 on failure
|
||||
* @note this wrapper takes care of ABI, STRACE(), and __winerr()
|
||||
*/
|
||||
textwindows int WSARecvFrom(
|
||||
uint64_t s, const struct NtIovec *inout_lpBuffers, uint32_t dwBufferCount,
|
||||
|
@ -52,9 +50,6 @@ textwindows int WSARecvFrom(
|
|||
if (opt_out_lpNumberOfBytesRecvd) {
|
||||
*opt_out_lpNumberOfBytesRecvd = NumberOfBytesRecvd;
|
||||
}
|
||||
if (rc == -1) {
|
||||
__winerr();
|
||||
}
|
||||
if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) {
|
||||
kprintf(STRACE_PROLOGUE "WSARecvFrom(%lu, [", s);
|
||||
DescribeIovNt(inout_lpBuffers, dwBufferCount,
|
||||
|
@ -69,9 +64,6 @@ textwindows int WSARecvFrom(
|
|||
opt_out_lpNumberOfBytesRecvd, inout_lpFlags,
|
||||
opt_out_fromsockaddr, opt_inout_fromsockaddrlen,
|
||||
opt_inout_lpOverlapped, opt_lpCompletionRoutine);
|
||||
if (rc == -1) {
|
||||
__winerr();
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/enum/processaccess.h"
|
||||
|
@ -46,7 +47,7 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
|
|||
char *const envp[]) {
|
||||
|
||||
// execve() needs to be @asyncsignalsafe
|
||||
sigset_t m = __sig_block();
|
||||
sigset_t sigmask = __sig_block();
|
||||
_pthread_lock();
|
||||
|
||||
// new process should be a child of our parent
|
||||
|
@ -55,10 +56,14 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
|
|||
if (!(hParentProcess = OpenProcess(
|
||||
kNtProcessDupHandle | kNtProcessCreateProcess, false, ppid))) {
|
||||
_pthread_unlock();
|
||||
__sig_unblock(m);
|
||||
__sig_unblock(sigmask);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// inherit signal mask
|
||||
char maskvar[6 + 21];
|
||||
FormatUint64(stpcpy(maskvar, "_MASK="), sigmask);
|
||||
|
||||
// define stdio handles for the spawned subprocess
|
||||
struct NtStartupInfo si = {
|
||||
.cb = sizeof(struct NtStartupInfo),
|
||||
|
@ -80,21 +85,21 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
|
|||
&lpExplicitHandles, &dwExplicitHandleCount))) {
|
||||
CloseHandle(hParentProcess);
|
||||
_pthread_unlock();
|
||||
__sig_unblock(m);
|
||||
__sig_unblock(sigmask);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// launch the process
|
||||
struct NtProcessInformation pi;
|
||||
int rc = ntspawn(AT_FDCWD, program, argv, envp, (char *[]){fdspec, 0}, 0, 0,
|
||||
hParentProcess, lpExplicitHandles, dwExplicitHandleCount,
|
||||
&si, &pi);
|
||||
int rc = ntspawn(AT_FDCWD, program, argv, envp,
|
||||
(char *[]){fdspec, maskvar, 0}, 0, 0, hParentProcess,
|
||||
lpExplicitHandles, dwExplicitHandleCount, &si, &pi);
|
||||
__undescribe_fds(hParentProcess, lpExplicitHandles, dwExplicitHandleCount);
|
||||
if (rc == -1) {
|
||||
free(fdspec);
|
||||
CloseHandle(hParentProcess);
|
||||
_pthread_unlock();
|
||||
__sig_unblock(m);
|
||||
__sig_unblock(sigmask);
|
||||
if (GetLastError() == kNtErrorSharingViolation) {
|
||||
return etxtbsy();
|
||||
} else {
|
||||
|
|
|
@ -401,7 +401,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
|||
}
|
||||
}
|
||||
if (rc == -1) {
|
||||
__proc_free(proc);
|
||||
dll_make_first(&__proc.free, &proc->elem);
|
||||
}
|
||||
ftrace_enabled(+1);
|
||||
strace_enabled(+1);
|
||||
|
|
|
@ -248,9 +248,9 @@ static textwindows errno_t posix_spawn_nt_impl(
|
|||
struct SpawnFds fds = {0};
|
||||
int64_t dirhand = AT_FDCWD;
|
||||
int64_t *lpExplicitHandles = 0;
|
||||
sigset_t sigmask = __sig_block();
|
||||
uint32_t dwExplicitHandleCount = 0;
|
||||
int64_t hCreatorProcess = GetCurrentProcess();
|
||||
sigset_t m = __sig_block();
|
||||
|
||||
// reserve process tracking object
|
||||
__proc_lock();
|
||||
|
@ -266,11 +266,11 @@ static textwindows errno_t posix_spawn_nt_impl(
|
|||
free(fdspec);
|
||||
if (proc) {
|
||||
__proc_lock();
|
||||
__proc_free(proc);
|
||||
dll_make_first(&__proc.free, &proc->elem);
|
||||
__proc_unlock();
|
||||
}
|
||||
spawnfds_destroy(&fds);
|
||||
__sig_unblock(m);
|
||||
__sig_unblock(sigmask);
|
||||
errno = e;
|
||||
return err;
|
||||
}
|
||||
|
@ -360,13 +360,17 @@ static textwindows errno_t posix_spawn_nt_impl(
|
|||
}
|
||||
}
|
||||
|
||||
// inherit signal mask
|
||||
char maskvar[6 + 21];
|
||||
FormatUint64(stpcpy(maskvar, "_MASK="), sigmask);
|
||||
|
||||
// launch process
|
||||
int rc = -1;
|
||||
struct NtProcessInformation procinfo;
|
||||
if (!envp) envp = environ;
|
||||
if ((fdspec = __describe_fds(fds.p, fds.n, &startinfo, hCreatorProcess,
|
||||
&lpExplicitHandles, &dwExplicitHandleCount))) {
|
||||
rc = ntspawn(dirhand, path, argv, envp, (char *[]){fdspec, 0},
|
||||
rc = ntspawn(dirhand, path, argv, envp, (char *[]){fdspec, maskvar, 0},
|
||||
dwCreationFlags, lpCurrentDirectory, 0, lpExplicitHandles,
|
||||
dwExplicitHandleCount, &startinfo, &procinfo);
|
||||
}
|
||||
|
|
|
@ -32,15 +32,20 @@
|
|||
* - `POSIX_SPAWN_SETSCHEDPARAM`
|
||||
* - `POSIX_SPAWN_SETSCHEDULER`
|
||||
* - `POSIX_SPAWN_SETSID`
|
||||
* - `POSIX_SPAWN_SETRLIMIT`
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise EINVAL if `flags` has invalid bits
|
||||
*/
|
||||
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
|
||||
if (flags &
|
||||
~(POSIX_SPAWN_USEVFORK | POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP |
|
||||
POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK |
|
||||
POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER |
|
||||
POSIX_SPAWN_SETSID | POSIX_SPAWN_SETRLIMIT)) {
|
||||
if (flags & ~(POSIX_SPAWN_USEVFORK | //
|
||||
POSIX_SPAWN_RESETIDS | //
|
||||
POSIX_SPAWN_SETPGROUP | //
|
||||
POSIX_SPAWN_SETSIGDEF | //
|
||||
POSIX_SPAWN_SETSIGMASK | //
|
||||
POSIX_SPAWN_SETSCHEDPARAM | //
|
||||
POSIX_SPAWN_SETSCHEDULER | //
|
||||
POSIX_SPAWN_SETSID | //
|
||||
POSIX_SPAWN_SETRLIMIT)) {
|
||||
return EINVAL;
|
||||
}
|
||||
(*attr)->flags = flags;
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
|
@ -25,14 +24,14 @@
|
|||
* Setting `pgroup` to zero will ensure newly created processes are
|
||||
* placed within their own brand new process group.
|
||||
*
|
||||
* This setter also sets the `POSIX_SPAWN_SETPGROUP` flag.
|
||||
* You also need to pass `POSIX_SPAWN_SETPGROUP` to
|
||||
* posix_spawnattr_setflags() for it to take effect.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param pgroup is the process group id, or 0 for self
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, int pgroup) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETPGROUP;
|
||||
(*attr)->pgroup = pgroup;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
/**
|
||||
* Sets resource limit on spawned process.
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETRLIMIT`.
|
||||
* You also need to pass `POSIX_SPAWN_SETRLIMIT` to
|
||||
* posix_spawnattr_setflags() for it to take effect.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise EINVAL if resource is invalid
|
||||
|
@ -34,7 +35,6 @@
|
|||
int posix_spawnattr_setrlimit(posix_spawnattr_t *attr, int resource,
|
||||
const struct rlimit *rlim) {
|
||||
if (0 <= resource && resource < MIN(RLIM_NLIMITS, ARRAYLEN((*attr)->rlim))) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETRLIMIT;
|
||||
(*attr)->rlimset |= 1u << resource;
|
||||
(*attr)->rlim[resource] = *rlim;
|
||||
return 0;
|
||||
|
|
|
@ -16,14 +16,13 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sched_param.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Specifies scheduler parameter override for spawned process.
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETSCHEDPARAM`.
|
||||
* You also need to pass `POSIX_SPAWN_SETSCHEDPARAM` to
|
||||
* posix_spawnattr_setflags() for it to take effect.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param schedparam receives the result
|
||||
|
@ -31,7 +30,6 @@
|
|||
*/
|
||||
int posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
|
||||
const struct sched_param *schedparam) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSCHEDPARAM;
|
||||
(*attr)->schedparam = *schedparam;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,19 +16,18 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Specifies scheduler policy override for spawned process.
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETSCHEDULER`.
|
||||
* You also need to pass `POSIX_SPAWN_SETSCHEDULER` to
|
||||
* posix_spawnattr_setflags() for it to take effect.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSCHEDULER;
|
||||
(*attr)->schedpolicy = schedpolicy;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
|
@ -32,13 +30,13 @@
|
|||
* set the signals to `SIG_IGN` earlier (since posix_spawn() will not
|
||||
* issue O(128) system calls just to be totally pedantic about that).
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETSIGDEF`.
|
||||
* You also need to pass `POSIX_SPAWN_SETSIGDEF` to
|
||||
* posix_spawnattr_setflags() for it to take effect.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr,
|
||||
const sigset_t *sigdefault) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSIGDEF;
|
||||
(*attr)->sigdefault = *sigdefault;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,20 +16,18 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Specifies signal mask for sigprocmask() in child process.
|
||||
*
|
||||
* This setter also sets the `POSIX_SPAWN_SETSIGMASK` flag.
|
||||
* You also need to pass `POSIX_SPAWN_SETSIGMASK` to
|
||||
* posix_spawnattr_setflags() for it to take effect.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
|
||||
const sigset_t *sigmask) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSIGMASK;
|
||||
(*attr)->sigmask = *sigmask;
|
||||
return 0;
|
||||
}
|
||||
|
|
200
libc/proc/proc.c
200
libc/proc/proc.c
|
@ -16,7 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
|
@ -37,6 +36,7 @@
|
|||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/events.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/filetime.h"
|
||||
|
@ -59,61 +59,33 @@
|
|||
|
||||
struct Procs __proc;
|
||||
|
||||
static textwindows void GetProcessStats(int64_t h, struct rusage *ru) {
|
||||
static textwindows void __proc_stats(int64_t h, struct rusage *ru) {
|
||||
bzero(ru, sizeof(*ru));
|
||||
struct NtProcessMemoryCountersEx memcount = {sizeof(memcount)};
|
||||
unassert(GetProcessMemoryInfo(h, &memcount, sizeof(memcount)));
|
||||
GetProcessMemoryInfo(h, &memcount, sizeof(memcount));
|
||||
ru->ru_maxrss = memcount.PeakWorkingSetSize / 1024;
|
||||
ru->ru_majflt = memcount.PageFaultCount;
|
||||
struct NtFileTime createtime, exittime;
|
||||
struct NtFileTime kerneltime, usertime;
|
||||
unassert(GetProcessTimes(h, &createtime, &exittime, &kerneltime, &usertime));
|
||||
GetProcessTimes(h, &createtime, &exittime, &kerneltime, &usertime);
|
||||
ru->ru_utime = WindowsDurationToTimeVal(ReadFileTime(usertime));
|
||||
ru->ru_stime = WindowsDurationToTimeVal(ReadFileTime(kerneltime));
|
||||
struct NtIoCounters iocount;
|
||||
unassert(GetProcessIoCounters(h, &iocount));
|
||||
GetProcessIoCounters(h, &iocount);
|
||||
ru->ru_inblock = iocount.ReadOperationCount;
|
||||
ru->ru_oublock = iocount.WriteOperationCount;
|
||||
}
|
||||
|
||||
static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
|
||||
__bootstrap_tls(&__proc.tls, __builtin_frame_address(0));
|
||||
for (;;) {
|
||||
|
||||
// assemble a group of processes to wait on. if more than 64
|
||||
// children exist, then we'll use a small timeout and select
|
||||
// processes with a shifting window via a double linked list
|
||||
// performs accounting on exited process
|
||||
// multiple threads can wait on a process
|
||||
// it's important that only one calls this
|
||||
textwindows int __proc_harvest(struct Proc *pr, bool iswait4) {
|
||||
int sic = 0;
|
||||
uint32_t status;
|
||||
struct rusage ru;
|
||||
int64_t handles[64];
|
||||
int sic, dosignal = 0;
|
||||
struct Proc *pr, *objects[64];
|
||||
struct Dll *e, *e2, *samples = 0;
|
||||
uint32_t millis, status, i, n = 1;
|
||||
__proc_lock();
|
||||
handles[0] = __proc.onstart;
|
||||
for (e = dll_first(__proc.list); e && n < 64; ++n, e = e2) {
|
||||
pr = PROC_CONTAINER(e);
|
||||
e2 = dll_next(__proc.list, e);
|
||||
dll_remove(&__proc.list, e);
|
||||
dll_make_last(&samples, e);
|
||||
handles[n] = pr->handle;
|
||||
objects[n] = pr;
|
||||
}
|
||||
dll_make_last(&__proc.list, samples);
|
||||
__proc_unlock();
|
||||
|
||||
// wait for win32 to report any status change
|
||||
millis = n == 64 ? 20 : -1u;
|
||||
unassert((i = WaitForMultipleObjects(n, handles, false, millis)) != -1u);
|
||||
i &= ~kNtWaitAbandoned;
|
||||
if (!i || i == kNtWaitTimeout) continue;
|
||||
GetExitCodeProcess(handles[i], &status);
|
||||
if (status == kNtStillActive) continue;
|
||||
GetProcessStats(handles[i], &ru);
|
||||
|
||||
// update data structures and notify folks
|
||||
__proc_lock();
|
||||
pr = objects[i];
|
||||
GetExitCodeProcess(pr->handle, &status);
|
||||
if (status == kNtStillActive) return 0;
|
||||
__proc_stats(pr->handle, &ru);
|
||||
rusage_add(&pr->ru, &ru);
|
||||
rusage_add(&__proc.ruchlds, &ru);
|
||||
if ((status & 0xFF000000u) == 0x23000000u) {
|
||||
|
@ -121,33 +93,133 @@ static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
|
|||
CloseHandle(pr->handle);
|
||||
pr->handle = status & 0x00FFFFFF;
|
||||
} else {
|
||||
// handle child _exit()
|
||||
CloseHandle(pr->handle);
|
||||
// handle child _Exit()
|
||||
if (status == 0xc9af3d51u) {
|
||||
status = kNtStillActive;
|
||||
}
|
||||
pr->wstatus = status;
|
||||
if ((__sighandrvas[SIGCHLD] == (uintptr_t)SIG_IGN ||
|
||||
(__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) &&
|
||||
(!pr->waiters && !__proc.waiters)) {
|
||||
if (!iswait4 && !pr->waiters && !__proc.waiters &&
|
||||
(__sighandrvas[SIGCHLD] == (uintptr_t)SIG_IGN ||
|
||||
(__sighandflags[SIGCHLD] & SA_NOCLDWAIT))) {
|
||||
// perform automatic zombie reaping
|
||||
dll_remove(&__proc.list, &pr->elem);
|
||||
dll_make_first(&__proc.free, &pr->elem);
|
||||
CloseHandle(pr->handle);
|
||||
} else {
|
||||
pr->iszombie = 1;
|
||||
// transitions process to zombie state
|
||||
// wait4 is responsible for reaping it
|
||||
pr->status = PROC_ZOMBIE;
|
||||
dll_remove(&__proc.list, &pr->elem);
|
||||
dll_make_first(&__proc.zombies, &pr->elem);
|
||||
if (pr->waiters) {
|
||||
nsync_cv_broadcast(&pr->onexit);
|
||||
} else if (__proc.waiters) {
|
||||
nsync_cv_signal(&__proc.onexit);
|
||||
SetEvent(__proc.haszombies);
|
||||
if (!pr->waiters && !__proc.waiters) {
|
||||
if (WIFSIGNALED(status)) {
|
||||
sic = CLD_KILLED;
|
||||
} else {
|
||||
dosignal = 1;
|
||||
sic = WIFSIGNALED(status) ? CLD_KILLED : CLD_EXITED;
|
||||
sic = CLD_EXITED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sic;
|
||||
}
|
||||
|
||||
static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
|
||||
struct CosmoTib tls;
|
||||
__bootstrap_tls(&tls, __builtin_frame_address(0));
|
||||
for (;;) {
|
||||
|
||||
// assemble a group of processes to wait on. if more than 64
|
||||
// children exist, then we'll use a small timeout and select
|
||||
// processes with a shifting window via a double linked list
|
||||
// if fewer than 64 processes exist then we'll also wait for
|
||||
// process birth notifications, and wait on them immediately
|
||||
int64_t handles[64];
|
||||
struct Proc *objects[64];
|
||||
uint32_t millis, i, n = 0;
|
||||
struct Dll *e, *e2, *samples = 0;
|
||||
__proc_lock();
|
||||
for (e = dll_first(__proc.list); e && n < 64; e = e2) {
|
||||
struct Proc *pr = PROC_CONTAINER(e);
|
||||
e2 = dll_next(__proc.list, e);
|
||||
// cycle process to end of list
|
||||
dll_remove(&__proc.list, e);
|
||||
dll_make_last(&samples, e);
|
||||
// don't bother waiting if it's already awaited
|
||||
if (!pr->waiters) {
|
||||
handles[n] = pr->handle;
|
||||
objects[n] = pr;
|
||||
++pr->waiters;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
dll_make_last(&__proc.list, samples);
|
||||
__proc_unlock();
|
||||
if (dosignal) {
|
||||
|
||||
// wait for something to happen
|
||||
if (n == 64) {
|
||||
millis = 5;
|
||||
} else {
|
||||
millis = -1u;
|
||||
handles[n++] = __proc.onbirth;
|
||||
}
|
||||
i = WaitForMultipleObjects(n, handles, false, millis);
|
||||
if (i == -1u) {
|
||||
STRACE("proc wait panic %d", GetLastError());
|
||||
_Exit(157);
|
||||
}
|
||||
if (i & kNtWaitAbandoned) {
|
||||
i &= ~kNtWaitAbandoned;
|
||||
STRACE("proc %u handle %ld abandoned", i, handles[i]);
|
||||
}
|
||||
__proc_lock();
|
||||
|
||||
// release our waiter status
|
||||
for (int j = 0; j < n; ++j) {
|
||||
if (handles[j] == __proc.onbirth) continue;
|
||||
if (j == i) continue;
|
||||
if (!--objects[j]->waiters && objects[j]->status == PROC_UNDEAD) {
|
||||
__proc_free(objects[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// check if we need to churn due to >64 processes
|
||||
if (i == kNtWaitTimeout) {
|
||||
__proc_unlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
// churn on new process birth
|
||||
if (handles[i] == __proc.onbirth) {
|
||||
__proc_unlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
// handle process status change
|
||||
int sic = 0;
|
||||
--objects[i]->waiters;
|
||||
switch (objects[i]->status) {
|
||||
case PROC_ALIVE:
|
||||
sic = __proc_harvest(objects[i], false);
|
||||
break;
|
||||
case PROC_ZOMBIE:
|
||||
break;
|
||||
case PROC_UNDEAD:
|
||||
if (!objects[i]->waiters) {
|
||||
__proc_free(objects[i]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
__proc_unlock();
|
||||
|
||||
// don't raise SIGCHLD if
|
||||
// 1. wait4() is being used
|
||||
// 2. SIGCHLD has SIG_IGN handler
|
||||
// 3. SIGCHLD has SA_NOCLDWAIT flag
|
||||
if (sic) {
|
||||
__sig_generate(SIGCHLD, sic);
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +230,8 @@ static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
|
|||
* Lazy initializes process tracker data structures and worker.
|
||||
*/
|
||||
static textwindows void __proc_setup(void) {
|
||||
__proc.onstart = CreateSemaphore(0, 0, 1, 0);
|
||||
__proc.onbirth = CreateEvent(0, 0, 0, 0); // auto reset
|
||||
__proc.haszombies = CreateEvent(0, 1, 0, 0); // manual reset
|
||||
__proc.thread = CreateThread(0, 65536, __proc_worker, 0,
|
||||
kNtStackSizeParamIsAReservation, 0);
|
||||
}
|
||||
|
@ -191,7 +264,7 @@ textwindows void __proc_wipe(void) {
|
|||
* The returned memory is not tracked by any list. It must be filled in
|
||||
* with system process information and then added back to the system by
|
||||
* calling __proc_add(). If process creation fails, then it needs to be
|
||||
* released using __proc_free().
|
||||
* added back to the __proc.free list by caller.
|
||||
*/
|
||||
textwindows struct Proc *__proc_new(void) {
|
||||
struct Dll *e;
|
||||
|
@ -230,16 +303,13 @@ IGNORE_LEAKS(__proc_new)
|
|||
*/
|
||||
textwindows void __proc_add(struct Proc *proc) {
|
||||
dll_make_first(&__proc.list, &proc->elem);
|
||||
ReleaseSemaphore(__proc.onstart, 1, 0);
|
||||
SetEvent(__proc.onbirth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees process allocation.
|
||||
*
|
||||
* Process must not be currently tracked in the active or zombies list.
|
||||
*/
|
||||
textwindows void __proc_free(struct Proc *proc) {
|
||||
dll_make_first(&__proc.free, &proc->elem);
|
||||
textwindows void __proc_free(struct Proc *pr) {
|
||||
dll_remove(&__proc.undead, &pr->elem);
|
||||
dll_make_first(&__proc.free, &pr->elem);
|
||||
CloseHandle(pr->handle);
|
||||
}
|
||||
|
||||
// returns owned handle of direct child process
|
||||
|
|
|
@ -2,25 +2,27 @@
|
|||
#define COSMOPOLITAN_LIBC_PROC_H_
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "third_party/nsync/cv.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define PROC_ALIVE 0
|
||||
#define PROC_ZOMBIE 1
|
||||
#define PROC_UNDEAD 2
|
||||
|
||||
#define PROC_CONTAINER(e) DLL_CONTAINER(struct Proc, elem, e)
|
||||
|
||||
struct Proc {
|
||||
int pid;
|
||||
int status;
|
||||
int waiters;
|
||||
bool iszombie;
|
||||
bool wasforked;
|
||||
uint32_t wstatus;
|
||||
int64_t handle;
|
||||
struct Dll elem;
|
||||
nsync_cv onexit;
|
||||
struct rusage ru;
|
||||
};
|
||||
|
||||
|
@ -28,15 +30,15 @@ struct Procs {
|
|||
int waiters;
|
||||
atomic_uint once;
|
||||
nsync_mu lock;
|
||||
nsync_cv onexit;
|
||||
intptr_t thread;
|
||||
intptr_t onstart;
|
||||
intptr_t onbirth;
|
||||
intptr_t haszombies;
|
||||
struct Dll *list;
|
||||
struct Dll *free;
|
||||
struct Dll *undead;
|
||||
struct Dll *zombies;
|
||||
struct Proc pool[8];
|
||||
unsigned allocated;
|
||||
struct CosmoTib tls;
|
||||
struct rusage ruchlds;
|
||||
};
|
||||
|
||||
|
@ -50,6 +52,7 @@ int64_t __proc_search(int);
|
|||
struct Proc *__proc_new(void);
|
||||
void __proc_add(struct Proc *);
|
||||
void __proc_free(struct Proc *);
|
||||
int __proc_harvest(struct Proc *, bool);
|
||||
int sys_wait4_nt(int, int *, int, struct rusage *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
|
@ -35,8 +35,7 @@ LIBC_PROC_A_DIRECTDEPS = \
|
|||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
THIRD_PARTY_NSYNC \
|
||||
THIRD_PARTY_NSYNC_MEM
|
||||
THIRD_PARTY_NSYNC
|
||||
|
||||
LIBC_PROC_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(LIBC_PROC_A_DIRECTDEPS),$($(x))))
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sched_param.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/sysv/consts/ioprio.h"
|
||||
#include "libc/sysv/consts/prio.h"
|
||||
#include "libc/sysv/consts/sched.h"
|
||||
|
@ -31,7 +32,7 @@
|
|||
*/
|
||||
int verynice(void) {
|
||||
int e = errno;
|
||||
setpriority(PRIO_PROCESS, 0, 10);
|
||||
setpriority(PRIO_PROCESS, 0, NZERO);
|
||||
sys_ioprio_set(IOPRIO_WHO_PROCESS, 0,
|
||||
IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
|
||||
struct sched_param param = {sched_get_priority_min(SCHED_IDLE)};
|
||||
|
|
|
@ -16,41 +16,24 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/wintime.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/events.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/filetime.h"
|
||||
#include "libc/nt/struct/processmemorycounters.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/w.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
static textwindows struct timespec GetNextDeadline(struct timespec deadline) {
|
||||
if (timespec_iszero(deadline)) deadline = timespec_real();
|
||||
struct timespec delay = timespec_frommillis(__SIG_PROC_INTERVAL_MS);
|
||||
return timespec_add(deadline, delay);
|
||||
}
|
||||
|
||||
static textwindows int ReapZombie(struct Proc *pr, int *wstatus,
|
||||
static textwindows int __proc_reap(struct Proc *pr, int *wstatus,
|
||||
struct rusage *opt_out_rusage) {
|
||||
if (wstatus) {
|
||||
*wstatus = pr->wstatus;
|
||||
|
@ -58,97 +41,166 @@ static textwindows int ReapZombie(struct Proc *pr, int *wstatus,
|
|||
if (opt_out_rusage) {
|
||||
*opt_out_rusage = pr->ru;
|
||||
}
|
||||
if (!pr->waiters) {
|
||||
dll_remove(&__proc.zombies, &pr->elem);
|
||||
if (dll_is_empty(__proc.zombies)) {
|
||||
ResetEvent(__proc.haszombies);
|
||||
}
|
||||
if (pr->waiters) {
|
||||
pr->status = PROC_UNDEAD;
|
||||
dll_make_first(&__proc.undead, &pr->elem);
|
||||
} else {
|
||||
dll_make_first(&__proc.free, &pr->elem);
|
||||
CloseHandle(pr->handle);
|
||||
}
|
||||
return pr->pid;
|
||||
}
|
||||
|
||||
static textwindows int CheckZombies(int pid, int *wstatus,
|
||||
static textwindows int __proc_check(int pid, int *wstatus,
|
||||
struct rusage *opt_out_rusage) {
|
||||
struct Dll *e;
|
||||
for (e = dll_first(__proc.zombies); e; e = dll_next(__proc.zombies, e)) {
|
||||
struct Proc *pr = PROC_CONTAINER(e);
|
||||
if (pid == -1 && pr->waiters) {
|
||||
continue; // this zombie has been claimed
|
||||
}
|
||||
if (pid == -1 || pid == pr->pid) {
|
||||
return ReapZombie(pr, wstatus, opt_out_rusage);
|
||||
return __proc_reap(pr, wstatus, opt_out_rusage);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static textwindows void UnwindWaiterCount(void *arg) {
|
||||
int *waiters = arg;
|
||||
--*waiters;
|
||||
static textwindows int __proc_wait(int pid, int *wstatus, int options,
|
||||
struct rusage *rusage, sigset_t waitmask) {
|
||||
for (;;) {
|
||||
|
||||
// check for signals and cancelation
|
||||
int sig, handler_was_called;
|
||||
if (_check_cancel() == -1) {
|
||||
return -1;
|
||||
}
|
||||
if ((sig = __sig_get(waitmask))) {
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) {
|
||||
return -1; // ECANCELED because SIGTHR was just handled
|
||||
}
|
||||
if (handler_was_called & SIG_HANDLED_NO_RESTART) {
|
||||
return eintr(); // a non-SA_RESTART handler was called
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows int WaitForProcess(int pid, int *wstatus, int options,
|
||||
struct rusage *rusage,
|
||||
uint64_t waitmask) {
|
||||
uint64_t m;
|
||||
int rc, *wv;
|
||||
nsync_cv *cv;
|
||||
struct Dll *e;
|
||||
struct Proc *pr;
|
||||
struct timespec deadline = timespec_zero;
|
||||
|
||||
// check list of processes that've already exited
|
||||
if ((rc = CheckZombies(pid, wstatus, rusage))) {
|
||||
// check for zombie to harvest
|
||||
__proc_lock();
|
||||
CheckForZombies:
|
||||
int rc = __proc_check(pid, wstatus, rusage);
|
||||
if (rc || (options & WNOHANG)) {
|
||||
__proc_unlock();
|
||||
return rc;
|
||||
}
|
||||
|
||||
// find the mark
|
||||
pr = 0;
|
||||
if (pid == -1) {
|
||||
// there's no zombies left
|
||||
// check if there's any living processes
|
||||
if (dll_is_empty(__proc.list)) {
|
||||
__proc_unlock();
|
||||
return echild();
|
||||
}
|
||||
cv = &__proc.onexit;
|
||||
wv = &__proc.waiters;
|
||||
|
||||
// get appropriate wait object
|
||||
// register ourself as waiting
|
||||
struct Proc *pr = 0;
|
||||
uintptr_t hWaitObject;
|
||||
if (pid == -1) {
|
||||
// wait for any status change
|
||||
hWaitObject = __proc.haszombies;
|
||||
++__proc.waiters;
|
||||
} else {
|
||||
for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) {
|
||||
if (pid == PROC_CONTAINER(e)->pid) {
|
||||
// wait on specific child
|
||||
for (struct Dll *e = dll_first(__proc.list); e;
|
||||
e = dll_next(__proc.list, e)) {
|
||||
pr = PROC_CONTAINER(e);
|
||||
}
|
||||
if (pid == pr->pid) break;
|
||||
}
|
||||
if (pr) {
|
||||
unassert(!pr->iszombie);
|
||||
cv = &pr->onexit;
|
||||
wv = &pr->waiters;
|
||||
// by making the waiter count non-zero, the proc daemon stops
|
||||
// being obligated to monitor this process. this means we may
|
||||
// need to assume responsibility later on for zombifying this
|
||||
++pr->waiters;
|
||||
hWaitObject = pr->handle;
|
||||
} else {
|
||||
__proc_unlock();
|
||||
return echild();
|
||||
}
|
||||
}
|
||||
__proc_unlock();
|
||||
|
||||
// perform blocking operation
|
||||
uint32_t wi;
|
||||
uintptr_t sem;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->pt_blkmask = waitmask;
|
||||
pt->pt_semaphore = sem = CreateSemaphore(0, 0, 1, 0);
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM,
|
||||
memory_order_release);
|
||||
wi = WaitForMultipleObjects(2, (intptr_t[2]){hWaitObject, sem}, 0, -1u);
|
||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||
CloseHandle(sem);
|
||||
|
||||
// log warning if handle unexpectedly closed
|
||||
if (wi & kNtWaitAbandoned) {
|
||||
wi &= ~kNtWaitAbandoned;
|
||||
STRACE("wait4 abandoned %u", wi);
|
||||
}
|
||||
|
||||
// check for wait() style wakeup
|
||||
__proc_lock();
|
||||
if (!wi && !pr) {
|
||||
--__proc.waiters;
|
||||
goto CheckForZombies;
|
||||
}
|
||||
|
||||
// check if killed or win32 error
|
||||
if (wi) {
|
||||
if (pr && --pr->waiters && pr->status == PROC_UNDEAD) {
|
||||
__proc_free(pr);
|
||||
}
|
||||
__proc_unlock();
|
||||
if (wi == 1) {
|
||||
// __sig_cancel() woke our semaphore
|
||||
continue;
|
||||
} else {
|
||||
// neither posix or win32 define i/o error conditions for
|
||||
// generic wait. failure should only be due to api misuse
|
||||
return einval();
|
||||
}
|
||||
}
|
||||
|
||||
// wait for status change
|
||||
if (options & WNOHANG) return 0;
|
||||
WaitMore:
|
||||
deadline = GetNextDeadline(deadline);
|
||||
SpuriousWakeup:
|
||||
++*wv;
|
||||
pthread_cleanup_push(UnwindWaiterCount, wv);
|
||||
m = __sig_begin(waitmask);
|
||||
if ((rc = _check_signal(true)) != -1) {
|
||||
rc = nsync_cv_wait_with_deadline(cv, &__proc.lock, deadline, 0);
|
||||
// handle process exit notification
|
||||
--pr->waiters;
|
||||
if (pr->status == PROC_ALIVE) {
|
||||
__proc_harvest(pr, true);
|
||||
}
|
||||
switch (pr->status) {
|
||||
case PROC_ALIVE:
|
||||
// exit caused by execve() reparenting
|
||||
__proc_unlock();
|
||||
break;
|
||||
case PROC_ZOMBIE:
|
||||
// exit happened and we're the first to know
|
||||
rc = __proc_reap(pr, wstatus, rusage);
|
||||
__proc_unlock();
|
||||
return rc;
|
||||
case PROC_UNDEAD:
|
||||
// exit happened but another thread waited first
|
||||
if (!pr->waiters) {
|
||||
__proc_free(pr);
|
||||
}
|
||||
__proc_unlock();
|
||||
return echild();
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
__sig_finish(m);
|
||||
pthread_cleanup_pop(true);
|
||||
if (rc == -1) return -1;
|
||||
if (rc == ETIMEDOUT) goto WaitMore;
|
||||
if (rc == ECANCELED) return ecanceled();
|
||||
if (pr && pr->iszombie) return ReapZombie(pr, wstatus, rusage);
|
||||
unassert(!rc); // i have to follow my dreams however crazy they seem
|
||||
if (!pr && (rc = CheckZombies(pid, wstatus, rusage))) return rc;
|
||||
goto SpuriousWakeup;
|
||||
}
|
||||
|
||||
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
||||
struct rusage *opt_out_rusage) {
|
||||
int rc;
|
||||
uint64_t m;
|
||||
// no support for WCONTINUED and WUNTRACED yet
|
||||
if (options & ~WNOHANG) return einval();
|
||||
// XXX: NT doesn't really have process groups. For instance the
|
||||
|
@ -156,12 +208,9 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
|||
// just does an "ignore ctrl-c" internally.
|
||||
if (pid == 0) pid = -1;
|
||||
if (pid < -1) pid = -pid;
|
||||
m = __sig_block();
|
||||
__proc_lock();
|
||||
pthread_cleanup_push((void *)__proc_unlock, 0);
|
||||
rc = WaitForProcess(pid, opt_out_wstatus, options, opt_out_rusage,
|
||||
sigset_t m = __sig_block();
|
||||
int rc = __proc_wait(pid, opt_out_wstatus, options, opt_out_rusage,
|
||||
m | 1ull << (SIGCHLD - 1));
|
||||
pthread_cleanup_pop(true);
|
||||
__sig_unblock(m);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "libc/intrin/asancodes.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/getenv.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/files.h"
|
||||
|
@ -48,6 +49,18 @@ extern unsigned char __tls_add_nt_rax[];
|
|||
|
||||
_Alignas(TLS_ALIGNMENT) static char __static_tls[6016];
|
||||
|
||||
static unsigned long ParseMask(const char *str) {
|
||||
int c;
|
||||
unsigned long x = 0;
|
||||
if (str) {
|
||||
while ((c = *str++)) {
|
||||
x *= 10;
|
||||
x += c - '0';
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables thread local storage for main process.
|
||||
*
|
||||
|
@ -210,6 +223,13 @@ textstartup void __enable_tls(void) {
|
|||
atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed);
|
||||
// TODO(jart): set_tid_address?
|
||||
|
||||
// inherit signal mask
|
||||
if (IsWindows()) {
|
||||
atomic_store_explicit(&tib->tib_sigmask,
|
||||
ParseMask(__getenv(environ, "_MASK").s),
|
||||
memory_order_relaxed);
|
||||
}
|
||||
|
||||
// initialize posix threads
|
||||
_pthread_static.tib = tib;
|
||||
_pthread_static.pt_flags = PT_STATIC;
|
||||
|
|
|
@ -109,7 +109,7 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr,
|
|||
if ((resources.handle = WSASocket(f->family, f->type, f->protocol, 0, 0,
|
||||
kNtWsaFlagOverlapped)) == -1) {
|
||||
client = __winsockerr();
|
||||
goto WeFailed;
|
||||
goto Finish;
|
||||
}
|
||||
|
||||
// accept network connection
|
||||
|
@ -118,7 +118,10 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr,
|
|||
ssize_t bytes_received = __winsock_block(
|
||||
resources.handle, 0, !!(f->flags & O_NONBLOCK), f->rcvtimeo, m,
|
||||
sys_accept_nt_start, &(struct AcceptArgs){f->handle, &buffer});
|
||||
if (bytes_received == -1) goto WeFailed;
|
||||
if (bytes_received == -1) {
|
||||
__imp_closesocket(resources.handle);
|
||||
goto Finish;
|
||||
}
|
||||
|
||||
// create file descriptor for new socket
|
||||
// don't inherit the file open mode bits
|
||||
|
@ -138,7 +141,7 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr,
|
|||
memcpy(addr, &buffer.remote.addr, sizeof(*addr));
|
||||
g_fds.p[client].kind = kFdSocket;
|
||||
|
||||
WeFailed:
|
||||
Finish:
|
||||
pthread_cleanup_pop(false);
|
||||
__sig_unblock(m);
|
||||
if (client == -1 && errno == ECONNRESET) {
|
||||
|
|
|
@ -25,8 +25,14 @@
|
|||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define _MSG_OOB 1
|
||||
#define _MSG_PEEK 2
|
||||
#define _MSG_WAITALL 8
|
||||
#define _MSG_DONTWAIT 64
|
||||
|
||||
struct RecvArgs {
|
||||
const struct iovec *iov;
|
||||
size_t iovlen;
|
||||
|
@ -44,11 +50,17 @@ static textwindows int sys_recv_nt_start(int64_t handle,
|
|||
|
||||
textwindows ssize_t sys_recv_nt(int fd, const struct iovec *iov, size_t iovlen,
|
||||
uint32_t flags) {
|
||||
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_PEEK | _MSG_WAITALL)) {
|
||||
return einval();
|
||||
}
|
||||
ssize_t rc;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
sigset_t m = __sig_block();
|
||||
rc = __winsock_block(f->handle, flags, !!(f->flags & O_NONBLOCK), f->rcvtimeo,
|
||||
m, sys_recv_nt_start, &(struct RecvArgs){iov, iovlen});
|
||||
bool nonblock = !(flags & _MSG_WAITALL) &&
|
||||
((f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT));
|
||||
flags &= ~_MSG_DONTWAIT;
|
||||
rc = __winsock_block(f->handle, flags, nonblock, f->rcvtimeo, m,
|
||||
sys_recv_nt_start, &(struct RecvArgs){iov, iovlen});
|
||||
__sig_unblock(m);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
* @param fd is the file descriptor returned by socket()
|
||||
* @param buf is where received network data gets copied
|
||||
* @param size is the byte capacity of buf
|
||||
* @param flags can have MSG_{WAITALL,PEEK,OOB}, etc.
|
||||
* @param flags can have `MSG_OOB`, `MSG_PEEK`, `MSG_DONTWAIT`, `MSG_WAITALL`
|
||||
* @return number of bytes received, 0 on remote close, or -1 w/ errno
|
||||
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
||||
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
||||
|
|
|
@ -24,9 +24,15 @@
|
|||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/consts/msg.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define _MSG_OOB 1
|
||||
#define _MSG_PEEK 2
|
||||
#define _MSG_DONTWAIT 64
|
||||
|
||||
struct RecvFromArgs {
|
||||
const struct iovec *iov;
|
||||
size_t iovlen;
|
||||
|
@ -48,11 +54,14 @@ textwindows ssize_t sys_recvfrom_nt(int fd, const struct iovec *iov,
|
|||
size_t iovlen, uint32_t flags,
|
||||
void *opt_out_srcaddr,
|
||||
uint32_t *opt_inout_srcaddrsize) {
|
||||
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_PEEK)) return einval();
|
||||
ssize_t rc;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
sigset_t m = __sig_block();
|
||||
rc = __winsock_block(f->handle, flags, !!(f->flags & O_NONBLOCK), f->rcvtimeo,
|
||||
m, sys_recvfrom_nt_start,
|
||||
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
|
||||
flags &= ~_MSG_DONTWAIT;
|
||||
rc = __winsock_block(f->handle, flags, nonblock, f->rcvtimeo, m,
|
||||
sys_recvfrom_nt_start,
|
||||
&(struct RecvFromArgs){iov, iovlen, opt_out_srcaddr,
|
||||
opt_inout_srcaddrsize});
|
||||
__sig_unblock(m);
|
||||
|
|
|
@ -34,15 +34,15 @@
|
|||
/**
|
||||
* Receives data from network.
|
||||
*
|
||||
* This function blocks unless MSG_DONTWAIT is passed. In that case, the
|
||||
* non-error EWOULDBLOCK might be returned. It basically means we didn't
|
||||
* wait around to learn an amount of bytes were written that we know in
|
||||
* advance are guaranteed to be atomic.
|
||||
*
|
||||
* @param fd is the file descriptor returned by socket()
|
||||
* @param buf is where received network data gets copied
|
||||
* @param size is the byte capacity of buf
|
||||
* @param flags is a bitmask which may contain any of the following:
|
||||
* - `MSG_DONTWAIT` to force `O_NONBLOCK` behavior for this call
|
||||
* - `MSG_OOB` is broadly supported (untested by cosmo)
|
||||
* - `MSG_PEEK` is broadly supported (untested by cosmo)
|
||||
* - `MSG_WAITALL` is broadly supported (untested by cosmo)
|
||||
* - `MSG_DONTROUTE` is broadly supported (untested by cosmo)
|
||||
* @param flags can have `MSG_OOB`, `MSG_PEEK`, and `MSG_DONTWAIT`
|
||||
* @param opt_out_srcaddr receives the binary ip:port of the data's origin
|
||||
* @param opt_inout_srcaddrsize is srcaddr capacity which gets updated
|
||||
* @return number of bytes received, 0 on remote close, or -1 w/ errno
|
||||
|
|
|
@ -25,8 +25,13 @@
|
|||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define _MSG_OOB 1
|
||||
#define _MSG_DONTROUTE 4
|
||||
#define _MSG_DONTWAIT 64
|
||||
|
||||
struct SendArgs {
|
||||
const struct iovec *iov;
|
||||
size_t iovlen;
|
||||
|
@ -44,11 +49,14 @@ static textwindows int sys_send_nt_start(int64_t handle,
|
|||
|
||||
textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen,
|
||||
uint32_t flags) {
|
||||
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE)) return einval();
|
||||
ssize_t rc;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
sigset_t m = __sig_block();
|
||||
rc = __winsock_block(f->handle, flags, !!(f->flags & O_NONBLOCK), f->sndtimeo,
|
||||
m, sys_send_nt_start, &(struct SendArgs){iov, iovlen});
|
||||
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
|
||||
flags &= ~_MSG_DONTWAIT;
|
||||
rc = __winsock_block(f->handle, flags, nonblock, f->sndtimeo, m,
|
||||
sys_send_nt_start, &(struct SendArgs){iov, iovlen});
|
||||
__sig_unblock(m);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
* @param fd is the file descriptor returned by socket()
|
||||
* @param buf is the data to send, which we'll copy if necessary
|
||||
* @param size is the byte-length of buf
|
||||
* @param flags MSG_OOB, MSG_DONTROUTE, MSG_PARTIAL, MSG_NOSIGNAL, etc.
|
||||
* @param flags can have `MSG_OOB`, `MSG_DONTROUTE`, and `MSG_DONTWAIT`
|
||||
* @return number of bytes transmitted, or -1 w/ errno
|
||||
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
||||
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
||||
|
|
|
@ -25,8 +25,13 @@
|
|||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define _MSG_OOB 1
|
||||
#define _MSG_DONTROUTE 4
|
||||
#define _MSG_DONTWAIT 64
|
||||
|
||||
struct SendToArgs {
|
||||
const struct iovec *iov;
|
||||
size_t iovlen;
|
||||
|
@ -47,12 +52,14 @@ static textwindows int sys_sendto_nt_start(int64_t handle,
|
|||
textwindows ssize_t sys_sendto_nt(int fd, const struct iovec *iov,
|
||||
size_t iovlen, uint32_t flags,
|
||||
void *opt_in_addr, uint32_t in_addrsize) {
|
||||
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE)) return einval();
|
||||
ssize_t rc;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
sigset_t m = __sig_block();
|
||||
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
|
||||
flags &= ~_MSG_DONTWAIT;
|
||||
rc = __winsock_block(
|
||||
f->handle, flags, !!(f->flags & O_NONBLOCK), f->sndtimeo, m,
|
||||
sys_sendto_nt_start,
|
||||
f->handle, flags, nonblock, f->sndtimeo, m, sys_sendto_nt_start,
|
||||
&(struct SendToArgs){iov, iovlen, opt_in_addr, in_addrsize});
|
||||
__sig_unblock(m);
|
||||
return rc;
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
* @param fd is the file descriptor returned by socket()
|
||||
* @param buf is the data to send, which we'll copy if necessary
|
||||
* @param size is the byte-length of buf
|
||||
* @param flags MSG_OOB, MSG_DONTROUTE, MSG_PARTIAL, MSG_NOSIGNAL, etc.
|
||||
* @param flags can have `MSG_OOB`, `MSG_DONTROUTE`, and `MSG_DONTWAIT`
|
||||
* @param opt_addr is a binary ip:port destination override, which is
|
||||
* mandatory for UDP if connect() wasn't called
|
||||
* @param addrsize is the byte-length of addr's true polymorphic form
|
||||
|
|
|
@ -16,50 +16,21 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/iovec.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
struct WinsockBlockResources {
|
||||
int64_t handle;
|
||||
struct NtOverlapped *overlap;
|
||||
};
|
||||
|
||||
static void UnwindWinsockBlock(void *arg) {
|
||||
struct WinsockBlockResources *wbr = arg;
|
||||
uint32_t got, flags;
|
||||
CancelIoEx(wbr->handle, wbr->overlap);
|
||||
WSAGetOverlappedResult(wbr->handle, wbr->overlap, &got, true, &flags);
|
||||
WSACloseEvent(wbr->overlap->hEvent);
|
||||
}
|
||||
|
||||
static void CancelWinsockBlock(int64_t handle, struct NtOverlapped *overlap) {
|
||||
if (!CancelIoEx(handle, overlap)) {
|
||||
unassert(WSAGetLastError() == kNtErrorNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
textwindows ssize_t
|
||||
__winsock_block(int64_t handle, uint32_t flags, bool nonblock,
|
||||
uint32_t srwtimeout, sigset_t waitmask,
|
||||
|
@ -67,31 +38,20 @@ __winsock_block(int64_t handle, uint32_t flags, bool nonblock,
|
|||
uint32_t *flags, void *arg),
|
||||
void *arg) {
|
||||
|
||||
int rc;
|
||||
int sig = 0;
|
||||
uint32_t status;
|
||||
uint32_t exchanged;
|
||||
int olderror = errno;
|
||||
bool eagained = false;
|
||||
bool canceled = false;
|
||||
int handler_was_called;
|
||||
struct PosixThread *pt;
|
||||
|
||||
RestartOperation:
|
||||
int rc, sig, reason = 0;
|
||||
uint32_t status, exchanged;
|
||||
if (_check_cancel() == -1) return -1; // ECANCELED
|
||||
if ((sig = __sig_get(waitmask))) goto HandleInterrupt;
|
||||
|
||||
struct NtOverlapped overlap = {.hEvent = WSACreateEvent()};
|
||||
struct WinsockBlockResources wbr = {handle, &overlap};
|
||||
pthread_cleanup_push(UnwindWinsockBlock, &wbr);
|
||||
rc = StartSocketOp(handle, &overlap, &flags, arg);
|
||||
if (rc && WSAGetLastError() == kNtErrorIoPending) {
|
||||
if (nonblock) {
|
||||
CancelWinsockBlock(handle, &overlap);
|
||||
eagained = true;
|
||||
} else if (_check_cancel()) {
|
||||
CancelWinsockBlock(handle, &overlap);
|
||||
canceled = true;
|
||||
} else if ((sig = __sig_get(waitmask))) {
|
||||
CancelWinsockBlock(handle, &overlap);
|
||||
CancelIoEx(handle, &overlap);
|
||||
reason = EAGAIN;
|
||||
} else {
|
||||
struct PosixThread *pt;
|
||||
pt = _pthread_self();
|
||||
pt->pt_blkmask = waitmask;
|
||||
pt->pt_iohandle = handle;
|
||||
|
@ -101,10 +61,13 @@ RestartOperation:
|
|||
status = WSAWaitForMultipleEvents(1, &overlap.hEvent, 0,
|
||||
srwtimeout ? srwtimeout : -1u, 0);
|
||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||
if (status) {
|
||||
if (status == kNtWaitTimeout) {
|
||||
// SO_RCVTIMEO or SO_SNDTIMEO elapsed
|
||||
CancelWinsockBlock(handle, &overlap);
|
||||
eagained = true;
|
||||
reason = EAGAIN; // SO_RCVTIMEO or SO_SNDTIMEO elapsed
|
||||
} else {
|
||||
reason = WSAGetLastError(); // ENETDOWN or ENOBUFS
|
||||
}
|
||||
CancelIoEx(handle, &overlap);
|
||||
}
|
||||
}
|
||||
rc = 0;
|
||||
|
@ -114,30 +77,21 @@ RestartOperation:
|
|||
? 0
|
||||
: -1;
|
||||
}
|
||||
pthread_cleanup_pop(false);
|
||||
WSACloseEvent(overlap.hEvent);
|
||||
|
||||
if (canceled) {
|
||||
return ecanceled();
|
||||
}
|
||||
if (sig) {
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
} else {
|
||||
handler_was_called = 0;
|
||||
}
|
||||
if (!rc) {
|
||||
errno = olderror;
|
||||
return exchanged;
|
||||
}
|
||||
if (WSAGetLastError() == kNtErrorOperationAborted) {
|
||||
if (eagained) return eagain();
|
||||
if (!handler_was_called && (sig = __sig_get(waitmask))) {
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (reason) {
|
||||
errno = reason;
|
||||
return -1;
|
||||
}
|
||||
if (handler_was_called != 1) {
|
||||
goto RestartOperation;
|
||||
if ((sig = __sig_get(waitmask))) {
|
||||
HandleInterrupt:
|
||||
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (handler_was_called != 1) goto RestartOperation;
|
||||
}
|
||||
return eintr();
|
||||
}
|
||||
|
|
|
@ -309,9 +309,9 @@ textstartup void __printargs(const char *prologue) {
|
|||
PRINT("");
|
||||
PRINT("SIGNAL MASK %#lx", ss);
|
||||
if (ss) {
|
||||
for (i = 0; i < 32; ++i) {
|
||||
if (ss & (1u << i)) {
|
||||
PRINT(" ☼ %G (%d) is masked", i + 1, i + 1);
|
||||
for (i = 1; i <= NSIG; ++i) {
|
||||
if (ss & (1ull << (i - 1))) {
|
||||
PRINT(" ☼ %G (%d) is masked", i, i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -56,17 +56,14 @@ int iswseparator(wint_t);
|
|||
wint_t towlower(wint_t);
|
||||
wint_t towupper(wint_t);
|
||||
|
||||
void bzero(void *, size_t) memcpyesque;
|
||||
void *memset(void *, int, size_t) memcpyesque;
|
||||
void *memmove(void *, const void *, size_t) memcpyesque;
|
||||
void *memcpy(void *restrict, const void *restrict, size_t) memcpyesque;
|
||||
void *mempcpy(void *restrict, const void *restrict, size_t) memcpyesque;
|
||||
char *hexpcpy(char *restrict, const void *restrict, size_t) memcpyesque;
|
||||
void *memccpy(void *restrict, const void *restrict, int, size_t) memcpyesque;
|
||||
void bcopy(const void *, void *, size_t) memcpyesque;
|
||||
void explicit_bzero(void *, size_t);
|
||||
|
||||
int bcmp(const void *, const void *, size_t) strlenesque;
|
||||
int memcmp(const void *, const void *, size_t) strlenesque;
|
||||
int timingsafe_bcmp(const void *, const void *, size_t);
|
||||
int timingsafe_memcmp(const void *, const void *, size_t);
|
||||
|
@ -75,7 +72,6 @@ size_t strlen(const char *) strlenesque;
|
|||
size_t strnlen(const char *, size_t) strlenesque;
|
||||
size_t strnlen_s(const char *, size_t);
|
||||
char *strchr(const char *, int) strlenesque;
|
||||
char *index(const char *, int) strlenesque;
|
||||
void *memchr(const void *, int, size_t) strlenesque;
|
||||
char *strchrnul(const char *, int) strlenesque returnsnonnull;
|
||||
void *rawmemchr(const void *, int) strlenesque returnsnonnull;
|
||||
|
@ -220,6 +216,17 @@ bool wcsendswith(const wchar_t *, const wchar_t *) strlenesque;
|
|||
char *__join_paths(char *, size_t, const char *, const char *) __wur;
|
||||
#endif /* _COSMO_SOURCE */
|
||||
|
||||
#if defined(_COSMO_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) || \
|
||||
defined(_POSIX_SOURCE) || \
|
||||
(defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE + 0 < 200809L) || \
|
||||
(defined(_XOPEN_SOURCE) && _XOPEN_SOURCE + 0 < 700)
|
||||
int bcmp(const void *, const void *, size_t) strlenesque;
|
||||
void bcopy(const void *, void *, size_t) memcpyesque;
|
||||
void bzero(void *, size_t) memcpyesque;
|
||||
char *index(const char *, int) strlenesque;
|
||||
char *rindex(const char *, int) strlenesque;
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_STR_STR_H_ */
|
||||
|
|
|
@ -115,7 +115,12 @@ static int PosixThread(void *arg, int tid) {
|
|||
}
|
||||
// set long jump handler so pthread_exit can bring control back here
|
||||
if (!setjmp(pt->pt_exiter)) {
|
||||
pthread_sigmask(SIG_SETMASK, &pt->pt_attr.__sigmask, 0);
|
||||
if (IsWindows()) {
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, pt->pt_attr.__sigmask,
|
||||
memory_order_release);
|
||||
} else {
|
||||
sys_sigprocmask(SIG_SETMASK, &pt->pt_attr.__sigmask, 0);
|
||||
}
|
||||
rc = pt->pt_start(pt->pt_arg);
|
||||
// ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup
|
||||
unassert(!pt->pt_cleanup);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/sysv/consts/rlimit.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
@ -58,8 +59,10 @@ void SetUp(void) {
|
|||
// tune down the main process's stack size to a reasonable amount
|
||||
// some operating systems, e.g. freebsd, will do things like have
|
||||
// 500mb RLIMIT_STACK by default, even on machines with 400mb RAM
|
||||
struct rlimit rl = {2 * 1024 * 1024, 2 * 1024 * 1024};
|
||||
if (!IsWindows() && !IsXnu()) {
|
||||
struct rlimit rl;
|
||||
getrlimit(RLIMIT_STACK, &rl);
|
||||
rl.rlim_cur = MIN(rl.rlim_cur, 2 * 1024 * 1024);
|
||||
ASSERT_SYS(0, 0, setrlimit(RLIMIT_STACK, &rl));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
/*-*- 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/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigaltstack.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
|
@ -29,10 +38,6 @@
|
|||
* rewrite thread cpu state to call pthread_exit
|
||||
* this method returns gracefully from signal handlers
|
||||
* unfortunately it relies on cpu architecture knowledge
|
||||
*
|
||||
* @see test/libc/thread/stackoverflow1_test.c
|
||||
* @see test/libc/thread/stackoverflow2_test.c
|
||||
* @see test/libc/thread/stackoverflow3_test.c
|
||||
*/
|
||||
|
||||
volatile bool smashed_stack;
|
||||
|
@ -47,11 +52,12 @@ void Exiter(void *rc) {
|
|||
void CrashHandler(int sig, siginfo_t *si, void *arg) {
|
||||
ucontext_t *ctx = arg;
|
||||
struct sigaltstack ss;
|
||||
ASSERT_FALSE(smashed_stack);
|
||||
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||
ASSERT_EQ(SS_ONSTACK, ss.ss_flags);
|
||||
kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr);
|
||||
smashed_stack = true;
|
||||
ASSERT_TRUE(__is_stack_overflow(si, ctx));
|
||||
// EXPECT_TRUE(__is_stack_overflow(si, ctx));
|
||||
//
|
||||
// the backtrace will look like this
|
||||
//
|
||||
|
@ -67,7 +73,15 @@ void CrashHandler(int sig, siginfo_t *si, void *arg) {
|
|||
ctx->uc_mcontext.PC = (long)Exiter;
|
||||
ctx->uc_mcontext.SP += 32768;
|
||||
ctx->uc_mcontext.SP &= -16;
|
||||
ctx->uc_mcontext.SP -= 8;
|
||||
#ifdef __x86_64__
|
||||
*(long *)(ctx->uc_mcontext.SP -= 8) = ctx->uc_mcontext.PC;
|
||||
#elif defined(__aarch64__)
|
||||
*(long *)(ctx->uc_mcontext.SP -= 8) = ctx->uc_mcontext.regs[30];
|
||||
*(long *)(ctx->uc_mcontext.SP -= 8) = ctx->uc_mcontext.regs[29];
|
||||
ctx->uc_mcontext.BP = ctx->uc_mcontext.SP;
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
int StackOverflow(int f(), int n) {
|
||||
|
@ -96,7 +110,7 @@ void *MyPosixThread(void *arg) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
TEST(stackoverflow, standardStack_altStack_thread_teleport) {
|
||||
void *res;
|
||||
pthread_t th;
|
||||
struct sigaltstack ss;
|
||||
|
@ -105,6 +119,7 @@ int main(int argc, char *argv[]) {
|
|||
pthread_join(th, &res);
|
||||
ASSERT_EQ((void *)123L, res);
|
||||
ASSERT_TRUE(smashed_stack);
|
||||
// this should be SS_DISABLE but ShowCrashReports() creates an alt stack
|
||||
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||
ASSERT_EQ(SS_DISABLE, ss.ss_flags);
|
||||
ASSERT_EQ(0, ss.ss_flags);
|
||||
}
|
86
test/libc/calls/stackoverflow4_test.c
Normal file
86
test/libc/calls/stackoverflow4_test.c
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*-*- 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/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigaltstack.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/ucontext.internal.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/ss.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* stack overflow recovery technique #4
|
||||
* just call pthread_exit() and let the thread die
|
||||
*/
|
||||
|
||||
volatile bool smashed_stack;
|
||||
|
||||
void CrashHandler(int sig) {
|
||||
smashed_stack = true;
|
||||
pthread_exit((void *)123L);
|
||||
}
|
||||
|
||||
int StackOverflow(int f(), int n) {
|
||||
if (n < INT_MAX) {
|
||||
return f(f, n + 1) - 1;
|
||||
} else {
|
||||
return INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
int (*pStackOverflow)(int (*)(), int) = StackOverflow;
|
||||
|
||||
void *MyPosixThread(void *arg) {
|
||||
struct sigaction sa;
|
||||
struct sigaltstack ss;
|
||||
ss.ss_flags = 0;
|
||||
ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 4096;
|
||||
ss.ss_sp = gc(malloc(ss.ss_size));
|
||||
ASSERT_SYS(0, 0, sigaltstack(&ss, 0));
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler = CrashHandler;
|
||||
sigaction(SIGBUS, &sa, 0);
|
||||
sigaction(SIGSEGV, &sa, 0);
|
||||
exit(pStackOverflow(pStackOverflow, 0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(stackoverflow, standardStack_altStack_thread_teleport) {
|
||||
void *res;
|
||||
pthread_t th;
|
||||
struct sigaltstack ss;
|
||||
smashed_stack = false;
|
||||
pthread_create(&th, 0, MyPosixThread, 0);
|
||||
pthread_join(th, &res);
|
||||
ASSERT_EQ((void *)123L, res);
|
||||
ASSERT_TRUE(smashed_stack);
|
||||
// this should be SS_DISABLE but ShowCrashReports() creates an alt stack
|
||||
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||
ASSERT_EQ(0, ss.ss_flags);
|
||||
}
|
|
@ -637,6 +637,8 @@ int Launch(void) {
|
|||
|
||||
posix_spawnattr_init(&spawnattr);
|
||||
posix_spawnattr_setsigmask(&spawnattr, &savemask);
|
||||
posix_spawnattr_setflags(&spawnattr,
|
||||
POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETRLIMIT);
|
||||
SetCpuLimit(cpuquota);
|
||||
SetFszLimit(fszquota);
|
||||
SetMemLimit(memquota);
|
||||
|
|
|
@ -552,7 +552,8 @@ RetryOnEtxtbsyRaceCondition:
|
|||
started = timespec_real();
|
||||
pipe2(client->pipe, O_CLOEXEC);
|
||||
posix_spawnattr_init(&spawnattr);
|
||||
posix_spawnattr_setflags(&spawnattr, POSIX_SPAWN_SETPGROUP);
|
||||
posix_spawnattr_setflags(&spawnattr,
|
||||
POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETPGROUP);
|
||||
posix_spawnattr_setsigmask(&spawnattr, &sigmask);
|
||||
posix_spawn_file_actions_init(&spawnfila);
|
||||
posix_spawn_file_actions_adddup2(&spawnfila, g_bogusfd, 0);
|
||||
|
|
Loading…
Reference in a new issue