mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-29 00:32:29 +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 {
|
} else {
|
||||||
rc = sys_getcwd_metal(buf, size);
|
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;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,8 @@ textwindows int ntspawn(
|
||||||
if (CreateProcess(sb->path, sb->cmdline, 0, 0, true,
|
if (CreateProcess(sb->path, sb->cmdline, 0, 0, true,
|
||||||
dwCreationFlags | kNtCreateUnicodeEnvironment |
|
dwCreationFlags | kNtCreateUnicodeEnvironment |
|
||||||
kNtExtendedStartupinfoPresent |
|
kNtExtendedStartupinfoPresent |
|
||||||
kNtInheritParentAffinity,
|
kNtInheritParentAffinity |
|
||||||
|
GetPriorityClass(GetCurrentProcess()),
|
||||||
sb->envblock, opt_lpCurrentDirectory,
|
sb->envblock, opt_lpCurrentDirectory,
|
||||||
&info.StartupInfo, opt_out_lpProcessInformation)) {
|
&info.StartupInfo, opt_out_lpProcessInformation)) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
|
|
@ -32,11 +32,7 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
|
||||||
bool restartable) {
|
bool restartable) {
|
||||||
int sig;
|
int sig;
|
||||||
if (_check_cancel() == -1) return -1;
|
if (_check_cancel() == -1) return -1;
|
||||||
if ((sig = __sig_get(waitmask))) {
|
if ((sig = __sig_get(waitmask))) goto 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();
|
|
||||||
}
|
|
||||||
int expect = 0;
|
int expect = 0;
|
||||||
atomic_int futex = 0;
|
atomic_int futex = 0;
|
||||||
struct PosixThread *pt = _pthread_self();
|
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);
|
bool32 ok = WaitOnAddress(&futex, &expect, sizeof(int), msdelay);
|
||||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||||
if (ok && (sig = __sig_get(waitmask))) {
|
if (ok && (sig = __sig_get(waitmask))) {
|
||||||
|
HandleSignal:
|
||||||
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||||
if (_check_cancel() == -1) return -1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,8 @@
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
|
#define POLL_INTERVAL_MS 10
|
||||||
|
|
||||||
// Polls on the New Technology.
|
// Polls on the New Technology.
|
||||||
//
|
//
|
||||||
// This function is used to implement poll() and select(). You may poll
|
// 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);
|
remain = timespec_sub(deadline, now);
|
||||||
millis = timespec_tomillis(remain);
|
millis = timespec_tomillis(remain);
|
||||||
waitfor = MIN(millis, 0xffffffffu);
|
waitfor = MIN(millis, 0xffffffffu);
|
||||||
waitfor = MIN(waitfor, __SIG_POLL_INTERVAL_MS);
|
waitfor = MIN(waitfor, POLL_INTERVAL_MS);
|
||||||
if (waitfor) {
|
if (waitfor) {
|
||||||
POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor,
|
POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor,
|
||||||
timespec_tomillis(remain));
|
timespec_tomillis(remain));
|
||||||
|
|
|
@ -723,8 +723,6 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
|
||||||
int sig;
|
int sig;
|
||||||
int64_t sem;
|
int64_t sem;
|
||||||
uint32_t wi, ms = -1;
|
uint32_t wi, ms = -1;
|
||||||
int handler_was_called;
|
|
||||||
struct PosixThread *pt;
|
|
||||||
if (!__ttyconf.vmin) {
|
if (!__ttyconf.vmin) {
|
||||||
if (!__ttyconf.vtime) {
|
if (!__ttyconf.vtime) {
|
||||||
return 0; // non-blocking w/o raising eagain
|
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;
|
ms = __ttyconf.vtime * 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (f->flags & _O_NONBLOCK) {
|
|
||||||
return eagain(); // standard unix non-blocking
|
|
||||||
}
|
|
||||||
if (_check_cancel() == -1) return -1;
|
if (_check_cancel() == -1) return -1;
|
||||||
if ((sig = __sig_get(waitmask))) {
|
if (f->flags & _O_NONBLOCK) return eagain();
|
||||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
if ((sig = __sig_get(waitmask))) goto DeliverSignal;
|
||||||
if (_check_cancel() == -1) return -1;
|
struct PosixThread *pt = _pthread_self();
|
||||||
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);
|
|
||||||
pt->pt_blkmask = waitmask;
|
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);
|
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM, memory_order_release);
|
||||||
wi = WaitForMultipleObjects(2, (int64_t[2]){__keystroke.cin, sem}, 0, ms);
|
wi = WaitForMultipleObjects(2, (int64_t[2]){__keystroke.cin, sem}, 0, ms);
|
||||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
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 == kNtWaitTimeout) return 0; // vtime elapsed
|
||||||
if (wi == 0) return -2; // console data
|
if (wi == 0) return -2; // console data
|
||||||
if (wi != 1) return __winerr(); // wait failed
|
if (wi != 1) return __winerr(); // wait failed
|
||||||
if (!(sig = __sig_get(waitmask))) return eintr();
|
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 (_check_cancel() == -1) return -1;
|
||||||
if (handler_was_called != 1) return -2;
|
if (!(handler_was_called & SIG_HANDLED_NO_RESTART)) return -2;
|
||||||
return eintr();
|
return eintr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,15 +16,12 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
|
||||||
#include "libc/calls/createfileflags.internal.h"
|
#include "libc/calls/createfileflags.internal.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/sig.internal.h"
|
#include "libc/calls/sig.internal.h"
|
||||||
#include "libc/calls/struct/fd.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/calls/syscall_support-nt.internal.h"
|
||||||
#include "libc/errno.h"
|
|
||||||
#include "libc/intrin/atomic.h"
|
|
||||||
#include "libc/nt/enum/filetype.h"
|
#include "libc/nt/enum/filetype.h"
|
||||||
#include "libc/nt/errors.h"
|
#include "libc/nt/errors.h"
|
||||||
#include "libc/nt/events.h"
|
#include "libc/nt/events.h"
|
||||||
|
@ -34,28 +31,10 @@
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
#include "libc/nt/thread.h"
|
#include "libc/nt/thread.h"
|
||||||
#include "libc/stdio/sysparam.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/consts/sicode.h"
|
||||||
#include "libc/sysv/errfuns.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__
|
#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.
|
* 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
|
textwindows ssize_t
|
||||||
sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
|
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 *,
|
bool32 ReadOrWriteFile(int64_t, void *, uint32_t, uint32_t *,
|
||||||
struct NtOverlapped *)) {
|
struct NtOverlapped *)) {
|
||||||
bool32 ok;
|
int sig;
|
||||||
int sig = 0;
|
|
||||||
uint32_t exchanged;
|
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;
|
struct Fd *f = g_fds.p + fd;
|
||||||
|
|
||||||
// win32 i/o apis generally take 32-bit values thus we implicitly
|
// 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:
|
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
|
// signals have already been fully blocked by caller
|
||||||
// perform i/o operation with atomic signal/cancel checking
|
// perform i/o operation with atomic signal/cancel checking
|
||||||
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 1, 0, 0),
|
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 1, 0, 0),
|
||||||
.Pointer = offset};
|
.Pointer = offset};
|
||||||
struct ReadwriteResources rwc = {handle, &overlap};
|
bool32 ok = ReadOrWriteFile(handle, data, size, 0, &overlap);
|
||||||
pthread_cleanup_push(UnwindReadwrite, &rwc);
|
|
||||||
ok = ReadOrWriteFile(handle, data, size, 0, &overlap);
|
|
||||||
if (!ok && GetLastError() == kNtErrorIoPending) {
|
if (!ok && GetLastError() == kNtErrorIoPending) {
|
||||||
// win32 says this i/o operation needs to block
|
// win32 says this i/o operation needs to block
|
||||||
if (f->flags & _O_NONBLOCK) {
|
if (f->flags & _O_NONBLOCK) {
|
||||||
// abort the i/o operation if file descriptor is in non-blocking mode
|
// abort the i/o operation if file descriptor is in non-blocking mode
|
||||||
CancelIoEx(handle, &overlap);
|
CancelIoEx(handle, &overlap);
|
||||||
eagained = true;
|
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 {
|
} else {
|
||||||
// wait until i/o either completes or is canceled by another thread
|
// wait until i/o either completes or is canceled by another thread
|
||||||
// we avoid a race condition by having a second mask for unblocking
|
// we avoid a race condition by having a second mask for unblocking
|
||||||
|
struct PosixThread *pt;
|
||||||
pt = _pthread_self();
|
pt = _pthread_self();
|
||||||
pt->pt_blkmask = waitmask;
|
pt->pt_blkmask = waitmask;
|
||||||
pt->pt_iohandle = handle;
|
pt->pt_iohandle = handle;
|
||||||
|
@ -147,30 +113,13 @@ RestartOperation:
|
||||||
if (ok) {
|
if (ok) {
|
||||||
ok = GetOverlappedResult(handle, &overlap, &exchanged, true);
|
ok = GetOverlappedResult(handle, &overlap, &exchanged, true);
|
||||||
}
|
}
|
||||||
pthread_cleanup_pop(false);
|
|
||||||
CloseHandle(overlap.hEvent);
|
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 i/o succeeded then return its result
|
||||||
if (ok) {
|
if (ok) {
|
||||||
if (!pwriting && seekable) {
|
if (!pwriting && seekable) {
|
||||||
f->pointer = offset + exchanged;
|
f->pointer = offset + exchanged;
|
||||||
}
|
}
|
||||||
errno = olderror;
|
|
||||||
return exchanged;
|
return exchanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,17 +129,15 @@ RestartOperation:
|
||||||
if (eagained) {
|
if (eagained) {
|
||||||
return eagain();
|
return eagain();
|
||||||
}
|
}
|
||||||
// at this point the i/o must have been canceled due to a signal.
|
// otherwise it must be due to a kill() via __sig_cancel()
|
||||||
// this could be because we found the signal earlier and canceled
|
if ((sig = __sig_get(waitmask))) {
|
||||||
// ourself. otherwise it's due to a kill from another thread that
|
HandleInterrupt:
|
||||||
// added something to our mask and canceled our i/o, so we check.
|
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||||
if (!handler_was_called && (sig = __sig_get(waitmask))) {
|
if (_check_cancel() == -1) return -1; // possible if we SIGTHR'd
|
||||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
// read() is @restartable unless non-SA_RESTART hands were called
|
||||||
if (_check_cancel() == -1) return -1;
|
if (!(handler_was_called & SIG_HANDLED_NO_RESTART)) {
|
||||||
}
|
goto RestartOperation;
|
||||||
// read() is @restartable unless non-SA_RESTART hands were called
|
}
|
||||||
if (handler_was_called != 1) {
|
|
||||||
goto RestartOperation;
|
|
||||||
}
|
}
|
||||||
return eintr();
|
return eintr();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,6 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "ape/sections.internal.h"
|
#include "ape/sections.internal.h"
|
||||||
#include "libc/assert.h"
|
|
||||||
#include "libc/atomic.h"
|
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.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"
|
||||||
|
@ -30,34 +28,26 @@
|
||||||
#include "libc/calls/ucontext.h"
|
#include "libc/calls/ucontext.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/fmt/itoa.h"
|
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/bsf.h"
|
#include "libc/intrin/bsf.h"
|
||||||
#include "libc/intrin/describebacktrace.internal.h"
|
#include "libc/intrin/describebacktrace.internal.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/dll.h"
|
||||||
#include "libc/intrin/popcnt.h"
|
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/intrin/weaken.h"
|
|
||||||
#include "libc/nt/console.h"
|
#include "libc/nt/console.h"
|
||||||
#include "libc/nt/enum/context.h"
|
#include "libc/nt/enum/context.h"
|
||||||
#include "libc/nt/enum/exceptionhandleractions.h"
|
#include "libc/nt/enum/exceptionhandleractions.h"
|
||||||
#include "libc/nt/enum/signal.h"
|
#include "libc/nt/enum/signal.h"
|
||||||
#include "libc/nt/enum/status.h"
|
#include "libc/nt/enum/status.h"
|
||||||
#include "libc/nt/errors.h"
|
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/signals.h"
|
#include "libc/nt/signals.h"
|
||||||
#include "libc/nt/struct/context.h"
|
|
||||||
#include "libc/nt/struct/ntexceptionpointers.h"
|
#include "libc/nt/struct/ntexceptionpointers.h"
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
#include "libc/nt/thread.h"
|
#include "libc/nt/thread.h"
|
||||||
#include "libc/runtime/runtime.h"
|
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/sa.h"
|
#include "libc/sysv/consts/sa.h"
|
||||||
#include "libc/sysv/consts/sicode.h"
|
#include "libc/sysv/consts/sicode.h"
|
||||||
#include "libc/sysv/consts/ss.h"
|
#include "libc/sysv/consts/ss.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#include "libc/thread/thread.h"
|
|
||||||
#include "libc/thread/tls.h"
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -206,16 +196,19 @@ textwindows int __sig_raise(volatile int sig, int sic) {
|
||||||
memory_order_acquire);
|
memory_order_acquire);
|
||||||
|
|
||||||
// call the user's signal handler
|
// call the user's signal handler
|
||||||
char ssbuf[2][128];
|
char ssbuf[128];
|
||||||
siginfo_t si = {.si_signo = sig, .si_code = sic};
|
siginfo_t si = {.si_signo = sig, .si_code = sic};
|
||||||
STRACE("__sig_raise(%G, %t) mask %s → %s", sig, __sig_handler(rva),
|
STRACE("__sig_raise(%G, %t) mask %s", sig, __sig_handler(rva),
|
||||||
(DescribeSigset)(ssbuf[0], 0, &ctx.uc_sigmask),
|
(DescribeSigset)(ssbuf, 0, (sigset_t *)&pt->tib->tib_sigmask));
|
||||||
(DescribeSigset)(ssbuf[1], 0, (sigset_t *)&pt->tib->tib_sigmask));
|
|
||||||
__sig_handler(rva)(sig, &si, &ctx);
|
__sig_handler(rva)(sig, &si, &ctx);
|
||||||
(void)ssbuf;
|
(void)ssbuf;
|
||||||
|
|
||||||
// record this handler
|
// 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
|
// restore sigmask
|
||||||
|
@ -320,12 +313,18 @@ static int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
||||||
__sig_terminate(sig);
|
__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
|
// take control of thread
|
||||||
// suspending the thread happens asynchronously
|
// suspending the thread happens asynchronously
|
||||||
// however getting the context blocks until it's frozen
|
// however getting the context blocks until it's frozen
|
||||||
static pthread_spinlock_t killer_lock;
|
static pthread_spinlock_t killer_lock;
|
||||||
pthread_spin_lock(&killer_lock);
|
pthread_spin_lock(&killer_lock);
|
||||||
uintptr_t th = _pthread_syshand(pt);
|
|
||||||
if (SuspendThread(th) == -1u) {
|
if (SuspendThread(th) == -1u) {
|
||||||
STRACE("SuspendThread failed w/ %d", GetLastError());
|
STRACE("SuspendThread failed w/ %d", GetLastError());
|
||||||
pthread_spin_unlock(&killer_lock);
|
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);
|
STRACE("terminating on %G due to no handler", sig);
|
||||||
__sig_terminate(sig);
|
__sig_terminate(sig);
|
||||||
}
|
}
|
||||||
|
if (atomic_load_explicit(&__sig.pending, memory_order_acquire) &
|
||||||
|
(1ull << (sig - 1))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
BLOCK_SIGNALS;
|
BLOCK_SIGNALS;
|
||||||
_pthread_lock();
|
_pthread_lock();
|
||||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
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
|
// choose this thread if it isn't masking sig
|
||||||
if (!(atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
|
if (!(atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
|
||||||
(1ull << (sig - 1)))) {
|
(1ull << (sig - 1)))) {
|
||||||
STRACE("generating %G by killing %d", sig, _pthread_tid(pt));
|
|
||||||
_pthread_ref(pt);
|
_pthread_ref(pt);
|
||||||
mark = pt;
|
mark = pt;
|
||||||
break;
|
break;
|
||||||
|
@ -439,7 +441,6 @@ textwindows void __sig_generate(int sig, int sic) {
|
||||||
if (atomic_load_explicit(&pt->pt_blocker, memory_order_acquire) &&
|
if (atomic_load_explicit(&pt->pt_blocker, memory_order_acquire) &&
|
||||||
!(atomic_load_explicit(&pt->pt_blkmask, memory_order_relaxed) &
|
!(atomic_load_explicit(&pt->pt_blkmask, memory_order_relaxed) &
|
||||||
(1ull << (sig - 1)))) {
|
(1ull << (sig - 1)))) {
|
||||||
STRACE("generating %G by unblocking %d", sig, _pthread_tid(pt));
|
|
||||||
_pthread_ref(pt);
|
_pthread_ref(pt);
|
||||||
mark = pt;
|
mark = pt;
|
||||||
break;
|
break;
|
||||||
|
@ -450,7 +451,6 @@ textwindows void __sig_generate(int sig, int sic) {
|
||||||
__sig_killer(mark, sig, sic);
|
__sig_killer(mark, sig, sic);
|
||||||
_pthread_unref(mark);
|
_pthread_unref(mark);
|
||||||
} else {
|
} else {
|
||||||
STRACE("all threads block %G so adding to pending signals of process", sig);
|
|
||||||
atomic_fetch_or_explicit(&__sig.pending, 1ull << (sig - 1),
|
atomic_fetch_or_explicit(&__sig.pending, 1ull << (sig - 1),
|
||||||
memory_order_relaxed);
|
memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,8 @@
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
|
||||||
#define __SIG_LOCK_INTERVAL_MS 1000 /* semaphore synchronization: solid */
|
#define SIG_HANDLED_NO_RESTART 1
|
||||||
#define __SIG_SIG_INTERVAL_MS 1000 /* posix signal polyfill also solid */
|
#define SIG_HANDLED_SA_RESTART 2
|
||||||
#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
|
|
||||||
|
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
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) {
|
static inline struct timespec timespec_fromseconds(int64_t __x) {
|
||||||
return (struct timespec){__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);
|
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;
|
return __ts.tv_sec >= 0 && __ts.tv_nsec + 0ull < 1000000000ull;
|
||||||
}
|
}
|
||||||
#endif /* _COSMO_SOURCE */
|
#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) {
|
static inline struct timespec timeval_totimespec(struct timeval __tv) {
|
||||||
return (struct timespec){__tv.tv_sec, __tv.tv_usec * 1000};
|
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);
|
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;
|
return __tv.tv_sec >= 0 && __tv.tv_usec + 0ull < 1000000ull;
|
||||||
}
|
}
|
||||||
#endif /* _COSMO_SOURCE */
|
#endif /* _COSMO_SOURCE */
|
||||||
|
|
|
@ -17,17 +17,14 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/ucontext.h"
|
#include "libc/calls/ucontext.h"
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/dce.h"
|
||||||
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
|
#include "libc/thread/tls.h"
|
||||||
|
|
||||||
int __tailcontext(const ucontext_t *);
|
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.
|
* Sets machine context.
|
||||||
*
|
*
|
||||||
|
@ -37,14 +34,31 @@ static int __contextmask(const sigset_t *opt_set, sigset_t *opt_out_oldset) {
|
||||||
* @see getcontext()
|
* @see getcontext()
|
||||||
*/
|
*/
|
||||||
int setcontext(const ucontext_t *uc) {
|
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);
|
return __tailcontext(uc);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __getcontextsig(ucontext_t *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) {
|
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
|
#endif
|
||||||
|
|
||||||
#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 406 || defined(__llvm__)) && \
|
#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)
|
int128_t i128abs(int128_t)
|
||||||
libcesque pureconst;
|
libcesque pureconst;
|
||||||
int128_t strtoi128(const char *, char **, int) paramsnonnull((1));
|
int128_t strtoi128(const char *, char **, int) paramsnonnull((1));
|
||||||
|
|
|
@ -71,7 +71,7 @@ TryAgain:
|
||||||
switch (__imp_GetLastError()) {
|
switch (__imp_GetLastError()) {
|
||||||
case kNtErrorPipeBusy:
|
case kNtErrorPipeBusy:
|
||||||
if (micros >= 1024) __imp_Sleep(micros / 1024);
|
if (micros >= 1024) __imp_Sleep(micros / 1024);
|
||||||
if (micros / 1024 < __SIG_IO_INTERVAL_MS) micros <<= 1;
|
if (micros < 1024 * 1024) micros <<= 1;
|
||||||
goto TryAgain;
|
goto TryAgain;
|
||||||
case kNtErrorAccessDenied:
|
case kNtErrorAccessDenied:
|
||||||
// GetNtOpenFlags() always greedily requests execute permissions
|
// GetNtOpenFlags() always greedily requests execute permissions
|
||||||
|
|
|
@ -50,7 +50,7 @@ TryAgain:
|
||||||
nDefaultTimeOutMs, opt_lpSecurityAttributes);
|
nDefaultTimeOutMs, opt_lpSecurityAttributes);
|
||||||
if (hServer == -1 && __imp_GetLastError() == kNtErrorPipeBusy) {
|
if (hServer == -1 && __imp_GetLastError() == kNtErrorPipeBusy) {
|
||||||
if (micros >= 1024) __imp_Sleep(micros / 1024);
|
if (micros >= 1024) __imp_Sleep(micros / 1024);
|
||||||
if (micros / 1024 < __SIG_IO_INTERVAL_MS) micros <<= 1;
|
if (micros < 1024 * 1024) micros <<= 1;
|
||||||
goto TryAgain;
|
goto TryAgain;
|
||||||
}
|
}
|
||||||
if (hServer == -1) __winerr();
|
if (hServer == -1) __winerr();
|
||||||
|
|
|
@ -23,9 +23,7 @@
|
||||||
|
|
||||||
static bool _pthread_deref(struct PosixThread *pt) {
|
static bool _pthread_deref(struct PosixThread *pt) {
|
||||||
int refs = atomic_load_explicit(&pt->pt_refs, memory_order_acquire);
|
int refs = atomic_load_explicit(&pt->pt_refs, memory_order_acquire);
|
||||||
if (!refs) return true;
|
return !refs || !atomic_fetch_sub(&pt->pt_refs, 1);
|
||||||
unassert(refs > 0);
|
|
||||||
return !atomic_fetch_sub(&pt->pt_refs, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pthread_unref(struct PosixThread *pt) {
|
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) {
|
if (how == SIG_BLOCK) {
|
||||||
oldmask = atomic_fetch_or_explicit(mask, *neu, memory_order_acq_rel);
|
oldmask = atomic_fetch_or_explicit(mask, *neu, memory_order_acq_rel);
|
||||||
} else if (how == SIG_UNBLOCK) {
|
} 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
|
} else { // SIG_SETMASK
|
||||||
oldmask = atomic_exchange_explicit(mask, *neu, memory_order_acq_rel);
|
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.
|
* Receives data from Windows socket.
|
||||||
*
|
|
||||||
* @return 0 on success, or -1 on failure
|
* @return 0 on success, or -1 on failure
|
||||||
* @note this wrapper takes care of ABI, STRACE(), and __winerr()
|
|
||||||
*/
|
*/
|
||||||
textwindows int WSARecv(
|
textwindows int WSARecv(
|
||||||
uint64_t s, const struct NtIovec *inout_lpBuffers, uint32_t dwBufferCount,
|
uint64_t s, const struct NtIovec *inout_lpBuffers, uint32_t dwBufferCount,
|
||||||
|
@ -57,9 +55,6 @@ textwindows int WSARecv(
|
||||||
if (opt_out_lpNumberOfBytesRecvd) {
|
if (opt_out_lpNumberOfBytesRecvd) {
|
||||||
*opt_out_lpNumberOfBytesRecvd = NumberOfBytesRecvd;
|
*opt_out_lpNumberOfBytesRecvd = NumberOfBytesRecvd;
|
||||||
}
|
}
|
||||||
if (rc == -1) {
|
|
||||||
__winsockerr();
|
|
||||||
}
|
|
||||||
if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) {
|
if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) {
|
||||||
kprintf(STRACE_PROLOGUE "WSARecv(%lu, [", s);
|
kprintf(STRACE_PROLOGUE "WSARecv(%lu, [", s);
|
||||||
DescribeIovNt(inout_lpBuffers, dwBufferCount,
|
DescribeIovNt(inout_lpBuffers, dwBufferCount,
|
||||||
|
@ -73,9 +68,6 @@ textwindows int WSARecv(
|
||||||
rc = __imp_WSARecv(s, inout_lpBuffers, dwBufferCount,
|
rc = __imp_WSARecv(s, inout_lpBuffers, dwBufferCount,
|
||||||
opt_out_lpNumberOfBytesRecvd, inout_lpFlags,
|
opt_out_lpNumberOfBytesRecvd, inout_lpFlags,
|
||||||
opt_inout_lpOverlapped, opt_lpCompletionRoutine);
|
opt_inout_lpOverlapped, opt_lpCompletionRoutine);
|
||||||
if (rc == -1) {
|
|
||||||
__winsockerr();
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,7 @@ __msabi extern typeof(WSARecvFrom) *const __imp_WSARecvFrom;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives data from Windows socket.
|
* Receives data from Windows socket.
|
||||||
*
|
|
||||||
* @return 0 on success, or -1 on failure
|
* @return 0 on success, or -1 on failure
|
||||||
* @note this wrapper takes care of ABI, STRACE(), and __winerr()
|
|
||||||
*/
|
*/
|
||||||
textwindows int WSARecvFrom(
|
textwindows int WSARecvFrom(
|
||||||
uint64_t s, const struct NtIovec *inout_lpBuffers, uint32_t dwBufferCount,
|
uint64_t s, const struct NtIovec *inout_lpBuffers, uint32_t dwBufferCount,
|
||||||
|
@ -52,9 +50,6 @@ textwindows int WSARecvFrom(
|
||||||
if (opt_out_lpNumberOfBytesRecvd) {
|
if (opt_out_lpNumberOfBytesRecvd) {
|
||||||
*opt_out_lpNumberOfBytesRecvd = NumberOfBytesRecvd;
|
*opt_out_lpNumberOfBytesRecvd = NumberOfBytesRecvd;
|
||||||
}
|
}
|
||||||
if (rc == -1) {
|
|
||||||
__winerr();
|
|
||||||
}
|
|
||||||
if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) {
|
if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) {
|
||||||
kprintf(STRACE_PROLOGUE "WSARecvFrom(%lu, [", s);
|
kprintf(STRACE_PROLOGUE "WSARecvFrom(%lu, [", s);
|
||||||
DescribeIovNt(inout_lpBuffers, dwBufferCount,
|
DescribeIovNt(inout_lpBuffers, dwBufferCount,
|
||||||
|
@ -69,9 +64,6 @@ textwindows int WSARecvFrom(
|
||||||
opt_out_lpNumberOfBytesRecvd, inout_lpFlags,
|
opt_out_lpNumberOfBytesRecvd, inout_lpFlags,
|
||||||
opt_out_fromsockaddr, opt_inout_fromsockaddrlen,
|
opt_out_fromsockaddr, opt_inout_fromsockaddrlen,
|
||||||
opt_inout_lpOverlapped, opt_lpCompletionRoutine);
|
opt_inout_lpOverlapped, opt_lpCompletionRoutine);
|
||||||
if (rc == -1) {
|
|
||||||
__winerr();
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "libc/calls/struct/sigset.internal.h"
|
#include "libc/calls/struct/sigset.internal.h"
|
||||||
#include "libc/calls/syscall-nt.internal.h"
|
#include "libc/calls/syscall-nt.internal.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/fmt/itoa.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/nt/enum/processaccess.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[]) {
|
char *const envp[]) {
|
||||||
|
|
||||||
// execve() needs to be @asyncsignalsafe
|
// execve() needs to be @asyncsignalsafe
|
||||||
sigset_t m = __sig_block();
|
sigset_t sigmask = __sig_block();
|
||||||
_pthread_lock();
|
_pthread_lock();
|
||||||
|
|
||||||
// new process should be a child of our parent
|
// 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(
|
if (!(hParentProcess = OpenProcess(
|
||||||
kNtProcessDupHandle | kNtProcessCreateProcess, false, ppid))) {
|
kNtProcessDupHandle | kNtProcessCreateProcess, false, ppid))) {
|
||||||
_pthread_unlock();
|
_pthread_unlock();
|
||||||
__sig_unblock(m);
|
__sig_unblock(sigmask);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inherit signal mask
|
||||||
|
char maskvar[6 + 21];
|
||||||
|
FormatUint64(stpcpy(maskvar, "_MASK="), sigmask);
|
||||||
|
|
||||||
// define stdio handles for the spawned subprocess
|
// define stdio handles for the spawned subprocess
|
||||||
struct NtStartupInfo si = {
|
struct NtStartupInfo si = {
|
||||||
.cb = sizeof(struct NtStartupInfo),
|
.cb = sizeof(struct NtStartupInfo),
|
||||||
|
@ -80,21 +85,21 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
|
||||||
&lpExplicitHandles, &dwExplicitHandleCount))) {
|
&lpExplicitHandles, &dwExplicitHandleCount))) {
|
||||||
CloseHandle(hParentProcess);
|
CloseHandle(hParentProcess);
|
||||||
_pthread_unlock();
|
_pthread_unlock();
|
||||||
__sig_unblock(m);
|
__sig_unblock(sigmask);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// launch the process
|
// launch the process
|
||||||
struct NtProcessInformation pi;
|
struct NtProcessInformation pi;
|
||||||
int rc = ntspawn(AT_FDCWD, program, argv, envp, (char *[]){fdspec, 0}, 0, 0,
|
int rc = ntspawn(AT_FDCWD, program, argv, envp,
|
||||||
hParentProcess, lpExplicitHandles, dwExplicitHandleCount,
|
(char *[]){fdspec, maskvar, 0}, 0, 0, hParentProcess,
|
||||||
&si, &pi);
|
lpExplicitHandles, dwExplicitHandleCount, &si, &pi);
|
||||||
__undescribe_fds(hParentProcess, lpExplicitHandles, dwExplicitHandleCount);
|
__undescribe_fds(hParentProcess, lpExplicitHandles, dwExplicitHandleCount);
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
free(fdspec);
|
free(fdspec);
|
||||||
CloseHandle(hParentProcess);
|
CloseHandle(hParentProcess);
|
||||||
_pthread_unlock();
|
_pthread_unlock();
|
||||||
__sig_unblock(m);
|
__sig_unblock(sigmask);
|
||||||
if (GetLastError() == kNtErrorSharingViolation) {
|
if (GetLastError() == kNtErrorSharingViolation) {
|
||||||
return etxtbsy();
|
return etxtbsy();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -401,7 +401,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
__proc_free(proc);
|
dll_make_first(&__proc.free, &proc->elem);
|
||||||
}
|
}
|
||||||
ftrace_enabled(+1);
|
ftrace_enabled(+1);
|
||||||
strace_enabled(+1);
|
strace_enabled(+1);
|
||||||
|
|
|
@ -248,9 +248,9 @@ static textwindows errno_t posix_spawn_nt_impl(
|
||||||
struct SpawnFds fds = {0};
|
struct SpawnFds fds = {0};
|
||||||
int64_t dirhand = AT_FDCWD;
|
int64_t dirhand = AT_FDCWD;
|
||||||
int64_t *lpExplicitHandles = 0;
|
int64_t *lpExplicitHandles = 0;
|
||||||
|
sigset_t sigmask = __sig_block();
|
||||||
uint32_t dwExplicitHandleCount = 0;
|
uint32_t dwExplicitHandleCount = 0;
|
||||||
int64_t hCreatorProcess = GetCurrentProcess();
|
int64_t hCreatorProcess = GetCurrentProcess();
|
||||||
sigset_t m = __sig_block();
|
|
||||||
|
|
||||||
// reserve process tracking object
|
// reserve process tracking object
|
||||||
__proc_lock();
|
__proc_lock();
|
||||||
|
@ -266,11 +266,11 @@ static textwindows errno_t posix_spawn_nt_impl(
|
||||||
free(fdspec);
|
free(fdspec);
|
||||||
if (proc) {
|
if (proc) {
|
||||||
__proc_lock();
|
__proc_lock();
|
||||||
__proc_free(proc);
|
dll_make_first(&__proc.free, &proc->elem);
|
||||||
__proc_unlock();
|
__proc_unlock();
|
||||||
}
|
}
|
||||||
spawnfds_destroy(&fds);
|
spawnfds_destroy(&fds);
|
||||||
__sig_unblock(m);
|
__sig_unblock(sigmask);
|
||||||
errno = e;
|
errno = e;
|
||||||
return err;
|
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
|
// launch process
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
struct NtProcessInformation procinfo;
|
struct NtProcessInformation procinfo;
|
||||||
if (!envp) envp = environ;
|
if (!envp) envp = environ;
|
||||||
if ((fdspec = __describe_fds(fds.p, fds.n, &startinfo, hCreatorProcess,
|
if ((fdspec = __describe_fds(fds.p, fds.n, &startinfo, hCreatorProcess,
|
||||||
&lpExplicitHandles, &dwExplicitHandleCount))) {
|
&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,
|
dwCreationFlags, lpCurrentDirectory, 0, lpExplicitHandles,
|
||||||
dwExplicitHandleCount, &startinfo, &procinfo);
|
dwExplicitHandleCount, &startinfo, &procinfo);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,15 +32,20 @@
|
||||||
* - `POSIX_SPAWN_SETSCHEDPARAM`
|
* - `POSIX_SPAWN_SETSCHEDPARAM`
|
||||||
* - `POSIX_SPAWN_SETSCHEDULER`
|
* - `POSIX_SPAWN_SETSCHEDULER`
|
||||||
* - `POSIX_SPAWN_SETSID`
|
* - `POSIX_SPAWN_SETSID`
|
||||||
|
* - `POSIX_SPAWN_SETRLIMIT`
|
||||||
* @return 0 on success, or errno on error
|
* @return 0 on success, or errno on error
|
||||||
* @raise EINVAL if `flags` has invalid bits
|
* @raise EINVAL if `flags` has invalid bits
|
||||||
*/
|
*/
|
||||||
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
|
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
|
||||||
if (flags &
|
if (flags & ~(POSIX_SPAWN_USEVFORK | //
|
||||||
~(POSIX_SPAWN_USEVFORK | POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP |
|
POSIX_SPAWN_RESETIDS | //
|
||||||
POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK |
|
POSIX_SPAWN_SETPGROUP | //
|
||||||
POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER |
|
POSIX_SPAWN_SETSIGDEF | //
|
||||||
POSIX_SPAWN_SETSID | POSIX_SPAWN_SETRLIMIT)) {
|
POSIX_SPAWN_SETSIGMASK | //
|
||||||
|
POSIX_SPAWN_SETSCHEDPARAM | //
|
||||||
|
POSIX_SPAWN_SETSCHEDULER | //
|
||||||
|
POSIX_SPAWN_SETSID | //
|
||||||
|
POSIX_SPAWN_SETRLIMIT)) {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
(*attr)->flags = flags;
|
(*attr)->flags = flags;
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/proc/posix_spawn.h"
|
|
||||||
#include "libc/proc/posix_spawn.internal.h"
|
#include "libc/proc/posix_spawn.internal.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,14 +24,14 @@
|
||||||
* Setting `pgroup` to zero will ensure newly created processes are
|
* Setting `pgroup` to zero will ensure newly created processes are
|
||||||
* placed within their own brand new process group.
|
* 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 attr was initialized by posix_spawnattr_init()
|
||||||
* @param pgroup is the process group id, or 0 for self
|
* @param pgroup is the process group id, or 0 for self
|
||||||
* @return 0 on success, or errno on error
|
* @return 0 on success, or errno on error
|
||||||
*/
|
*/
|
||||||
int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, int pgroup) {
|
int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, int pgroup) {
|
||||||
(*attr)->flags |= POSIX_SPAWN_SETPGROUP;
|
|
||||||
(*attr)->pgroup = pgroup;
|
(*attr)->pgroup = pgroup;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
/**
|
/**
|
||||||
* Sets resource limit on spawned process.
|
* 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
|
* @return 0 on success, or errno on error
|
||||||
* @raise EINVAL if resource is invalid
|
* @raise EINVAL if resource is invalid
|
||||||
|
@ -34,7 +35,6 @@
|
||||||
int posix_spawnattr_setrlimit(posix_spawnattr_t *attr, int resource,
|
int posix_spawnattr_setrlimit(posix_spawnattr_t *attr, int resource,
|
||||||
const struct rlimit *rlim) {
|
const struct rlimit *rlim) {
|
||||||
if (0 <= resource && resource < MIN(RLIM_NLIMITS, ARRAYLEN((*attr)->rlim))) {
|
if (0 <= resource && resource < MIN(RLIM_NLIMITS, ARRAYLEN((*attr)->rlim))) {
|
||||||
(*attr)->flags |= POSIX_SPAWN_SETRLIMIT;
|
|
||||||
(*attr)->rlimset |= 1u << resource;
|
(*attr)->rlimset |= 1u << resource;
|
||||||
(*attr)->rlim[resource] = *rlim;
|
(*attr)->rlim[resource] = *rlim;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -16,14 +16,13 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/struct/sched_param.h"
|
|
||||||
#include "libc/proc/posix_spawn.h"
|
|
||||||
#include "libc/proc/posix_spawn.internal.h"
|
#include "libc/proc/posix_spawn.internal.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies scheduler parameter override for spawned process.
|
* 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 attr was initialized by posix_spawnattr_init()
|
||||||
* @param schedparam receives the result
|
* @param schedparam receives the result
|
||||||
|
@ -31,7 +30,6 @@
|
||||||
*/
|
*/
|
||||||
int posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
|
int posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
|
||||||
const struct sched_param *schedparam) {
|
const struct sched_param *schedparam) {
|
||||||
(*attr)->flags |= POSIX_SPAWN_SETSCHEDPARAM;
|
|
||||||
(*attr)->schedparam = *schedparam;
|
(*attr)->schedparam = *schedparam;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,19 +16,18 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/proc/posix_spawn.h"
|
|
||||||
#include "libc/proc/posix_spawn.internal.h"
|
#include "libc/proc/posix_spawn.internal.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies scheduler policy override for spawned process.
|
* 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()
|
* @param attr was initialized by posix_spawnattr_init()
|
||||||
* @return 0 on success, or errno on error
|
* @return 0 on success, or errno on error
|
||||||
*/
|
*/
|
||||||
int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) {
|
int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) {
|
||||||
(*attr)->flags |= POSIX_SPAWN_SETSCHEDULER;
|
|
||||||
(*attr)->schedpolicy = schedpolicy;
|
(*attr)->schedpolicy = schedpolicy;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/struct/sigset.h"
|
|
||||||
#include "libc/proc/posix_spawn.h"
|
|
||||||
#include "libc/proc/posix_spawn.internal.h"
|
#include "libc/proc/posix_spawn.internal.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,13 +30,13 @@
|
||||||
* set the signals to `SIG_IGN` earlier (since posix_spawn() will not
|
* set the signals to `SIG_IGN` earlier (since posix_spawn() will not
|
||||||
* issue O(128) system calls just to be totally pedantic about that).
|
* 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
|
* @return 0 on success, or errno on error
|
||||||
*/
|
*/
|
||||||
int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr,
|
int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr,
|
||||||
const sigset_t *sigdefault) {
|
const sigset_t *sigdefault) {
|
||||||
(*attr)->flags |= POSIX_SPAWN_SETSIGDEF;
|
|
||||||
(*attr)->sigdefault = *sigdefault;
|
(*attr)->sigdefault = *sigdefault;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,20 +16,18 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/struct/sigset.h"
|
|
||||||
#include "libc/proc/posix_spawn.h"
|
|
||||||
#include "libc/proc/posix_spawn.internal.h"
|
#include "libc/proc/posix_spawn.internal.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies signal mask for sigprocmask() in child process.
|
* 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
|
* @return 0 on success, or errno on error
|
||||||
*/
|
*/
|
||||||
int posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
|
int posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
|
||||||
const sigset_t *sigmask) {
|
const sigset_t *sigmask) {
|
||||||
(*attr)->flags |= POSIX_SPAWN_SETSIGMASK;
|
|
||||||
(*attr)->sigmask = *sigmask;
|
(*attr)->sigmask = *sigmask;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
204
libc/proc/proc.c
204
libc/proc/proc.c
|
@ -16,7 +16,6 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/sig.internal.h"
|
#include "libc/calls/sig.internal.h"
|
||||||
|
@ -37,6 +36,7 @@
|
||||||
#include "libc/nt/enum/processcreationflags.h"
|
#include "libc/nt/enum/processcreationflags.h"
|
||||||
#include "libc/nt/enum/status.h"
|
#include "libc/nt/enum/status.h"
|
||||||
#include "libc/nt/enum/wait.h"
|
#include "libc/nt/enum/wait.h"
|
||||||
|
#include "libc/nt/events.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"
|
||||||
|
@ -59,95 +59,167 @@
|
||||||
|
|
||||||
struct Procs __proc;
|
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));
|
bzero(ru, sizeof(*ru));
|
||||||
struct NtProcessMemoryCountersEx memcount = {sizeof(memcount)};
|
struct NtProcessMemoryCountersEx memcount = {sizeof(memcount)};
|
||||||
unassert(GetProcessMemoryInfo(h, &memcount, sizeof(memcount)));
|
GetProcessMemoryInfo(h, &memcount, sizeof(memcount));
|
||||||
ru->ru_maxrss = memcount.PeakWorkingSetSize / 1024;
|
ru->ru_maxrss = memcount.PeakWorkingSetSize / 1024;
|
||||||
ru->ru_majflt = memcount.PageFaultCount;
|
ru->ru_majflt = memcount.PageFaultCount;
|
||||||
struct NtFileTime createtime, exittime;
|
struct NtFileTime createtime, exittime;
|
||||||
struct NtFileTime kerneltime, usertime;
|
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_utime = WindowsDurationToTimeVal(ReadFileTime(usertime));
|
||||||
ru->ru_stime = WindowsDurationToTimeVal(ReadFileTime(kerneltime));
|
ru->ru_stime = WindowsDurationToTimeVal(ReadFileTime(kerneltime));
|
||||||
struct NtIoCounters iocount;
|
struct NtIoCounters iocount;
|
||||||
unassert(GetProcessIoCounters(h, &iocount));
|
GetProcessIoCounters(h, &iocount);
|
||||||
ru->ru_inblock = iocount.ReadOperationCount;
|
ru->ru_inblock = iocount.ReadOperationCount;
|
||||||
ru->ru_oublock = iocount.WriteOperationCount;
|
ru->ru_oublock = iocount.WriteOperationCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
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) {
|
||||||
|
// handle child execve()
|
||||||
|
CloseHandle(pr->handle);
|
||||||
|
pr->handle = status & 0x00FFFFFF;
|
||||||
|
} else {
|
||||||
|
// handle child _Exit()
|
||||||
|
if (status == 0xc9af3d51u) {
|
||||||
|
status = kNtStillActive;
|
||||||
|
}
|
||||||
|
pr->wstatus = status;
|
||||||
|
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 {
|
||||||
|
// 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);
|
||||||
|
SetEvent(__proc.haszombies);
|
||||||
|
if (!pr->waiters && !__proc.waiters) {
|
||||||
|
if (WIFSIGNALED(status)) {
|
||||||
|
sic = CLD_KILLED;
|
||||||
|
} else {
|
||||||
|
sic = CLD_EXITED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sic;
|
||||||
|
}
|
||||||
|
|
||||||
static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
|
static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
|
||||||
__bootstrap_tls(&__proc.tls, __builtin_frame_address(0));
|
struct CosmoTib tls;
|
||||||
|
__bootstrap_tls(&tls, __builtin_frame_address(0));
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
// assemble a group of processes to wait on. if more than 64
|
// assemble a group of processes to wait on. if more than 64
|
||||||
// children exist, then we'll use a small timeout and select
|
// children exist, then we'll use a small timeout and select
|
||||||
// processes with a shifting window via a double linked list
|
// processes with a shifting window via a double linked list
|
||||||
struct rusage ru;
|
// if fewer than 64 processes exist then we'll also wait for
|
||||||
|
// process birth notifications, and wait on them immediately
|
||||||
int64_t handles[64];
|
int64_t handles[64];
|
||||||
int sic, dosignal = 0;
|
struct Proc *objects[64];
|
||||||
struct Proc *pr, *objects[64];
|
uint32_t millis, i, n = 0;
|
||||||
struct Dll *e, *e2, *samples = 0;
|
struct Dll *e, *e2, *samples = 0;
|
||||||
uint32_t millis, status, i, n = 1;
|
|
||||||
__proc_lock();
|
__proc_lock();
|
||||||
handles[0] = __proc.onstart;
|
for (e = dll_first(__proc.list); e && n < 64; e = e2) {
|
||||||
for (e = dll_first(__proc.list); e && n < 64; ++n, e = e2) {
|
struct Proc *pr = PROC_CONTAINER(e);
|
||||||
pr = PROC_CONTAINER(e);
|
|
||||||
e2 = dll_next(__proc.list, e);
|
e2 = dll_next(__proc.list, e);
|
||||||
|
// cycle process to end of list
|
||||||
dll_remove(&__proc.list, e);
|
dll_remove(&__proc.list, e);
|
||||||
dll_make_last(&samples, e);
|
dll_make_last(&samples, e);
|
||||||
handles[n] = pr->handle;
|
// don't bother waiting if it's already awaited
|
||||||
objects[n] = pr;
|
if (!pr->waiters) {
|
||||||
|
handles[n] = pr->handle;
|
||||||
|
objects[n] = pr;
|
||||||
|
++pr->waiters;
|
||||||
|
++n;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dll_make_last(&__proc.list, samples);
|
dll_make_last(&__proc.list, samples);
|
||||||
__proc_unlock();
|
__proc_unlock();
|
||||||
|
|
||||||
// wait for win32 to report any status change
|
// wait for something to happen
|
||||||
millis = n == 64 ? 20 : -1u;
|
if (n == 64) {
|
||||||
unassert((i = WaitForMultipleObjects(n, handles, false, millis)) != -1u);
|
millis = 5;
|
||||||
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];
|
|
||||||
rusage_add(&pr->ru, &ru);
|
|
||||||
rusage_add(&__proc.ruchlds, &ru);
|
|
||||||
if ((status & 0xFF000000u) == 0x23000000u) {
|
|
||||||
// handle child execve()
|
|
||||||
CloseHandle(pr->handle);
|
|
||||||
pr->handle = status & 0x00FFFFFF;
|
|
||||||
} else {
|
} else {
|
||||||
// handle child _exit()
|
millis = -1u;
|
||||||
CloseHandle(pr->handle);
|
handles[n++] = __proc.onbirth;
|
||||||
if (status == 0xc9af3d51u) {
|
}
|
||||||
status = kNtStillActive;
|
i = WaitForMultipleObjects(n, handles, false, millis);
|
||||||
}
|
if (i == -1u) {
|
||||||
pr->wstatus = status;
|
STRACE("proc wait panic %d", GetLastError());
|
||||||
if ((__sighandrvas[SIGCHLD] == (uintptr_t)SIG_IGN ||
|
_Exit(157);
|
||||||
(__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) &&
|
}
|
||||||
(!pr->waiters && !__proc.waiters)) {
|
if (i & kNtWaitAbandoned) {
|
||||||
dll_remove(&__proc.list, &pr->elem);
|
i &= ~kNtWaitAbandoned;
|
||||||
dll_make_first(&__proc.free, &pr->elem);
|
STRACE("proc %u handle %ld abandoned", i, handles[i]);
|
||||||
} else {
|
}
|
||||||
pr->iszombie = 1;
|
__proc_lock();
|
||||||
dll_remove(&__proc.list, &pr->elem);
|
|
||||||
dll_make_first(&__proc.zombies, &pr->elem);
|
// release our waiter status
|
||||||
if (pr->waiters) {
|
for (int j = 0; j < n; ++j) {
|
||||||
nsync_cv_broadcast(&pr->onexit);
|
if (handles[j] == __proc.onbirth) continue;
|
||||||
} else if (__proc.waiters) {
|
if (j == i) continue;
|
||||||
nsync_cv_signal(&__proc.onexit);
|
if (!--objects[j]->waiters && objects[j]->status == PROC_UNDEAD) {
|
||||||
} else {
|
__proc_free(objects[j]);
|
||||||
dosignal = 1;
|
|
||||||
sic = WIFSIGNALED(status) ? CLD_KILLED : CLD_EXITED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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();
|
__proc_unlock();
|
||||||
if (dosignal) {
|
|
||||||
|
// 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);
|
__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.
|
* Lazy initializes process tracker data structures and worker.
|
||||||
*/
|
*/
|
||||||
static textwindows void __proc_setup(void) {
|
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,
|
__proc.thread = CreateThread(0, 65536, __proc_worker, 0,
|
||||||
kNtStackSizeParamIsAReservation, 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
|
* 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
|
* with system process information and then added back to the system by
|
||||||
* calling __proc_add(). If process creation fails, then it needs to be
|
* 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) {
|
textwindows struct Proc *__proc_new(void) {
|
||||||
struct Dll *e;
|
struct Dll *e;
|
||||||
|
@ -230,16 +303,13 @@ IGNORE_LEAKS(__proc_new)
|
||||||
*/
|
*/
|
||||||
textwindows void __proc_add(struct Proc *proc) {
|
textwindows void __proc_add(struct Proc *proc) {
|
||||||
dll_make_first(&__proc.list, &proc->elem);
|
dll_make_first(&__proc.list, &proc->elem);
|
||||||
ReleaseSemaphore(__proc.onstart, 1, 0);
|
SetEvent(__proc.onbirth);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
textwindows void __proc_free(struct Proc *pr) {
|
||||||
* Frees process allocation.
|
dll_remove(&__proc.undead, &pr->elem);
|
||||||
*
|
dll_make_first(&__proc.free, &pr->elem);
|
||||||
* Process must not be currently tracked in the active or zombies list.
|
CloseHandle(pr->handle);
|
||||||
*/
|
|
||||||
textwindows void __proc_free(struct Proc *proc) {
|
|
||||||
dll_make_first(&__proc.free, &proc->elem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns owned handle of direct child process
|
// returns owned handle of direct child process
|
||||||
|
|
|
@ -2,25 +2,27 @@
|
||||||
#define COSMOPOLITAN_LIBC_PROC_H_
|
#define COSMOPOLITAN_LIBC_PROC_H_
|
||||||
#include "libc/atomic.h"
|
#include "libc/atomic.h"
|
||||||
#include "libc/calls/struct/rusage.h"
|
#include "libc/calls/struct/rusage.h"
|
||||||
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/dll.h"
|
#include "libc/intrin/dll.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "libc/thread/tls.h"
|
|
||||||
#include "third_party/nsync/cv.h"
|
|
||||||
#include "third_party/nsync/mu.h"
|
#include "third_party/nsync/mu.h"
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
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)
|
#define PROC_CONTAINER(e) DLL_CONTAINER(struct Proc, elem, e)
|
||||||
|
|
||||||
struct Proc {
|
struct Proc {
|
||||||
int pid;
|
int pid;
|
||||||
|
int status;
|
||||||
int waiters;
|
int waiters;
|
||||||
bool iszombie;
|
|
||||||
bool wasforked;
|
bool wasforked;
|
||||||
uint32_t wstatus;
|
uint32_t wstatus;
|
||||||
int64_t handle;
|
int64_t handle;
|
||||||
struct Dll elem;
|
struct Dll elem;
|
||||||
nsync_cv onexit;
|
|
||||||
struct rusage ru;
|
struct rusage ru;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,15 +30,15 @@ struct Procs {
|
||||||
int waiters;
|
int waiters;
|
||||||
atomic_uint once;
|
atomic_uint once;
|
||||||
nsync_mu lock;
|
nsync_mu lock;
|
||||||
nsync_cv onexit;
|
|
||||||
intptr_t thread;
|
intptr_t thread;
|
||||||
intptr_t onstart;
|
intptr_t onbirth;
|
||||||
|
intptr_t haszombies;
|
||||||
struct Dll *list;
|
struct Dll *list;
|
||||||
struct Dll *free;
|
struct Dll *free;
|
||||||
|
struct Dll *undead;
|
||||||
struct Dll *zombies;
|
struct Dll *zombies;
|
||||||
struct Proc pool[8];
|
struct Proc pool[8];
|
||||||
unsigned allocated;
|
unsigned allocated;
|
||||||
struct CosmoTib tls;
|
|
||||||
struct rusage ruchlds;
|
struct rusage ruchlds;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,6 +52,7 @@ int64_t __proc_search(int);
|
||||||
struct Proc *__proc_new(void);
|
struct Proc *__proc_new(void);
|
||||||
void __proc_add(struct Proc *);
|
void __proc_add(struct Proc *);
|
||||||
void __proc_free(struct Proc *);
|
void __proc_free(struct Proc *);
|
||||||
|
int __proc_harvest(struct Proc *, bool);
|
||||||
int sys_wait4_nt(int, int *, int, struct rusage *);
|
int sys_wait4_nt(int, int *, int, struct rusage *);
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
|
|
|
@ -35,8 +35,7 @@ LIBC_PROC_A_DIRECTDEPS = \
|
||||||
LIBC_STR \
|
LIBC_STR \
|
||||||
LIBC_SYSV \
|
LIBC_SYSV \
|
||||||
LIBC_SYSV_CALLS \
|
LIBC_SYSV_CALLS \
|
||||||
THIRD_PARTY_NSYNC \
|
THIRD_PARTY_NSYNC
|
||||||
THIRD_PARTY_NSYNC_MEM
|
|
||||||
|
|
||||||
LIBC_PROC_A_DEPS := \
|
LIBC_PROC_A_DEPS := \
|
||||||
$(call uniq,$(foreach x,$(LIBC_PROC_A_DIRECTDEPS),$($(x))))
|
$(call uniq,$(foreach x,$(LIBC_PROC_A_DIRECTDEPS),$($(x))))
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/struct/sched_param.h"
|
#include "libc/calls/struct/sched_param.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
#include "libc/sysv/consts/ioprio.h"
|
#include "libc/sysv/consts/ioprio.h"
|
||||||
#include "libc/sysv/consts/prio.h"
|
#include "libc/sysv/consts/prio.h"
|
||||||
#include "libc/sysv/consts/sched.h"
|
#include "libc/sysv/consts/sched.h"
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
*/
|
*/
|
||||||
int verynice(void) {
|
int verynice(void) {
|
||||||
int e = errno;
|
int e = errno;
|
||||||
setpriority(PRIO_PROCESS, 0, 10);
|
setpriority(PRIO_PROCESS, 0, NZERO);
|
||||||
sys_ioprio_set(IOPRIO_WHO_PROCESS, 0,
|
sys_ioprio_set(IOPRIO_WHO_PROCESS, 0,
|
||||||
IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
|
IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
|
||||||
struct sched_param param = {sched_get_priority_min(SCHED_IDLE)};
|
struct sched_param param = {sched_get_priority_min(SCHED_IDLE)};
|
||||||
|
|
|
@ -16,139 +16,191 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
|
||||||
#include "libc/calls/calls.h"
|
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/sig.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/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/dll.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/nt/enum/wait.h"
|
||||||
#include "libc/nt/accounting.h"
|
#include "libc/nt/events.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/synchronization.h"
|
||||||
#include "libc/nt/struct/processmemorycounters.h"
|
|
||||||
#include "libc/proc/proc.internal.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/sig.h"
|
||||||
#include "libc/sysv/consts/w.h"
|
#include "libc/sysv/consts/w.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/thread.h"
|
|
||||||
#include "libc/thread/tls.h"
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
static textwindows struct timespec GetNextDeadline(struct timespec deadline) {
|
static textwindows int __proc_reap(struct Proc *pr, int *wstatus,
|
||||||
if (timespec_iszero(deadline)) deadline = timespec_real();
|
struct rusage *opt_out_rusage) {
|
||||||
struct timespec delay = timespec_frommillis(__SIG_PROC_INTERVAL_MS);
|
|
||||||
return timespec_add(deadline, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
static textwindows int ReapZombie(struct Proc *pr, int *wstatus,
|
|
||||||
struct rusage *opt_out_rusage) {
|
|
||||||
if (wstatus) {
|
if (wstatus) {
|
||||||
*wstatus = pr->wstatus;
|
*wstatus = pr->wstatus;
|
||||||
}
|
}
|
||||||
if (opt_out_rusage) {
|
if (opt_out_rusage) {
|
||||||
*opt_out_rusage = pr->ru;
|
*opt_out_rusage = pr->ru;
|
||||||
}
|
}
|
||||||
if (!pr->waiters) {
|
dll_remove(&__proc.zombies, &pr->elem);
|
||||||
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);
|
dll_make_first(&__proc.free, &pr->elem);
|
||||||
|
CloseHandle(pr->handle);
|
||||||
}
|
}
|
||||||
return pr->pid;
|
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 rusage *opt_out_rusage) {
|
||||||
struct Dll *e;
|
struct Dll *e;
|
||||||
for (e = dll_first(__proc.zombies); e; e = dll_next(__proc.zombies, e)) {
|
for (e = dll_first(__proc.zombies); e; e = dll_next(__proc.zombies, e)) {
|
||||||
struct Proc *pr = PROC_CONTAINER(e);
|
struct Proc *pr = PROC_CONTAINER(e);
|
||||||
if (pid == -1 && pr->waiters) {
|
|
||||||
continue; // this zombie has been claimed
|
|
||||||
}
|
|
||||||
if (pid == -1 || pid == pr->pid) {
|
if (pid == -1 || pid == pr->pid) {
|
||||||
return ReapZombie(pr, wstatus, opt_out_rusage);
|
return __proc_reap(pr, wstatus, opt_out_rusage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void UnwindWaiterCount(void *arg) {
|
static textwindows int __proc_wait(int pid, int *wstatus, int options,
|
||||||
int *waiters = arg;
|
struct rusage *rusage, sigset_t waitmask) {
|
||||||
--*waiters;
|
for (;;) {
|
||||||
}
|
|
||||||
|
|
||||||
static textwindows int WaitForProcess(int pid, int *wstatus, int options,
|
// check for signals and cancelation
|
||||||
struct rusage *rusage,
|
int sig, handler_was_called;
|
||||||
uint64_t waitmask) {
|
if (_check_cancel() == -1) {
|
||||||
uint64_t m;
|
return -1;
|
||||||
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))) {
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the mark
|
|
||||||
pr = 0;
|
|
||||||
if (pid == -1) {
|
|
||||||
if (dll_is_empty(__proc.list)) {
|
|
||||||
return echild();
|
|
||||||
}
|
}
|
||||||
cv = &__proc.onexit;
|
if ((sig = __sig_get(waitmask))) {
|
||||||
wv = &__proc.waiters;
|
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||||
} else {
|
if (_check_cancel() == -1) {
|
||||||
for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) {
|
return -1; // ECANCELED because SIGTHR was just handled
|
||||||
if (pid == PROC_CONTAINER(e)->pid) {
|
}
|
||||||
pr = PROC_CONTAINER(e);
|
if (handler_was_called & SIG_HANDLED_NO_RESTART) {
|
||||||
|
return eintr(); // a non-SA_RESTART handler was called
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pr) {
|
|
||||||
unassert(!pr->iszombie);
|
// check for zombie to harvest
|
||||||
cv = &pr->onexit;
|
__proc_lock();
|
||||||
wv = &pr->waiters;
|
CheckForZombies:
|
||||||
} else {
|
int rc = __proc_check(pid, wstatus, rusage);
|
||||||
|
if (rc || (options & WNOHANG)) {
|
||||||
|
__proc_unlock();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's no zombies left
|
||||||
|
// check if there's any living processes
|
||||||
|
if (dll_is_empty(__proc.list)) {
|
||||||
|
__proc_unlock();
|
||||||
return echild();
|
return echild();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// wait for status change
|
// get appropriate wait object
|
||||||
if (options & WNOHANG) return 0;
|
// register ourself as waiting
|
||||||
WaitMore:
|
struct Proc *pr = 0;
|
||||||
deadline = GetNextDeadline(deadline);
|
uintptr_t hWaitObject;
|
||||||
SpuriousWakeup:
|
if (pid == -1) {
|
||||||
++*wv;
|
// wait for any status change
|
||||||
pthread_cleanup_push(UnwindWaiterCount, wv);
|
hWaitObject = __proc.haszombies;
|
||||||
m = __sig_begin(waitmask);
|
++__proc.waiters;
|
||||||
if ((rc = _check_signal(true)) != -1) {
|
} else {
|
||||||
rc = nsync_cv_wait_with_deadline(cv, &__proc.lock, deadline, 0);
|
// 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) {
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
||||||
struct rusage *opt_out_rusage) {
|
struct rusage *opt_out_rusage) {
|
||||||
int rc;
|
|
||||||
uint64_t m;
|
|
||||||
// no support for WCONTINUED and WUNTRACED yet
|
// no support for WCONTINUED and WUNTRACED yet
|
||||||
if (options & ~WNOHANG) return einval();
|
if (options & ~WNOHANG) return einval();
|
||||||
// XXX: NT doesn't really have process groups. For instance the
|
// 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.
|
// just does an "ignore ctrl-c" internally.
|
||||||
if (pid == 0) pid = -1;
|
if (pid == 0) pid = -1;
|
||||||
if (pid < -1) pid = -pid;
|
if (pid < -1) pid = -pid;
|
||||||
m = __sig_block();
|
sigset_t m = __sig_block();
|
||||||
__proc_lock();
|
int rc = __proc_wait(pid, opt_out_wstatus, options, opt_out_rusage,
|
||||||
pthread_cleanup_push((void *)__proc_unlock, 0);
|
m | 1ull << (SIGCHLD - 1));
|
||||||
rc = WaitForProcess(pid, opt_out_wstatus, options, opt_out_rusage,
|
|
||||||
m | 1ull << (SIGCHLD - 1));
|
|
||||||
pthread_cleanup_pop(true);
|
|
||||||
__sig_unblock(m);
|
__sig_unblock(m);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "libc/intrin/asancodes.h"
|
#include "libc/intrin/asancodes.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/dll.h"
|
#include "libc/intrin/dll.h"
|
||||||
|
#include "libc/intrin/getenv.internal.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/nt/files.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];
|
_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.
|
* 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);
|
atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed);
|
||||||
// TODO(jart): set_tid_address?
|
// 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
|
// initialize posix threads
|
||||||
_pthread_static.tib = tib;
|
_pthread_static.tib = tib;
|
||||||
_pthread_static.pt_flags = PT_STATIC;
|
_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,
|
if ((resources.handle = WSASocket(f->family, f->type, f->protocol, 0, 0,
|
||||||
kNtWsaFlagOverlapped)) == -1) {
|
kNtWsaFlagOverlapped)) == -1) {
|
||||||
client = __winsockerr();
|
client = __winsockerr();
|
||||||
goto WeFailed;
|
goto Finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
// accept network connection
|
// 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(
|
ssize_t bytes_received = __winsock_block(
|
||||||
resources.handle, 0, !!(f->flags & O_NONBLOCK), f->rcvtimeo, m,
|
resources.handle, 0, !!(f->flags & O_NONBLOCK), f->rcvtimeo, m,
|
||||||
sys_accept_nt_start, &(struct AcceptArgs){f->handle, &buffer});
|
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
|
// create file descriptor for new socket
|
||||||
// don't inherit the file open mode bits
|
// 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));
|
memcpy(addr, &buffer.remote.addr, sizeof(*addr));
|
||||||
g_fds.p[client].kind = kFdSocket;
|
g_fds.p[client].kind = kFdSocket;
|
||||||
|
|
||||||
WeFailed:
|
Finish:
|
||||||
pthread_cleanup_pop(false);
|
pthread_cleanup_pop(false);
|
||||||
__sig_unblock(m);
|
__sig_unblock(m);
|
||||||
if (client == -1 && errno == ECONNRESET) {
|
if (client == -1 && errno == ECONNRESET) {
|
||||||
|
|
|
@ -25,8 +25,14 @@
|
||||||
#include "libc/sock/internal.h"
|
#include "libc/sock/internal.h"
|
||||||
#include "libc/sock/syscall_fd.internal.h"
|
#include "libc/sock/syscall_fd.internal.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
|
#define _MSG_OOB 1
|
||||||
|
#define _MSG_PEEK 2
|
||||||
|
#define _MSG_WAITALL 8
|
||||||
|
#define _MSG_DONTWAIT 64
|
||||||
|
|
||||||
struct RecvArgs {
|
struct RecvArgs {
|
||||||
const struct iovec *iov;
|
const struct iovec *iov;
|
||||||
size_t iovlen;
|
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,
|
textwindows ssize_t sys_recv_nt(int fd, const struct iovec *iov, size_t iovlen,
|
||||||
uint32_t flags) {
|
uint32_t flags) {
|
||||||
|
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_PEEK | _MSG_WAITALL)) {
|
||||||
|
return einval();
|
||||||
|
}
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
struct Fd *f = g_fds.p + fd;
|
struct Fd *f = g_fds.p + fd;
|
||||||
sigset_t m = __sig_block();
|
sigset_t m = __sig_block();
|
||||||
rc = __winsock_block(f->handle, flags, !!(f->flags & O_NONBLOCK), f->rcvtimeo,
|
bool nonblock = !(flags & _MSG_WAITALL) &&
|
||||||
m, sys_recv_nt_start, &(struct RecvArgs){iov, iovlen});
|
((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);
|
__sig_unblock(m);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
* @param fd is the file descriptor returned by socket()
|
* @param fd is the file descriptor returned by socket()
|
||||||
* @param buf is where received network data gets copied
|
* @param buf is where received network data gets copied
|
||||||
* @param size is the byte capacity of buf
|
* @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
|
* @return number of bytes received, 0 on remote close, or -1 w/ errno
|
||||||
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
||||||
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
||||||
|
|
|
@ -24,9 +24,15 @@
|
||||||
#include "libc/nt/winsock.h"
|
#include "libc/nt/winsock.h"
|
||||||
#include "libc/sock/internal.h"
|
#include "libc/sock/internal.h"
|
||||||
#include "libc/sock/syscall_fd.internal.h"
|
#include "libc/sock/syscall_fd.internal.h"
|
||||||
|
#include "libc/sysv/consts/msg.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
|
#define _MSG_OOB 1
|
||||||
|
#define _MSG_PEEK 2
|
||||||
|
#define _MSG_DONTWAIT 64
|
||||||
|
|
||||||
struct RecvFromArgs {
|
struct RecvFromArgs {
|
||||||
const struct iovec *iov;
|
const struct iovec *iov;
|
||||||
size_t iovlen;
|
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,
|
size_t iovlen, uint32_t flags,
|
||||||
void *opt_out_srcaddr,
|
void *opt_out_srcaddr,
|
||||||
uint32_t *opt_inout_srcaddrsize) {
|
uint32_t *opt_inout_srcaddrsize) {
|
||||||
|
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_PEEK)) return einval();
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
struct Fd *f = g_fds.p + fd;
|
struct Fd *f = g_fds.p + fd;
|
||||||
sigset_t m = __sig_block();
|
sigset_t m = __sig_block();
|
||||||
rc = __winsock_block(f->handle, flags, !!(f->flags & O_NONBLOCK), f->rcvtimeo,
|
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
|
||||||
m, sys_recvfrom_nt_start,
|
flags &= ~_MSG_DONTWAIT;
|
||||||
|
rc = __winsock_block(f->handle, flags, nonblock, f->rcvtimeo, m,
|
||||||
|
sys_recvfrom_nt_start,
|
||||||
&(struct RecvFromArgs){iov, iovlen, opt_out_srcaddr,
|
&(struct RecvFromArgs){iov, iovlen, opt_out_srcaddr,
|
||||||
opt_inout_srcaddrsize});
|
opt_inout_srcaddrsize});
|
||||||
__sig_unblock(m);
|
__sig_unblock(m);
|
||||||
|
|
|
@ -34,15 +34,15 @@
|
||||||
/**
|
/**
|
||||||
* Receives data from network.
|
* 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 fd is the file descriptor returned by socket()
|
||||||
* @param buf is where received network data gets copied
|
* @param buf is where received network data gets copied
|
||||||
* @param size is the byte capacity of buf
|
* @param size is the byte capacity of buf
|
||||||
* @param flags is a bitmask which may contain any of the following:
|
* @param flags can have `MSG_OOB`, `MSG_PEEK`, and `MSG_DONTWAIT`
|
||||||
* - `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 opt_out_srcaddr receives the binary ip:port of the data's origin
|
* @param opt_out_srcaddr receives the binary ip:port of the data's origin
|
||||||
* @param opt_inout_srcaddrsize is srcaddr capacity which gets updated
|
* @param opt_inout_srcaddrsize is srcaddr capacity which gets updated
|
||||||
* @return number of bytes received, 0 on remote close, or -1 w/ errno
|
* @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/internal.h"
|
||||||
#include "libc/sock/syscall_fd.internal.h"
|
#include "libc/sock/syscall_fd.internal.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
|
#define _MSG_OOB 1
|
||||||
|
#define _MSG_DONTROUTE 4
|
||||||
|
#define _MSG_DONTWAIT 64
|
||||||
|
|
||||||
struct SendArgs {
|
struct SendArgs {
|
||||||
const struct iovec *iov;
|
const struct iovec *iov;
|
||||||
size_t iovlen;
|
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,
|
textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen,
|
||||||
uint32_t flags) {
|
uint32_t flags) {
|
||||||
|
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE)) return einval();
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
struct Fd *f = g_fds.p + fd;
|
struct Fd *f = g_fds.p + fd;
|
||||||
sigset_t m = __sig_block();
|
sigset_t m = __sig_block();
|
||||||
rc = __winsock_block(f->handle, flags, !!(f->flags & O_NONBLOCK), f->sndtimeo,
|
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
|
||||||
m, sys_send_nt_start, &(struct SendArgs){iov, iovlen});
|
flags &= ~_MSG_DONTWAIT;
|
||||||
|
rc = __winsock_block(f->handle, flags, nonblock, f->sndtimeo, m,
|
||||||
|
sys_send_nt_start, &(struct SendArgs){iov, iovlen});
|
||||||
__sig_unblock(m);
|
__sig_unblock(m);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
* @param fd is the file descriptor returned by socket()
|
* @param fd is the file descriptor returned by socket()
|
||||||
* @param buf is the data to send, which we'll copy if necessary
|
* @param buf is the data to send, which we'll copy if necessary
|
||||||
* @param size is the byte-length of buf
|
* @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
|
* @return number of bytes transmitted, or -1 w/ errno
|
||||||
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
||||||
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
||||||
|
|
|
@ -25,8 +25,13 @@
|
||||||
#include "libc/sock/internal.h"
|
#include "libc/sock/internal.h"
|
||||||
#include "libc/sock/syscall_fd.internal.h"
|
#include "libc/sock/syscall_fd.internal.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
|
#define _MSG_OOB 1
|
||||||
|
#define _MSG_DONTROUTE 4
|
||||||
|
#define _MSG_DONTWAIT 64
|
||||||
|
|
||||||
struct SendToArgs {
|
struct SendToArgs {
|
||||||
const struct iovec *iov;
|
const struct iovec *iov;
|
||||||
size_t iovlen;
|
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,
|
textwindows ssize_t sys_sendto_nt(int fd, const struct iovec *iov,
|
||||||
size_t iovlen, uint32_t flags,
|
size_t iovlen, uint32_t flags,
|
||||||
void *opt_in_addr, uint32_t in_addrsize) {
|
void *opt_in_addr, uint32_t in_addrsize) {
|
||||||
|
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE)) return einval();
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
struct Fd *f = g_fds.p + fd;
|
struct Fd *f = g_fds.p + fd;
|
||||||
sigset_t m = __sig_block();
|
sigset_t m = __sig_block();
|
||||||
|
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
|
||||||
|
flags &= ~_MSG_DONTWAIT;
|
||||||
rc = __winsock_block(
|
rc = __winsock_block(
|
||||||
f->handle, flags, !!(f->flags & O_NONBLOCK), f->sndtimeo, m,
|
f->handle, flags, nonblock, f->sndtimeo, m, sys_sendto_nt_start,
|
||||||
sys_sendto_nt_start,
|
|
||||||
&(struct SendToArgs){iov, iovlen, opt_in_addr, in_addrsize});
|
&(struct SendToArgs){iov, iovlen, opt_in_addr, in_addrsize});
|
||||||
__sig_unblock(m);
|
__sig_unblock(m);
|
||||||
return rc;
|
return rc;
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
* @param fd is the file descriptor returned by socket()
|
* @param fd is the file descriptor returned by socket()
|
||||||
* @param buf is the data to send, which we'll copy if necessary
|
* @param buf is the data to send, which we'll copy if necessary
|
||||||
* @param size is the byte-length of buf
|
* @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
|
* @param opt_addr is a binary ip:port destination override, which is
|
||||||
* mandatory for UDP if connect() wasn't called
|
* mandatory for UDP if connect() wasn't called
|
||||||
* @param addrsize is the byte-length of addr's true polymorphic form
|
* @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 │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/sig.internal.h"
|
#include "libc/calls/sig.internal.h"
|
||||||
#include "libc/calls/struct/fd.internal.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/calls/struct/sigset.internal.h"
|
|
||||||
#include "libc/errno.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/enum/wait.h"
|
||||||
#include "libc/nt/errors.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/struct/overlapped.h"
|
||||||
#include "libc/nt/thread.h"
|
#include "libc/nt/thread.h"
|
||||||
#include "libc/nt/winsock.h"
|
#include "libc/nt/winsock.h"
|
||||||
#include "libc/runtime/runtime.h"
|
|
||||||
#include "libc/sock/internal.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/consts/sicode.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#ifdef __x86_64__
|
#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
|
textwindows ssize_t
|
||||||
__winsock_block(int64_t handle, uint32_t flags, bool nonblock,
|
__winsock_block(int64_t handle, uint32_t flags, bool nonblock,
|
||||||
uint32_t srwtimeout, sigset_t waitmask,
|
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),
|
uint32_t *flags, void *arg),
|
||||||
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:
|
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 NtOverlapped overlap = {.hEvent = WSACreateEvent()};
|
||||||
struct WinsockBlockResources wbr = {handle, &overlap};
|
|
||||||
pthread_cleanup_push(UnwindWinsockBlock, &wbr);
|
|
||||||
rc = StartSocketOp(handle, &overlap, &flags, arg);
|
rc = StartSocketOp(handle, &overlap, &flags, arg);
|
||||||
if (rc && WSAGetLastError() == kNtErrorIoPending) {
|
if (rc && WSAGetLastError() == kNtErrorIoPending) {
|
||||||
if (nonblock) {
|
if (nonblock) {
|
||||||
CancelWinsockBlock(handle, &overlap);
|
CancelIoEx(handle, &overlap);
|
||||||
eagained = true;
|
reason = EAGAIN;
|
||||||
} else if (_check_cancel()) {
|
|
||||||
CancelWinsockBlock(handle, &overlap);
|
|
||||||
canceled = true;
|
|
||||||
} else if ((sig = __sig_get(waitmask))) {
|
|
||||||
CancelWinsockBlock(handle, &overlap);
|
|
||||||
} else {
|
} else {
|
||||||
|
struct PosixThread *pt;
|
||||||
pt = _pthread_self();
|
pt = _pthread_self();
|
||||||
pt->pt_blkmask = waitmask;
|
pt->pt_blkmask = waitmask;
|
||||||
pt->pt_iohandle = handle;
|
pt->pt_iohandle = handle;
|
||||||
|
@ -101,10 +61,13 @@ RestartOperation:
|
||||||
status = WSAWaitForMultipleEvents(1, &overlap.hEvent, 0,
|
status = WSAWaitForMultipleEvents(1, &overlap.hEvent, 0,
|
||||||
srwtimeout ? srwtimeout : -1u, 0);
|
srwtimeout ? srwtimeout : -1u, 0);
|
||||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||||
if (status == kNtWaitTimeout) {
|
if (status) {
|
||||||
// SO_RCVTIMEO or SO_SNDTIMEO elapsed
|
if (status == kNtWaitTimeout) {
|
||||||
CancelWinsockBlock(handle, &overlap);
|
reason = EAGAIN; // SO_RCVTIMEO or SO_SNDTIMEO elapsed
|
||||||
eagained = true;
|
} else {
|
||||||
|
reason = WSAGetLastError(); // ENETDOWN or ENOBUFS
|
||||||
|
}
|
||||||
|
CancelIoEx(handle, &overlap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
@ -114,30 +77,21 @@ RestartOperation:
|
||||||
? 0
|
? 0
|
||||||
: -1;
|
: -1;
|
||||||
}
|
}
|
||||||
pthread_cleanup_pop(false);
|
|
||||||
WSACloseEvent(overlap.hEvent);
|
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) {
|
if (!rc) {
|
||||||
errno = olderror;
|
|
||||||
return exchanged;
|
return exchanged;
|
||||||
}
|
}
|
||||||
if (WSAGetLastError() == kNtErrorOperationAborted) {
|
if (WSAGetLastError() == kNtErrorOperationAborted) {
|
||||||
if (eagained) return eagain();
|
if (reason) {
|
||||||
if (!handler_was_called && (sig = __sig_get(waitmask))) {
|
errno = reason;
|
||||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
return -1;
|
||||||
if (_check_cancel() == -1) return -1;
|
|
||||||
}
|
}
|
||||||
if (handler_was_called != 1) {
|
if ((sig = __sig_get(waitmask))) {
|
||||||
goto RestartOperation;
|
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();
|
return eintr();
|
||||||
}
|
}
|
||||||
|
|
|
@ -309,9 +309,9 @@ textstartup void __printargs(const char *prologue) {
|
||||||
PRINT("");
|
PRINT("");
|
||||||
PRINT("SIGNAL MASK %#lx", ss);
|
PRINT("SIGNAL MASK %#lx", ss);
|
||||||
if (ss) {
|
if (ss) {
|
||||||
for (i = 0; i < 32; ++i) {
|
for (i = 1; i <= NSIG; ++i) {
|
||||||
if (ss & (1u << i)) {
|
if (ss & (1ull << (i - 1))) {
|
||||||
PRINT(" ☼ %G (%d) is masked", i + 1, i + 1);
|
PRINT(" ☼ %G (%d) is masked", i, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -56,17 +56,14 @@ int iswseparator(wint_t);
|
||||||
wint_t towlower(wint_t);
|
wint_t towlower(wint_t);
|
||||||
wint_t towupper(wint_t);
|
wint_t towupper(wint_t);
|
||||||
|
|
||||||
void bzero(void *, size_t) memcpyesque;
|
|
||||||
void *memset(void *, int, size_t) memcpyesque;
|
void *memset(void *, int, size_t) memcpyesque;
|
||||||
void *memmove(void *, const void *, size_t) memcpyesque;
|
void *memmove(void *, const void *, size_t) memcpyesque;
|
||||||
void *memcpy(void *restrict, const void *restrict, size_t) memcpyesque;
|
void *memcpy(void *restrict, const void *restrict, size_t) memcpyesque;
|
||||||
void *mempcpy(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;
|
char *hexpcpy(char *restrict, const void *restrict, size_t) memcpyesque;
|
||||||
void *memccpy(void *restrict, const void *restrict, int, 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);
|
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 memcmp(const void *, const void *, size_t) strlenesque;
|
||||||
int timingsafe_bcmp(const void *, const void *, size_t);
|
int timingsafe_bcmp(const void *, const void *, size_t);
|
||||||
int timingsafe_memcmp(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(const char *, size_t) strlenesque;
|
||||||
size_t strnlen_s(const char *, size_t);
|
size_t strnlen_s(const char *, size_t);
|
||||||
char *strchr(const char *, int) strlenesque;
|
char *strchr(const char *, int) strlenesque;
|
||||||
char *index(const char *, int) strlenesque;
|
|
||||||
void *memchr(const void *, int, size_t) strlenesque;
|
void *memchr(const void *, int, size_t) strlenesque;
|
||||||
char *strchrnul(const char *, int) strlenesque returnsnonnull;
|
char *strchrnul(const char *, int) strlenesque returnsnonnull;
|
||||||
void *rawmemchr(const void *, 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;
|
char *__join_paths(char *, size_t, const char *, const char *) __wur;
|
||||||
#endif /* _COSMO_SOURCE */
|
#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_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
#endif /* COSMOPOLITAN_LIBC_STR_STR_H_ */
|
#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
|
// set long jump handler so pthread_exit can bring control back here
|
||||||
if (!setjmp(pt->pt_exiter)) {
|
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);
|
rc = pt->pt_start(pt->pt_arg);
|
||||||
// ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup
|
// ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup
|
||||||
unassert(!pt->pt_cleanup);
|
unassert(!pt->pt_cleanup);
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/runtime/sysconf.h"
|
#include "libc/runtime/sysconf.h"
|
||||||
|
#include "libc/stdio/sysparam.h"
|
||||||
#include "libc/sysv/consts/rlimit.h"
|
#include "libc/sysv/consts/rlimit.h"
|
||||||
#include "libc/sysv/consts/sa.h"
|
#include "libc/sysv/consts/sa.h"
|
||||||
#include "libc/sysv/consts/sig.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
|
// tune down the main process's stack size to a reasonable amount
|
||||||
// some operating systems, e.g. freebsd, will do things like have
|
// some operating systems, e.g. freebsd, will do things like have
|
||||||
// 500mb RLIMIT_STACK by default, even on machines with 400mb RAM
|
// 500mb RLIMIT_STACK by default, even on machines with 400mb RAM
|
||||||
struct rlimit rl = {2 * 1024 * 1024, 2 * 1024 * 1024};
|
|
||||||
if (!IsWindows() && !IsXnu()) {
|
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));
|
ASSERT_SYS(0, 0, setrlimit(RLIMIT_STACK, &rl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
#if 0
|
/*-*- 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│
|
||||||
│ To the extent possible under law, Justine Tunney has waived │
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
│ all copyright and related or neighboring rights to this file, │
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||||
│ as it is written in the following disclaimers: │
|
│ │
|
||||||
│ • http://unlicense.org/ │
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
╚─────────────────────────────────────────────────────────────────*/
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
#endif
|
│ │
|
||||||
|
│ 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/sigaction.h"
|
||||||
#include "libc/calls/struct/sigaltstack.h"
|
#include "libc/calls/struct/sigaltstack.h"
|
||||||
#include "libc/calls/struct/siginfo.h"
|
#include "libc/calls/struct/siginfo.h"
|
||||||
|
@ -29,10 +38,6 @@
|
||||||
* rewrite thread cpu state to call pthread_exit
|
* rewrite thread cpu state to call pthread_exit
|
||||||
* this method returns gracefully from signal handlers
|
* this method returns gracefully from signal handlers
|
||||||
* unfortunately it relies on cpu architecture knowledge
|
* 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;
|
volatile bool smashed_stack;
|
||||||
|
@ -47,11 +52,12 @@ void Exiter(void *rc) {
|
||||||
void CrashHandler(int sig, siginfo_t *si, void *arg) {
|
void CrashHandler(int sig, siginfo_t *si, void *arg) {
|
||||||
ucontext_t *ctx = arg;
|
ucontext_t *ctx = arg;
|
||||||
struct sigaltstack ss;
|
struct sigaltstack ss;
|
||||||
|
ASSERT_FALSE(smashed_stack);
|
||||||
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
ASSERT_EQ(SS_ONSTACK, ss.ss_flags);
|
ASSERT_EQ(SS_ONSTACK, ss.ss_flags);
|
||||||
kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr);
|
kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr);
|
||||||
smashed_stack = true;
|
smashed_stack = true;
|
||||||
ASSERT_TRUE(__is_stack_overflow(si, ctx));
|
// EXPECT_TRUE(__is_stack_overflow(si, ctx));
|
||||||
//
|
//
|
||||||
// the backtrace will look like this
|
// 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.PC = (long)Exiter;
|
||||||
ctx->uc_mcontext.SP += 32768;
|
ctx->uc_mcontext.SP += 32768;
|
||||||
ctx->uc_mcontext.SP &= -16;
|
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) {
|
int StackOverflow(int f(), int n) {
|
||||||
|
@ -96,7 +110,7 @@ void *MyPosixThread(void *arg) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
TEST(stackoverflow, standardStack_altStack_thread_teleport) {
|
||||||
void *res;
|
void *res;
|
||||||
pthread_t th;
|
pthread_t th;
|
||||||
struct sigaltstack ss;
|
struct sigaltstack ss;
|
||||||
|
@ -105,6 +119,7 @@ int main(int argc, char *argv[]) {
|
||||||
pthread_join(th, &res);
|
pthread_join(th, &res);
|
||||||
ASSERT_EQ((void *)123L, res);
|
ASSERT_EQ((void *)123L, res);
|
||||||
ASSERT_TRUE(smashed_stack);
|
ASSERT_TRUE(smashed_stack);
|
||||||
|
// this should be SS_DISABLE but ShowCrashReports() creates an alt stack
|
||||||
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
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_init(&spawnattr);
|
||||||
posix_spawnattr_setsigmask(&spawnattr, &savemask);
|
posix_spawnattr_setsigmask(&spawnattr, &savemask);
|
||||||
|
posix_spawnattr_setflags(&spawnattr,
|
||||||
|
POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETRLIMIT);
|
||||||
SetCpuLimit(cpuquota);
|
SetCpuLimit(cpuquota);
|
||||||
SetFszLimit(fszquota);
|
SetFszLimit(fszquota);
|
||||||
SetMemLimit(memquota);
|
SetMemLimit(memquota);
|
||||||
|
|
|
@ -552,7 +552,8 @@ RetryOnEtxtbsyRaceCondition:
|
||||||
started = timespec_real();
|
started = timespec_real();
|
||||||
pipe2(client->pipe, O_CLOEXEC);
|
pipe2(client->pipe, O_CLOEXEC);
|
||||||
posix_spawnattr_init(&spawnattr);
|
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_spawnattr_setsigmask(&spawnattr, &sigmask);
|
||||||
posix_spawn_file_actions_init(&spawnfila);
|
posix_spawn_file_actions_init(&spawnfila);
|
||||||
posix_spawn_file_actions_adddup2(&spawnfila, g_bogusfd, 0);
|
posix_spawn_file_actions_adddup2(&spawnfila, g_bogusfd, 0);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue