Make signal handling work well across platforms

- Fix sigsuspend() on XNU
- Fix strsignal() on non-Linux
- Add unit tests for strsignal()
- Add unit tests for setitimer()
- Add unit tests for sigsuspend()
- Rewrite setitimer() for New Technology
- Rewrite nanosleep() for New Technology
- Polyfill SIGALRM on the New Technology
- select(0,0,0,0) on NT now calls pause()
- Remove some NTDLL calls that aren't needed
- Polyfill SA_NOCLDWAIT on the New Technology
- Polyfill SA_RESETHAND on the New Technology
- Polyfill sigprocmask() on the New Technology
- Polyfill SIGCHLD+SIG_IGN on the New Technology
- Polyfill SA_RESTART masking on the New Technology
- Deliver console signals from main thread on New Technology
- Document SA_RESTART behavior w/ @sarestartable / @norestart
- System call trace in MODE=dbg now prints inherited FDs and signal mask
This commit is contained in:
Justine Tunney 2022-03-25 07:11:44 -07:00
parent 3b9e66ecba
commit 072e1d2910
82 changed files with 1388 additions and 450 deletions

View file

@ -18,6 +18,7 @@
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/timeval.h"
#include "libc/dce.h"
#include "libc/fmt/conv.h"
@ -39,9 +40,10 @@
* @see strftime(), gettimeofday()
* @asyncsignalsafe
*/
int clock_gettime(int clockid, struct timespec *ts) {
noinstrument int clock_gettime(int clockid, struct timespec *ts) {
int rc, e;
axdx_t ad;
char buf[45];
if (!ts) {
rc = efault();
} else if (IsAsan() && !__asan_is_valid(ts, sizeof(*ts))) {
@ -64,6 +66,9 @@ int clock_gettime(int clockid, struct timespec *ts) {
} else {
rc = sys_clock_gettime_nt(clockid, ts);
}
/* TODO(jart): Add get_clock_gettime() so we can STRACE() */
if (!__time_critical) {
STRACE("clock_gettime(%d, [%s]) → %d% m", clockid,
__strace_timespec(buf, sizeof(buf), rc, ts), rc);
}
return rc;
}

View file

@ -19,6 +19,7 @@
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
@ -47,8 +48,10 @@
* @param arg can be FD_CLOEXEC, etc. depending
* @return 0 on success, or -1 w/ errno
* @asyncsignalsafe
* @restartable
*/
int fcntl(int fd, int cmd, ...) {
int rc;
va_list va;
uintptr_t arg;
va_start(va, cmd);
@ -56,13 +59,15 @@ int fcntl(int fd, int cmd, ...) {
va_end(va);
if (fd >= 0) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
return weaken(__zipos_fcntl)(fd, cmd, arg);
rc = weaken(__zipos_fcntl)(fd, cmd, arg);
} else if (!IsWindows()) {
return sys_fcntl(fd, cmd, arg);
rc = sys_fcntl(fd, cmd, arg);
} else {
return sys_fcntl_nt(fd, cmd, arg);
rc = sys_fcntl_nt(fd, cmd, arg);
}
} else {
return einval();
rc = einval();
}
STRACE("fcntl(%d, %d, %p) → %d% m", fd, cmd, arg);
return rc;
}

View file

@ -29,6 +29,7 @@
* @param op can have LOCK_{SH,EX,NB,UN} for shared, exclusive,
* non-blocking, and unlocking
* @return 0 on success, or -1 w/ errno
* @restartable
*/
int flock(int fd, int op) {
int rc;

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
@ -16,10 +16,29 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/runtime/pc.internal.h"
#include "libc/macros.internal.h"
.text.windows
__winalarm_nt:
ezlea __winalarm,ax
jmp __nt2sysv
.endfn __winalarm_nt,globl,hidden
// fmod [sic] does (𝑥 rem 𝑦) w/ round()-style rounding.
//
// @param 𝑥 is an 80-bit long double passed on stack in 16-bytes
// @param 𝑦 is the power, also pushed on stack, in reverse order
// @return remainder ∈ (-|𝑦|,|𝑦|) in %st
// @define 𝑥-truncl(𝑥/𝑦)*𝑦
// @see emod()
fmodl: push %rbp
mov %rsp,%rbp
.profilable
fldt 32(%rbp)
fldt 16(%rbp)
1: fprem
fnstsw
test $FPU_C2>>8,%ah
jnz 1b
fstp %st(1)
pop %rbp
ret
1: int3
pop %rbp
ret
.endfn fmodl,globl

View file

@ -19,3 +19,4 @@
#include "libc/calls/internal.h"
unsigned __sighandrvas[NSIG];
unsigned __sighandflags[NSIG];

View file

@ -9,8 +9,6 @@ static inline int GetConsoleCtrlEvent(int sig) {
switch (sig) {
case SIGINT:
return kNtCtrlCEvent;
case SIGHUP:
return kNtCtrlCloseEvent;
case SIGQUIT:
return kNtCtrlBreakEvent;
default:

View file

@ -58,6 +58,7 @@ struct Fd {
unsigned flags;
int64_t handle;
int64_t extra;
bool zombie;
};
struct Fds {
@ -69,9 +70,10 @@ struct Fds {
extern const struct Fd kEmptyFd;
hidden extern volatile bool __interrupted;
hidden extern int __vforked;
hidden extern bool __time_critical;
hidden extern unsigned __sighandrvas[NSIG];
hidden extern unsigned __sighandflags[NSIG];
hidden extern struct Fds g_fds;
hidden extern const struct NtSecurityAttributes kNtIsInheritable;
@ -306,8 +308,10 @@ int ioctl_tiocgwinsz_nt(struct Fd *, struct winsize *) hidden;
cosmopolitan § syscalls » windows nt » support
*/
bool _check_sigchld(void) hidden;
int __sample_pids(int[hasatleast 64], int64_t[hasatleast 64]) hidden;
bool _check_interrupts(bool, struct Fd *) hidden;
void _check_sigchld(void) hidden;
void _check_sigalrm(void) hidden;
int __sample_pids(int[hasatleast 64], int64_t[hasatleast 64], bool) hidden;
bool isdirectory_nt(const char *) hidden;
bool isregularfile_nt(const char *) hidden;
bool issymlink_nt(const char *) hidden;
@ -327,7 +331,6 @@ struct NtOverlapped *offset2overlap(int64_t, struct NtOverlapped *) hidden;
unsigned __wincrash_nt(struct NtExceptionPointers *);
void *GetProcAddressModule(const char *, const char *) hidden;
void WinMainForked(void) hidden;
void __winalarm(void *, uint32_t, uint32_t) hidden;
void ntcontext2linux(struct ucontext *, const struct NtContext *) hidden;
/*───────────────────────────────────────────────────────────────────────────│─╗

View file

@ -0,0 +1,33 @@
/*-*- 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 2022 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/assert.h"
#include "libc/bits/weaken.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/dce.h"
textwindows bool _check_interrupts(bool restartable, struct Fd *fd) {
if (__time_critical) return false;
if (weaken(_check_sigalrm)) weaken(_check_sigalrm)();
if (weaken(_check_sigchld)) weaken(_check_sigchld)();
if (fd && weaken(_check_sigwinch)) weaken(_check_sigwinch)(fd);
return weaken(__sig_check) && weaken(__sig_check)(restartable);
}

View file

@ -25,6 +25,7 @@
/**
* Controls settings on device.
* @restartable
* @vforksafe
*/
int(ioctl)(int fd, uint64_t request, ...) {

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/getconsolectrlevent.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
@ -35,7 +36,7 @@ textwindows int sys_kill_nt(int pid, int sig) {
if (pid) {
pid = ABS(pid);
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
/* kill(pid, SIGINT|SIGHUP|SIGQUIT) */
/* kill(pid, SIGINT|SIGQUIT) */
if (__isfdkind(pid, kFdProcess)) {
ntpid = GetProcessId(g_fds.p[pid].handle);
} else if (!__isfdopen(pid)) {

View file

@ -16,33 +16,61 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#define ShouldUseMsabiAttribute() 1
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/errno.h"
#include "libc/macros.internal.h"
#include "libc/nt/errors.h"
#include "libc/nt/nt/time.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/errfuns.h"
textwindows noinstrument int sys_nanosleep_nt(const struct timespec *req,
struct timespec *rem) {
/* no function tracing because timing sensitive */
int64_t millis, hectonanos, relasleep;
if (rem) memcpy(rem, req, sizeof(*rem));
hectonanos = req->tv_sec * 10000000 + req->tv_nsec / 100;
hectonanos = MAX(1, hectonanos);
relasleep = -hectonanos;
if (NtError(__imp_NtDelayExecution(true, &relasleep))) {
millis = hectonanos / 10000;
millis = MAX(1, millis);
if (__imp_SleepEx(millis, true) == kNtWaitIoCompletion) {
errno = EINTR;
return -1;
int rc;
int64_t sec, nsec;
uint64_t ms, slice;
if (__builtin_mul_overflow(req->tv_sec, 1000, &ms) ||
__builtin_add_overflow(ms, req->tv_nsec / 1000000, &ms)) {
ms = -1;
}
if (!ms && (req->tv_sec || req->tv_nsec)) {
ms = 1;
}
rc = 0;
do {
if (!__time_critical && _check_interrupts(false, g_fds.p)) {
rc = eintr();
break;
}
if (ms > __SIG_POLLING_INTERVAL_MS) {
slice = __SIG_POLLING_INTERVAL_MS;
} else {
slice = ms;
}
if (!__time_critical) {
STRACE("SleepEx(%u, true)", slice);
}
if (SleepEx(slice, true) == kNtWaitIoCompletion) {
rc = eintr();
break;
}
ms -= slice;
} while (ms);
if (rem) {
sec = ms / 1000;
nsec = ms % 1000 * 1000000000;
rem->tv_nsec -= nsec;
if (rem->tv_nsec < 0) {
--rem->tv_sec;
rem->tv_nsec = 1000000000 - rem->tv_nsec;
}
rem->tv_sec -= sec;
if (rem->tv_sec < 0) {
rem->tv_sec = 0;
rem->tv_nsec = 0;
}
}
if (rem) {
rem->tv_sec = 0;
rem->tv_nsec = 0;
}
return 0;
return rc;
}

View file

@ -18,24 +18,35 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/sysv/errfuns.h"
/**
* Sleeps for a particular amount of time.
* @norestart
*/
int nanosleep(const struct timespec *req, struct timespec *rem) {
if (!req) return efault();
if (req->tv_sec < 0 || !(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) {
return einval();
}
if (!IsWindows() && !IsMetal() && !IsXnu()) {
return sys_nanosleep(req, rem);
noinstrument int nanosleep(const struct timespec *req, struct timespec *rem) {
int rc;
char buf[2][45];
if (!req) {
rc = efault();
} else if (req->tv_sec < 0 ||
!(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) {
rc = einval();
} else if (!IsWindows() && !IsMetal() && !IsXnu()) {
rc = sys_nanosleep(req, rem);
} else if (IsXnu()) {
return sys_nanosleep_xnu(req, rem);
rc = sys_nanosleep_xnu(req, rem);
} else if (IsMetal()) {
return enosys(); /* TODO: Sleep on Metal */
rc = enosys(); /* TODO: Sleep on Metal */
} else {
return sys_nanosleep_nt(req, rem);
rc = sys_nanosleep_nt(req, rem);
}
if (!__time_critical) {
STRACE("nanosleep(%s, [%s]) → %d% m",
__strace_timespec(buf[0], sizeof(buf[0]), rc, req),
__strace_timespec(buf[1], sizeof(buf[1]), rc, rem), rc);
}
return rc;
}

View file

@ -20,6 +20,8 @@
#include "libc/bits/initializer.internal.h"
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/rdtsc.h"
@ -40,17 +42,20 @@ static long double GetTimeSample(void) {
sched_yield();
time1 = dtime(CLOCK_REALTIME);
tick1 = rdtsc();
nanosleep(&(struct timespec){0, 100000}, NULL);
nanosleep(&(struct timespec){0, 1000000}, NULL);
time2 = dtime(CLOCK_REALTIME);
tick2 = rdtsc();
return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1);
}
static long double MeasureNanosPerCycle(void) {
bool tc;
int i, n;
long double avg, samp;
tc = __time_critical;
__time_critical = true;
if (IsWindows()) {
n = 20;
n = 10;
} else {
n = 5;
}
@ -58,6 +63,8 @@ static long double MeasureNanosPerCycle(void) {
samp = GetTimeSample();
avg += (samp - avg) / i;
}
__time_critical = tc;
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
return avg;
}

View file

@ -16,52 +16,25 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/pushpop.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/typedef/sigaction_f.h"
#include "libc/calls/sig.internal.h"
#include "libc/nt/enum/ctrlevent.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
textwindows bool32 __onntconsoleevent(uint32_t CtrlType) {
int sig;
unsigned rva;
siginfo_t info;
switch (CtrlType) {
textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) {
switch (dwCtrlType) {
case kNtCtrlCEvent:
STRACE("kNtCtrlCEvent");
sig = pushpop(SIGINT);
break;
__sig_add(SIGINT, SI_KERNEL);
return true;
case kNtCtrlBreakEvent:
STRACE("kNtCtrlBreakEvent");
sig = pushpop(SIGQUIT);
break;
__sig_add(SIGQUIT, SI_KERNEL);
return true;
case kNtCtrlCloseEvent:
STRACE("kNtCtrlCloseEvent");
sig = pushpop(SIGHUP);
break;
case kNtCtrlLogoffEvent: // only received by services so hack hack hack
case kNtCtrlShutdownEvent: // only received by services so hack hack hack
STRACE("kNtCtrlLogoffEvent");
sig = pushpop(SIGALRM);
break;
case kNtCtrlLogoffEvent: // only received by services
case kNtCtrlShutdownEvent: // only received by services
__sig_add(SIGHUP, SI_KERNEL);
return true;
default:
return false;
}
switch ((rva = __sighandrvas[sig])) {
case (uintptr_t)SIG_DFL:
_Exit(128 + sig);
case (uintptr_t)SIG_IGN:
return true;
default:
bzero(&info, sizeof(info));
info.si_signo = sig;
((sigaction_f)(_base + rva))(sig, &info, NULL);
__interrupted = true;
return true;
}
}

View file

@ -29,6 +29,7 @@
* ignored if O_CREAT or O_TMPFILE weren't passed
* @return number needing close(), or -1 w/ errno
* @asyncsignalsafe
* @restartable
* @vforksafe
*/
int open(const char *file, int flags, ...) {

View file

@ -20,28 +20,32 @@
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
/**
* Waits for signal.
* Waits for any signal.
*
* This suspends execution until an unmasked signal is delivered
* and its callback function has been called. It's a better idea
* to use sigsuspend() w/ sigprocmask() to avoid race conditions
* This suspends execution until an unmasked signal is delivered and its
* callback function has been called. The current signal mask is used.
*
* @return should always be -1 w/ EINTR
* @see sigsuspend()
* @norestart
*/
int pause(void) {
int rc, olderr;
sigset_t oldmask;
olderr = errno;
STRACE("pause()");
int e, rc;
sigset_t mask;
e = errno;
STRACE("pause() → [...]");
if ((rc = sys_pause()) == -1 && errno == ENOSYS) {
errno = olderr;
if (sigprocmask(SIG_BLOCK, NULL, &oldmask) == -1) return -1;
rc = sigsuspend(&oldmask);
errno = e;
if (sigprocmask(SIG_BLOCK, 0, &mask) == -1) return -1;
rc = sigsuspend(&mask);
}
STRACE("[...] pause → %d% m", rc);
return rc;
}

View file

@ -19,10 +19,12 @@
#include "libc/calls/calls.h"
#include "libc/calls/getconsolectrlevent.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
/**
@ -33,26 +35,28 @@
* @asyncsignalsafe
*/
int raise(int sig) {
int event;
STRACE("raise(%d)", sig);
int rc, event;
STRACE("raise(%d) → [...]", sig);
if (sig == SIGTRAP) {
DebugBreak();
return 0;
}
if (sig == SIGFPE) {
rc = 0;
} else if (sig == SIGFPE) {
volatile int x = 0;
x = 1 / x;
return 0;
}
if (!IsWindows()) {
return sys_kill(getpid(), sig, 1);
} else if ((event = GetConsoleCtrlEvent(sig))) {
if (GenerateConsoleCtrlEvent(event, 0)) {
return 0;
} else {
return __winerr();
}
rc = 0;
} else if (!IsWindows()) {
rc = sys_kill(getpid(), sig, 1);
} else {
_Exit(128 + sig);
if ((event = GetConsoleCtrlEvent(sig)) != -1) {
if (GenerateConsoleCtrlEvent(event, 0)) {
rc = 0;
} else {
rc = __winerr();
}
} else {
rc = __sig_add(sig, SI_USER);
}
}
STRACE("[...] raise → %d% m", rc);
return rc;
}

View file

@ -51,11 +51,7 @@ textwindows ssize_t sys_read_nt(struct Fd *fd, const struct iovec *iov,
ssize_t rc;
uint32_t size;
size_t i, total;
if (cmpxchg(&__interrupted, true, false) ||
(weaken(_check_sigchld) && weaken(_check_sigchld)()) ||
(weaken(_check_sigwinch) && weaken(_check_sigwinch)(fd))) {
return eintr();
}
if (_check_interrupts(true, fd)) return eintr();
while (iovlen && !iov[0].iov_len) iov++, iovlen--;
if (iovlen) {
for (total = i = 0; i < iovlen; ++i) {

View file

@ -38,6 +38,7 @@
* exception of size==0, in which case return zero means no error
* @see write(), pread(), readv()
* @asyncsignalsafe
* @restartable
*/
ssize_t read(int fd, void *buf, size_t size) {
ssize_t rc;

View file

@ -30,7 +30,7 @@
* Reads data to multiple buffers.
*
* @return number of bytes actually read, or -1 w/ errno
* @asyncsignalsafe
* @restartable
*/
ssize_t readv(int fd, const struct iovec *iov, int iovlen) {
ssize_t rc;

View file

@ -16,7 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/rand/lcg.internal.h"
/**
@ -28,13 +30,15 @@
*
* @return number of items returned in pids and handles
*/
int __sample_pids(int pids[hasatleast 64], int64_t handles[hasatleast 64]) {
textwindows int __sample_pids(int pids[hasatleast 64],
int64_t handles[hasatleast 64],
bool exploratory) {
static uint64_t rando = 1;
uint32_t i, j, base, count;
base = KnuthLinearCongruentialGenerator(&rando);
for (count = i = 0; i < g_fds.n; ++i) {
j = (base + i) % g_fds.n;
if (g_fds.p[j].kind == kFdProcess) {
if (g_fds.p[j].kind == kFdProcess && (!exploratory || !g_fds.p[j].zombie)) {
pids[count] = j;
handles[count] = g_fds.p[j].handle;
if (++count == 64) {

View file

@ -19,11 +19,16 @@
#include "libc/calls/internal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/ntdll.h"
#include "libc/nt/synchronization.h"
textwindows int sys_sched_yield_nt(void) {
size_t i;
if (NtYieldExecution() == kNtStatusDllNotFound) {
for (i = 0; i < 16; ++i) asm("pause");
}
// A value of zero, together with the bAlertable parameter set to
// FALSE, causes the thread to relinquish the remainder of its time
// slice to any other thread that is ready to run, if there are no
// pending user APCs on the calling thread. If there are no other
// threads ready to run and no user APCs are queued, the function
// returns immediately, and the thread continues execution.
// ──Quoth MSDN
SleepEx(0, false);
return 0;
}

View file

@ -16,20 +16,31 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/log/check.h"
#include "libc/math.h"
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/nexgen32e/nt2sysv.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/itimer.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/time/time.h"
/**
* @fileoverview Heartbreaking polyfill for SIGALRM on NT.
@ -45,59 +56,68 @@
* interrupting i/o operations on the standard input handle.
*/
static struct ItimerNt {
int64_t ith;
uint32_t tid;
struct itimerval itv;
} g_itimernt;
static bool __hastimer;
static bool __singleshot;
static long double __lastalrm;
static long double __interval;
static uint32_t ItimerWorker(void *arg) {
do {
if (!WaitForSingleObject(g_itimernt.ith, -1)) {
__winalarm(NULL, 0, 0);
textwindows void _check_sigalrm(void) {
// TODO(jart): use a different timing source
// TODO(jart): synchronize across intervals?
long double now, elapsed;
if (!__hastimer) return;
now = nowl();
elapsed = now - __lastalrm;
if (elapsed > __interval) {
__sig_add(SIGALRM, SI_TIMER);
if (__singleshot) {
__hastimer = false;
} else {
__lastalrm = now;
}
} while (g_itimernt.ith && g_itimernt.tid == GetCurrentThreadId());
return 0;
}
}
textwindows int sys_setitimer_nt(int which, const struct itimerval *newvalue,
struct itimerval *out_opt_oldvalue) {
int32_t period;
int64_t ith, duetime;
long double elapsed, untilnext;
if (which != ITIMER_REAL) return einval();
if (newvalue) {
if (newvalue->it_value.tv_sec && newvalue->it_value.tv_usec) {
if (!(ith = CreateWaitableTimer(NULL, false, NULL))) {
return __winerr();
}
duetime = -(newvalue->it_value.tv_sec * HECTONANOSECONDS +
newvalue->it_value.tv_usec * 10);
period = newvalue->it_value.tv_sec * 1000 +
div1000int64(newvalue->it_value.tv_usec);
if (!period && newvalue->it_value.tv_usec) period = 1;
if (!SetWaitableTimer(ith, &duetime, period, NULL, NULL, false)) {
errno = GetLastError();
CloseHandle(ith);
return -1;
}
} else {
ith = 0;
}
if (g_itimernt.ith) {
CloseHandle(g_itimernt.ith);
g_itimernt.ith = 0;
}
} else {
ith = 0;
}
if (out_opt_oldvalue) {
memcpy(out_opt_oldvalue, &g_itimernt.itv, sizeof(struct itimerval));
if (__hastimer) {
elapsed = nowl() - __lastalrm;
if (elapsed > __interval) {
untilnext = 0;
} else {
untilnext = __interval - elapsed;
}
out_opt_oldvalue->it_interval.tv_sec = __interval;
out_opt_oldvalue->it_interval.tv_usec = 1 / 1e6 * fmodl(__interval, 1);
out_opt_oldvalue->it_value.tv_sec = untilnext;
out_opt_oldvalue->it_value.tv_usec = 1 / 1e6 * fmodl(untilnext, 1);
} else {
out_opt_oldvalue->it_interval.tv_sec = 0;
out_opt_oldvalue->it_interval.tv_usec = 0;
out_opt_oldvalue->it_value.tv_sec = 0;
out_opt_oldvalue->it_value.tv_usec = 0;
}
}
if (ith) {
g_itimernt.ith = ith;
memcpy(&g_itimernt.itv, newvalue, sizeof(struct itimerval));
CloseHandle(
CreateThread(NULL, STACKSIZE, ItimerWorker, NULL, 0, &g_itimernt.tid));
if (newvalue) {
if (newvalue->it_interval.tv_sec || newvalue->it_interval.tv_usec ||
newvalue->it_value.tv_sec || newvalue->it_value.tv_usec) {
__hastimer = true;
if (newvalue->it_interval.tv_sec || newvalue->it_interval.tv_usec) {
__singleshot = false;
__interval = newvalue->it_interval.tv_sec +
1 / 1e6 * newvalue->it_interval.tv_usec;
} else {
__singleshot = true;
__interval =
newvalue->it_value.tv_sec + 1 / 1e6 * newvalue->it_value.tv_usec;
}
__lastalrm = nowl();
} else {
__hastimer = false;
}
}
return 0;
}

220
libc/calls/sig.c Normal file
View file

@ -0,0 +1,220 @@
/*-*- 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 2022 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/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/typedef/sigaction_f.h"
#include "libc/macros.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
/**
* @fileoverview UNIX signals for the New Technology.
* @threadsafe
*/
#define LOCK \
for (;;) \
if (lockcmpxchg(&__sig.lock, false, true)) { \
asm volatile("" ::: "memory")
#define UNLOCK \
asm volatile("" ::: "memory"); \
__sig.lock = false; \
break; \
} \
else sched_yield()
struct Signal {
struct Signal *next;
bool used;
int sig;
int si_code;
};
struct Signals {
bool lock;
sigset_t mask;
struct Signal *queue;
struct Signal mem[__SIG_QUEUE_LENGTH];
};
struct Signals __sig;
/**
* Allocates piece of memory for storing pending signal.
*/
static textwindows struct Signal *__sig_alloc(void) {
int i;
for (i = 0; i < ARRAYLEN(__sig.mem); ++i) {
if (!__sig.mem[i].used && lockcmpxchg(&__sig.mem[i].used, false, true)) {
return __sig.mem + i;
}
}
return 0;
}
/**
* Returns signal memory to static pool.
*/
static textwindows void __sig_free(struct Signal *mem) {
mem->used = false;
}
/**
* Dequeues signal that isn't masked.
* @return signal or null if empty or none unmasked
*/
static textwindows struct Signal *__sig_remove(void) {
struct Signal *prev, *res;
if (__sig.queue) {
LOCK;
for (prev = 0, res = __sig.queue; res; prev = res, res = res->next) {
if (!sigismember(&__sig.mask, res->sig)) {
if (res == __sig.queue) {
__sig.queue = res->next;
} else if (prev) {
prev->next = res->next;
}
res->next = 0;
break;
} else {
STRACE("%s is masked", strsignal(res->sig));
}
}
UNLOCK;
} else {
res = 0;
}
return res;
}
/**
* Delivers signal.
* @note called from main thread
* @return true if EINTR should be returned by caller
*/
static textwindows bool __sig_deliver(bool restartable, struct Signal *sig,
unsigned rva) {
unsigned flags;
siginfo_t info;
flags = __sighandflags[sig->sig];
// TODO(jart): polyfill prevention of re-entry
if (flags & SA_RESETHAND) {
__sighandrvas[sig->sig] = (int32_t)(intptr_t)SIG_DFL;
}
STRACE("delivering %s", strsignal(sig->sig));
bzero(&info, sizeof(info));
info.si_signo = sig->sig;
info.si_code = sig->si_code;
((sigaction_f)(_base + rva))(sig->sig, &info, 0);
if (!restartable) {
return true; // always send EINTR for wait4(), poll(), etc.
} else if (flags & SA_RESTART) {
STRACE("restarting syscall on %s", strsignal(sig->sig));
return false; // resume syscall for read(), write(), etc.
} else {
return true; // default course is to raise EINTR
}
}
/**
* Returns true if signal default action is to end process.
*/
static textwindows bool __sig_isfatal(int sig) {
return sig != SIGCHLD;
}
/**
* Enqueues generic signal for delivery on New Technology.
* @threadsafe
*/
textwindows int __sig_add(int sig, int si_code) {
struct Signal *mem;
if (1 <= sig && sig <= NSIG) {
if ((mem = __sig_alloc())) {
mem->sig = sig;
mem->si_code = si_code;
LOCK;
mem->next = __sig.queue;
__sig.queue = mem;
UNLOCK;
return 0;
} else {
return enomem();
}
} else {
return einval();
}
}
/**
* Enqueues generic signal for delivery on New Technology.
*
* @param restartable is for functions like read() but not poll()
* @return true if EINTR should be returned by caller
* @note called from main thread
* @threadsafe
*/
textwindows bool __sig_check(bool restartable) {
unsigned rva;
bool delivered;
struct Signal *sig;
delivered = false;
while ((sig = __sig_remove())) {
switch ((rva = __sighandrvas[sig->sig])) {
case (intptr_t)SIG_DFL:
if (__sig_isfatal(sig->sig)) {
STRACE("terminating on %s", strsignal(sig->sig));
__restorewintty();
_Exit(128 + sig->sig);
}
// fallthrough
case (intptr_t)SIG_IGN:
STRACE("ignoring %s", strsignal(sig->sig));
break;
default:
delivered = __sig_deliver(restartable, sig, rva);
break;
}
__sig_free(sig);
}
return delivered;
}
/**
* Changes signal mask for main thread.
* @return old mask
*/
textwindows sigset_t __sig_mask(const sigset_t *neu) {
sigset_t old;
LOCK;
old = __sig.mask;
if (neu) __sig.mask = *neu;
UNLOCK;
return old;
}

17
libc/calls/sig.internal.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
#include "libc/calls/struct/sigset.h"
#define __SIG_QUEUE_LENGTH 8
#define __SIG_POLLING_INTERVAL_MS 50
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
bool __sig_check(bool) hidden;
int __sig_add(int, int) hidden;
sigset_t __sig_mask(const sigset_t *) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ */

View file

@ -219,6 +219,7 @@ static int __sigaction(int sig, const struct sigaction *act,
}
if (act) {
__sighandrvas[sig] = rva;
__sighandflags[sig] = act->sa_flags;
}
}
return rc;
@ -232,6 +233,7 @@ static int __sigaction(int sig, const struct sigaction *act,
* .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO};
* CHECK_NE(-1, sigaction(SIGINT, &sa, NULL));
*
* @return 0 on success or -1 w/ errno
* @see xsigaction() for a much better api
* @asyncsignalsafe
* @vforksafe

View file

@ -16,40 +16,46 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/typedef/sigaction_f.h"
#include "libc/nt/enum/status.h"
#include "libc/dce.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
/**
* Checks to see if SIGCHLD should be raised on Windows.
* @return true if a signal was raised
* @note yoinked by fork-nt.c
*/
bool _check_sigchld(void) {
void _check_sigchld(void) {
siginfo_t si;
int pids[64];
uint32_t i, n;
int64_t handles[64];
if (__sighandrvas[SIGCHLD] < kSigactionMinRva) return false;
if (!(n = __sample_pids(pids, handles))) return false;
if (!(n = __sample_pids(pids, handles, true))) return;
i = WaitForMultipleObjects(n, handles, false, 0);
if (i == kNtWaitTimeout) return false;
if (i == kNtWaitTimeout) return;
if (i == kNtWaitFailed) {
STRACE("%s failed %u", "WaitForMultipleObjects", GetLastError());
return false;
return;
}
STRACE("SIGCHLD fd=%d handle=%ld", pids[i], handles[i]);
bzero(&si, sizeof(si));
si.si_signo = SIGCHLD;
si.si_code = CLD_EXITED;
si.si_pid = pids[i];
((sigaction_f)(_base + __sighandrvas[SIGCHLD]))(SIGCHLD, &si, 0);
return true;
if (__sighandrvas[SIGCHLD] == (intptr_t)SIG_IGN) {
STRACE("killing zombie fd=%d handle=%ld", pids[i], handles[i]);
CloseHandle(handles[i]);
__releasefd(pids[i]);
return;
}
if (__sighandflags[SIGCHLD] & SA_NOCLDWAIT) {
STRACE("SIGCHILD SA_NOCLDWAIT fd=%d handle=%ld", pids[i], handles[i]);
CloseHandle(handles[i]);
__releasefd(pids[i]);
}
g_fds.p[pids[i]].zombie = true;
__sig_add(SIGCHLD, CLD_EXITED);
}

View file

@ -19,6 +19,8 @@
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
@ -39,7 +41,7 @@ static const char *DescribeHow(char buf[12], int how) {
}
/**
* Changes program signal blocking state, e.g.:
* Changes signal blocking state of calling thread, e.g.:
*
* sigset_t neu,old;
* sigfillset(&neu);
@ -51,56 +53,45 @@ static const char *DescribeHow(char buf[12], int how) {
* @param oldset will receive the old mask (optional) and can't overlap
* @return 0 on success, or -1 w/ errno
* @asyncsignalsafe
* @restartable
* @vforksafe
*/
int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
int x, rc;
sigset_t old;
char howbuf[12];
char buf[2][41];
sigset_t old, *oldp;
const sigset_t *arg;
if (!(IsAsan() &&
((opt_set && !__asan_is_valid(opt_set, sizeof(*opt_set))) ||
(opt_out_oldset &&
!__asan_is_valid(opt_out_oldset, sizeof(*opt_out_oldset)))))) {
if (!IsWindows() && !IsOpenbsd()) {
if (opt_out_oldset) {
bzero(&old, sizeof(old));
oldp = &old;
} else {
oldp = 0;
}
if (sys_sigprocmask(how, opt_set, oldp, 8) != -1) {
if (opt_out_oldset) {
memcpy(opt_out_oldset, &old, sizeof(old));
}
rc = 0;
} else {
rc = -1;
}
} else if (IsOpenbsd()) {
if (opt_set) {
/* openbsd only supports 32 signals so it passses them in a reg */
arg = (sigset_t *)(uintptr_t)(*(uint32_t *)opt_set);
} else {
how = 1; /* SIG_BLOCK */
arg = 0; /* changes nothing */
}
if ((x = sys_sigprocmask(how, arg, 0, 0)) != -1) {
if (opt_out_oldset) {
bzero(opt_out_oldset, sizeof(*opt_out_oldset));
memcpy(opt_out_oldset, &x, sizeof(x));
}
rc = 0;
} else {
rc = -1;
}
} else {
if (opt_out_oldset) bzero(opt_out_oldset, sizeof(*opt_out_oldset));
rc = 0; /* TODO(jart): Implement me! */
}
} else {
int res, rc, arg1;
const sigset_t *arg2;
sigemptyset(&old);
if (IsAsan() &&
((opt_set && !__asan_is_valid(opt_set, sizeof(*opt_set))) ||
(opt_out_oldset &&
!__asan_is_valid(opt_out_oldset, sizeof(*opt_out_oldset))))) {
rc = efault();
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsNetbsd()) {
rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0, 8);
} else if (IsOpenbsd()) {
if (opt_set) {
// openbsd only supports 32 signals so it passses them in a reg
arg1 = how;
arg2 = (sigset_t *)(uintptr_t)(*(uint32_t *)opt_set);
} else {
arg1 = how; // SIG_BLOCK
arg2 = 0; // changes nothing
}
if ((res = sys_sigprocmask(arg1, arg2, 0, 0)) != -1) {
memcpy(&old, &res, sizeof(res));
rc = 0;
} else {
rc = -1;
}
} else { // windows or metal
old = __sig_mask(opt_set);
_check_interrupts(false, 0);
rc = 0;
}
if (rc != -1 && opt_out_oldset) {
*opt_out_oldset = old;
}
STRACE("sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(howbuf, how),
__strace_sigset(buf[0], sizeof(buf[0]), 0, opt_set),

View file

@ -18,32 +18,64 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/errfuns.h"
/**
* Blocks until SIG MASK is delivered to process.
*
* @param ignore is a bitset of signals to block temporarily
* @return -1 w/ EINTR
* @return -1 w/ EINTR or possibly EFAULT
* @asyncsignalsafe
* @norestart
*/
int sigsuspend(const sigset_t *ignore) {
int rc;
char buf[41];
if (!ignore || (IsAsan() && !__asan_is_valid(ignore, sizeof(*ignore)))) {
sigset_t save, mask, *arg;
STRACE("sigsuspend(%s) → [...]",
__strace_sigset(buf, sizeof(buf), 0, ignore));
if (IsAsan() && ignore && !__asan_is_valid(ignore, sizeof(*ignore))) {
rc = efault();
} else if (!IsWindows()) {
STRACE("sigsuspend(%s)", __strace_sigset(buf, sizeof(buf), 0, ignore));
if (IsOpenbsd()) ignore = (sigset_t *)(uintptr_t)(*(uint32_t *)ignore);
return sys_sigsuspend(ignore, 8);
} else if (IsXnu() || IsOpenbsd()) {
// openbsd and xnu only support 32 signals
// they use a register calling convention for sigsuspend
if (ignore) {
arg = (sigset_t *)(uintptr_t)(*(uint32_t *)ignore);
} else {
arg = 0;
}
rc = sys_sigsuspend(arg, 8);
} else if (IsLinux() || IsFreebsd() || IsNetbsd() || IsWindows()) {
if (ignore) {
arg = ignore;
} else {
sigemptyset(&mask);
arg = &mask;
}
if (!IsWindows()) {
rc = sys_sigsuspend(arg, 8);
} else {
save = __sig_mask(arg);
do {
if (_check_interrupts(false, g_fds.p)) {
rc = eintr();
break;
}
SleepEx(__SIG_POLLING_INTERVAL_MS, true);
} while (1);
__sig_mask(&save);
}
} else {
rc = enosys(); /* TODO(jart): Implement me! */
// TODO(jart): sigsuspend metal support
rc = enosys();
}
STRACE("sigsuspend(%s) → %d% m", __strace_sigset(buf, sizeof(buf), 0, ignore),
rc);
STRACE("[...] sigsuspend → %d% m", rc);
return rc;
}

View file

@ -16,18 +16,23 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/winsize.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/nt/struct/consolescreenbufferinfoex.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
static struct winsize __ws;
textwindows bool _check_sigwinch(struct Fd *fd) {
textwindows void _check_sigwinch(struct Fd *fd) {
int e;
siginfo_t si;
struct winsize ws, old;
@ -39,14 +44,7 @@ textwindows bool _check_sigwinch(struct Fd *fd) {
if (old.ws_col != ws.ws_col || old.ws_row != ws.ws_row) {
__ws = ws;
if (old.ws_col | old.ws_row) {
STRACE("SIGWINCH %hhu×%hhu → %hhu×%hhu", old.ws_col, old.ws_row,
ws.ws_col, ws.ws_row);
if (__sighandrvas[SIGWINCH] >= kSigactionMinRva) {
bzero(&si, sizeof(si));
si.si_signo = SIGWINCH;
((sigaction_f)(_base + __sighandrvas[SIGWINCH]))(SIGWINCH, &si, 0);
return true;
}
__sig_add(SIGWINCH, SI_KERNEL);
}
}
} else {
@ -56,5 +54,4 @@ textwindows bool _check_sigwinch(struct Fd *fd) {
}
}
}
return false;
}

View file

@ -16,17 +16,17 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/sysv/errfuns.h"
#include "libc/time/time.h"
/**
* Sleeps for a particular amount of time.
* @asyncsignalsafe
* @norestart
*/
int sleep(uint32_t seconds) {
int rc;
rc = nanosleep(&(struct timespec){seconds, 0}, NULL);
STRACE("sleep(%u) → %d% m", seconds, rc);
return rc;
return nanosleep(&(struct timespec){seconds, 0}, NULL);
}

View file

@ -28,6 +28,7 @@ const char *__strace_sigaction(char *, size_t, int, const struct sigaction *);
const char *__strace_sigset(char[41], size_t, int, const sigset_t *);
const char *__strace_rlimit_name(int);
const char *__strace_rlimit(char[41], size_t, int, const struct rlimit *);
const char *__strace_timespec(char[45], size_t, int, const struct timespec *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -1,7 +1,7 @@
/*-*- 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 2020 Justine Alexandra Roberts Tunney
Copyright 2021 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
@ -16,20 +16,14 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/typedef/sigaction_f.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sig.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/intrin/kprintf.h"
void __winalarm(void *lpArgToCompletionRoutine, uint32_t dwTimerLowValue,
uint32_t dwTimerHighValue) {
int rva;
siginfo_t info;
bzero(&info, sizeof(info));
info.si_signo = SIGALRM;
rva = __sighandrvas[SIGALRM];
if (rva >= kSigactionMinRva) {
((sigaction_f)(_base + rva))(SIGALRM, &info, NULL);
}
privileged const char *__strace_timespec(char buf[45], size_t bufsize, int rc,
const struct timespec *ts) {
if (rc == -1) return "n/a";
if (!ts) return "NULL";
ksnprintf(buf, bufsize, "{%ld, %ld}", ts->tv_sec, ts->tv_nsec);
return buf;
}

View file

@ -24,7 +24,7 @@ COSMOPOLITAN_C_START_
void _init_onntconsoleevent(void);
void _init_wincrash(void);
bool _check_sigwinch();
void _check_sigwinch();
#ifndef __SIGACTION_YOINK
#define __SIGACTION_YOINK(SIG) \

View file

@ -1,7 +1,7 @@
/*-*- 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 2021 Justine Alexandra Roberts Tunney
Copyright 2022 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
@ -18,4 +18,4 @@
*/
#include "libc/calls/internal.h"
volatile bool __interrupted;
bool __time_critical;

View file

@ -56,17 +56,33 @@ int uname(struct utsname *lool) {
} else {
bzero(tmp, sizeof(tmp));
if (!IsWindows()) {
if ((rc = sys_uname(tmp)) != -1) {
out = (char *)lool;
for (i = j = 0;;) {
len = strlen(&tmp[j]);
if (len >= sizeof(struct utsname) - i) break;
memcpy(&out[i], &tmp[j], len + 1);
i += SYS_NMLN;
j += len;
while (j < sizeof(tmp) && tmp[j] == '\0') ++j;
if (j == sizeof(tmp)) break;
if (IsLinux() || IsFreebsd()) {
if ((rc = sys_uname(tmp)) != -1) {
out = (char *)lool;
for (i = j = 0;;) {
len = strlen(&tmp[j]);
if (len >= sizeof(struct utsname) - i) break;
memcpy(&out[i], &tmp[j], len + 1);
i += SYS_NMLN;
j += len;
while (j < sizeof(tmp) && tmp[j] == '\0') ++j;
if (j == sizeof(tmp)) break;
}
}
} else if (IsXnu()) {
strcpy(lool->sysname, "XNU's Not UNIX!");
gethostname_bsd(lool->nodename, sizeof(lool->nodename));
rc = 0;
} else if (IsOpenbsd()) {
strcpy(lool->sysname, "OpenBSD");
gethostname_bsd(lool->nodename, sizeof(lool->nodename));
rc = 0;
} else if (IsNetbsd()) {
strcpy(lool->sysname, "NetBSD");
gethostname_bsd(lool->nodename, sizeof(lool->nodename));
rc = 0;
} else {
rc = enosys();
}
} else {
v = NtGetVersion();

View file

@ -16,18 +16,18 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/sysv/errfuns.h"
#include "libc/time/time.h"
/**
* Sleeps for particular amount of microseconds.
* @norestart
*/
int usleep(uint32_t microseconds) {
int rc;
rc = nanosleep(&(struct timespec){(uint64_t)microseconds / 1000000,
(uint64_t)microseconds % 1000000 * 1000},
NULL);
STRACE("usleep(%'u) → %d% m", microseconds, rc);
return rc;
return nanosleep(&(struct timespec){(uint64_t)microseconds / 1000000,
(uint64_t)microseconds % 1000000 * 1000},
NULL);
}

View file

@ -25,6 +25,7 @@
* may be inspected using WEEXITSTATUS(), etc.
* @return process id of terminated child or -1 w/ errno
* @asyncsignalsafe
* @restartable
* @vforksafe
*/
int wait(int *opt_out_wstatus) {

View file

@ -30,6 +30,7 @@
* @param opt_out_rusage optionally returns accounting data
* @return process id of terminated child or -1 w/ errno
* @asyncsignalsafe
* @restartable
*/
int wait3(int *opt_out_wstatus, int options, struct rusage *opt_out_rusage) {
return wait4(-1, opt_out_wstatus, options, opt_out_rusage);

View file

@ -19,6 +19,7 @@
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/rusage.h"
#include "libc/fmt/conv.h"
@ -34,6 +35,7 @@
#include "libc/nt/struct/processmemorycounters.h"
#include "libc/nt/synchronization.h"
#include "libc/rand/lcg.internal.h"
#include "libc/runtime/ezmap.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
@ -46,9 +48,11 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
int64_t handle;
int64_t handles[64];
uint32_t dwExitCode;
bool shouldinterrupt;
uint32_t i, j, base, count, timeout;
struct NtProcessMemoryCountersEx memcount;
struct NtFileTime createfiletime, exitfiletime, kernelfiletime, userfiletime;
if (_check_interrupts(true, g_fds.p)) return eintr();
if (pid != -1 && pid != 0) {
if (pid < 0) {
/* XXX: this is sloppy */
@ -75,10 +79,11 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
pids[0] = pid;
count = 1;
} else {
count = __sample_pids(pids, handles);
count = __sample_pids(pids, handles, false);
if (!count) return echild();
}
for (;;) {
if (_check_interrupts(true, 0)) return eintr();
dwExitCode = kNtStillActive;
if (options & WNOHANG) {
i = WaitForMultipleObjects(count, handles, false, 0);

View file

@ -35,6 +35,7 @@
* @param opt_out_rusage optionally returns accounting data
* @return process id of terminated child or -1 w/ errno
* @asyncsignalsafe
* @restartable
*/
int wait4(int pid, int *opt_out_wstatus, int options,
struct rusage *opt_out_rusage) {

View file

@ -28,6 +28,7 @@
* @param options can have WNOHANG, WUNTRACED, WCONTINUED, etc.
* @return process id of terminated child or -1 w/ errno
* @asyncsignalsafe
* @restartable
*/
int waitpid(int pid, int *opt_out_wstatus, int options) {
return wait4(pid, opt_out_wstatus, options, NULL);

View file

@ -27,6 +27,7 @@
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/overlapped.h"
#include "libc/runtime/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
@ -35,6 +36,7 @@ static textwindows ssize_t sys_write_nt_epipe(int fd) {
siginfo_t info;
STRACE("WriteFile(%d:%p) → %m", fd, g_fds.p[fd].handle);
if (!__sighandrvas[SIGPIPE]) {
__restorewintty();
_Exit(128 + SIGPIPE);
} else if (__sighandrvas[SIGPIPE] >= kSigactionMinRva) {
bzero(&info, sizeof(info));

View file

@ -36,6 +36,7 @@
* impossible unless size was passed as zero to do an error check
* @see read(), pwrite(), writev(), SIGPIPE
* @asyncsignalsafe
* @restartable
*/
ssize_t write(int fd, const void *buf, size_t size) {
ssize_t rc;

View file

@ -34,6 +34,7 @@
* call using write().
*
* @return number of bytes actually handed off, or -1 w/ errno
* @restartable
*/
ssize_t writev(int fd, const struct iovec *iov, int iovlen) {
ssize_t rc;