Implement support for POSIX thread cancellations

This change makes some miracle modifications to the System Five system
call support, which lets us have safe, correct, and atomic handling of
thread cancellations. It all turned out to be cheaper than anticipated
because it wasn't necessary to modify the system call veneers. We were
able to encode the cancellability of each system call into the magnums
found in libc/sysv/syscalls.sh. Since cancellations are so waq, we are
also supporting a lovely Musl Libc mask feature for raising ECANCELED.
This commit is contained in:
Justine Tunney 2022-11-04 01:04:43 -07:00
parent 37d40e087f
commit 2278327eba
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
145 changed files with 715 additions and 265 deletions

View file

@ -204,6 +204,7 @@ textwindows int __sig_raise(int sig, int si_code) {
if (1 <= sig && sig <= 64) {
if (!__sig_is_masked(sig)) {
++__sig_count;
// TODO(jart): ucontext_t support
__sig_handle(false, sig, si_code, 0);
return 0;
} else {

View file

@ -34,7 +34,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
for (;;) {
if (sys_clock_gettime_nt(clock, &now)) return -1;
if (_timespec_gte(now, abs)) return 0;
if (_check_interrupts(false, g_fds.p)) return eintr();
if (_check_interrupts(false, g_fds.p)) return -1;
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
_timespec_tomillis(_timespec_sub(abs, now))),
false);
@ -47,7 +47,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
if (_timespec_gte(now, abs)) return 0;
if (_check_interrupts(false, g_fds.p)) {
if (rem) *rem = _timespec_sub(abs, now);
return eintr();
return -1;
}
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
_timespec_tomillis(_timespec_sub(abs, now))),

View file

@ -80,6 +80,7 @@
* @raise EINVAL if `flags` has an unrecognized value
* @raise EINVAL if `req->tv_nsec [0,1000000000)`
* @raise ENOSYS on bare metal
* @cancellationpoint
* @returnserrno
* @norestart
*/

View file

@ -26,7 +26,7 @@
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
int sys_fcntl(int fd, int cmd, uintptr_t arg) {
int sys_fcntl(int fd, int cmd, uintptr_t arg, int impl(int, int, ...)) {
int e, rc;
bool islock;
if ((islock = cmd == F_GETLK || //
@ -40,12 +40,12 @@ int sys_fcntl(int fd, int cmd, uintptr_t arg) {
cosmo2flock(arg);
}
e = errno;
rc = __sys_fcntl(fd, cmd, arg);
rc = impl(fd, cmd, arg);
if (islock) {
flock2cosmo(arg);
} else if (rc == -1 && cmd == F_DUPFD_CLOEXEC && errno == EINVAL) {
errno = e;
rc = __fixupnewfd(__sys_fcntl(fd, F_DUPFD, arg), O_CLOEXEC);
rc = __fixupnewfd(impl(fd, F_DUPFD, arg), O_CLOEXEC);
}
return rc;
}

View file

@ -94,6 +94,7 @@
* @raise EDEADLK if `cmd` was `F_SETLKW` and waiting would deadlock
* @raise EMFILE if `cmd` is `F_DUPFD` or `F_DUPFD_CLOEXEC` and
* `RLIMIT_NOFILE` would be exceeded
* @cancellationpoint when `cmd` is `F_SETLKW`
* @asyncsignalsafe
* @restartable
*/
@ -111,7 +112,11 @@ int fcntl(int fd, int cmd, ...) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = _weaken(__zipos_fcntl)(fd, cmd, arg);
} else if (!IsWindows()) {
rc = sys_fcntl(fd, cmd, arg);
if (cmd == F_SETLKW) {
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl_cp);
} else {
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl);
}
} else {
rc = sys_fcntl_nt(fd, cmd, arg);
}

View file

@ -30,6 +30,7 @@
* @return 0 on success, or -1 w/ errno
* @see sync(), fsync(), sync_file_range()
* @see __nosync to secretly disable
* @cancellationpoint
* @asyncsignalsafe
*/
int fdatasync(int fd) {

View file

@ -17,10 +17,10 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
/**
* Acquires lock on file.
@ -30,6 +30,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
* @cancellationpoint
* @restartable
*/
int flock(int fd, int op) {

View file

@ -18,16 +18,17 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/struct/statfs-meta.internal.h"
#include "libc/calls/struct/statfs.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/errfuns.h"
/**
* Returns information about filesystem.
* @return 0 on success, or -1 w/ errno
* @cancellationpoint
*/
int fstatfs(int fd, struct statfs *sf) {
int rc;

View file

@ -30,6 +30,7 @@
* @return 0 on success, or -1 w/ errno
* @see fdatasync(), sync_file_range()
* @see __nosync to secretly disable
* @cancellationpoint
* @asyncsignalsafe
*/
int fsync(int fd) {

View file

@ -56,7 +56,7 @@
* @raise EINVAL if `fd` is a non-file, e.g. pipe, socket
* @raise EINVAL if `fd` wasn't opened in a writeable mode
* @raise ENOSYS on bare metal
* @see truncate()
* @cancellationpoint
* @asyncsignalsafe
* @threadsafe
*/

View file

@ -43,7 +43,7 @@ forceinline size_t _clampio(size_t size) {
}
int sys_close_nt(struct Fd *, int) hidden;
bool _check_interrupts(bool, struct Fd *) hidden;
int _check_interrupts(bool, struct Fd *) hidden;
int sys_openat_metal(int, const char *, int, unsigned);
COSMOPOLITAN_C_END_

View file

@ -18,24 +18,33 @@
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/lockcmpxchgp.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
textwindows bool _check_interrupts(bool restartable, struct Fd *fd) {
bool res;
textwindows int _check_interrupts(bool restartable, struct Fd *fd) {
int rc;
if (_weaken(pthread_testcancel_np) &&
(rc = _weaken(pthread_testcancel_np)())) {
errno = rc;
return -1;
}
if (_weaken(_check_sigalrm)) _weaken(_check_sigalrm)();
if (!__tls_enabled || !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
if (_weaken(_check_sigchld)) _weaken(_check_sigchld)();
if (fd && _weaken(_check_sigwinch)) _weaken(_check_sigwinch)(fd);
}
res = _weaken(__sig_check) && _weaken(__sig_check)(restartable);
return res;
if (_weaken(__sig_check) && _weaken(__sig_check)(restartable)) return eintr();
return 0;
}

View file

@ -32,6 +32,7 @@
* @raise EFAULT if `req` is NULL or `req` / `rem` is a bad pointer
* @raise ENOSYS on bare metal
* @see clock_nanosleep()
* @cancellationpoint
* @norestart
*/
int nanosleep(const struct timespec *req, struct timespec *rem) {

View file

@ -21,6 +21,8 @@
#include "libc/nt/struct/context.h"
#include "libc/str/str.h"
// TODO(jart): uc_sigmask support
privileged void _ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) {
if (!cr) return;
ctx->uc_mcontext.eflags = cr->EFlags;

View file

@ -29,6 +29,7 @@
* @param file specifies filesystem path to open
* @return file descriptor, or -1 w/ errno
* @see openat() for further documentation
* @cancellationpoint
* @asyncsignalsafe
* @restartable
* @threadsafe

View file

@ -141,6 +141,7 @@
* @raise ELOOP if `flags` had `O_NOFOLLOW` and `file` is a symbolic link
* @raise ELOOP if a loop was detected resolving components of `file`
* @raise EISDIR if writing is requested and `file` names a directory
* @cancellationpoint
* @asyncsignalsafe
* @restartable
* @threadsafe

View file

@ -18,8 +18,9 @@
*/
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/errors.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/errfuns.h"
@ -31,7 +32,7 @@ textwindows int sys_pause_nt(void) {
for (;;) {
if (_check_interrupts(false, g_fds.p)) {
return eintr();
return -1;
}
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {

View file

@ -17,9 +17,9 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sock/internal.h"
/**
@ -37,6 +37,7 @@
* However this has a tinier footprint and better logging.
*
* @return -1 w/ errno set to EINTR
* @cancellationpoint
* @see sigsuspend()
* @norestart
*/

View file

@ -21,7 +21,6 @@
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/errno.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
@ -64,8 +63,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
if (sigmask) {
__sig_mask(SIG_SETMASK, sigmask, &oldmask);
}
if (_check_interrupts(false, g_fds.p)) {
rc = eintr();
if ((rc = _check_interrupts(false, g_fds.p))) {
goto ReturnPath;
}
@ -190,8 +188,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
}
// otherwise loop limitlessly for timeout to elapse while
// checking for signal delivery interrupts, along the way
if (_check_interrupts(false, g_fds.p)) {
rc = eintr();
if ((rc = _check_interrupts(false, g_fds.p))) {
goto ReturnPath;
}
}

View file

@ -55,6 +55,7 @@
* @return fds[𝑖].revents is always zero initializaed and then will
* be populated with POLL{IN,OUT,PRI,HUP,ERR,NVAL} if something
* was determined about the file descriptor
* @cancellationpoint
* @asyncsignalsafe
* @threadsafe
* @norestart

View file

@ -50,6 +50,7 @@
*
* @param timeout if null will block indefinitely
* @param sigmask may be null in which case no mask change happens
* @cancellationpoint
* @asyncsignalsafe
* @threadsafe
* @norestart

View file

@ -48,6 +48,7 @@
* @raise EIO if a complicated i/o error happened
* @raise EINTR if signal was delivered instead
* @see pwrite(), write()
* @cancellationpoint
* @asyncsignalsafe
* @threadsafe
* @vforksafe

View file

@ -108,6 +108,7 @@ static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
* Reads with maximum generality.
*
* @return number of bytes actually read, or -1 w/ errno
* @cancellationpoint
* @asyncsignalsafe
* @vforksafe
*/

View file

@ -42,6 +42,7 @@
* @return [1..size] bytes on success, or -1 w/ errno; noting zero is
* impossible unless size was passed as zero to do an error check
* @see pread(), write()
* @cancellationpoint
* @asyncsignalsafe
* @threadsafe
* @vforksafe

View file

@ -114,6 +114,7 @@ static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen,
* call using pwrite().
*
* @return number of bytes actually sent, or -1 w/ errno
* @cancellationpoint
* @asyncsignalsafe
* @vforksafe
*/

View file

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

View file

@ -56,6 +56,7 @@
* or `SO_RCVTIMEO` is in play and the time interval elapsed
* @raise ENOBUFS is specified by POSIX
* @raise ENXIO is specified by POSIX
* @cancellationpoint
* @asyncsignalsafe
* @restartable
* @vforksafe

View file

@ -43,6 +43,7 @@
* performance boost in the case of a single small iovec.
*
* @return number of bytes actually read, or -1 w/ errno
* @cancellationpoint
* @restartable
*/
ssize_t readv(int fd, const struct iovec *iov, int iovlen) {

View file

@ -17,15 +17,10 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/sigset.h"
#include "libc/str/str.h"
/**
* Blocks all signals without strace logging.
*
* @param neu is new signal mask for process
* @return old signal mask
*/
sigset_t _sigblockall(void) {
sigset_t ss;
sigfillset(&ss);
memset(&ss, -1, sizeof(ss));
return _sigsetmask(ss);
}

View file

@ -75,6 +75,11 @@ privileged void __sigenter_netbsd(int sig, struct siginfo_netbsd *si,
uc.uc_mcontext.rsp = ctx->uc_mcontext.rsp;
*uc.uc_mcontext.fpregs = ctx->uc_mcontext.__fpregs;
((sigaction_f)(_base + rva))(sig, &si2, &uc);
ctx->uc_stack.ss_sp = uc.uc_stack.ss_sp;
ctx->uc_stack.ss_size = uc.uc_stack.ss_size;
ctx->uc_stack.ss_flags = uc.uc_stack.ss_flags;
__repmovsb(&ctx->uc_sigmask, &uc.uc_sigmask,
MIN(sizeof(uc.uc_sigmask), sizeof(ctx->uc_sigmask)));
ctx->uc_mcontext.rdi = uc.uc_mcontext.rdi;
ctx->uc_mcontext.rsi = uc.uc_mcontext.rsi;
ctx->uc_mcontext.rdx = uc.uc_mcontext.rdx;

View file

@ -76,6 +76,7 @@ privileged void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo,
*g.uc.uc_mcontext.fpregs = *ctx->sc_fpstate;
}
((sigaction_f)(_base + rva))(sig, &g.si, &g.uc);
ctx->sc_mask = g.uc.uc_sigmask.__bits[0];
ctx->sc_rdi = g.uc.uc_mcontext.rdi;
ctx->sc_rsi = g.uc.uc_mcontext.rsi;
ctx->sc_rdx = g.uc.uc_mcontext.rdx;

View file

@ -501,6 +501,7 @@ privileged void __sigenter_xnu(void *fn, int infostyle, int sig,
xnuctx->uc_stack.ss_sp = g.uc.uc_stack.ss_sp;
xnuctx->uc_stack.ss_flags = g.uc.uc_stack.ss_flags;
xnuctx->uc_stack.ss_size = g.uc.uc_stack.ss_size;
xnuctx->uc_sigmask = g.uc.uc_sigmask.__bits[0];
if (xnuctx->uc_mcontext) {
if (xnuctx->uc_mcsize >=
sizeof(struct __darwin_x86_exception_state64)) {

View file

@ -26,6 +26,7 @@
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
@ -59,7 +60,9 @@ int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
rc = efault();
} else if (IsMetal() || IsWindows()) {
rc = __sig_mask(how, opt_set, &old);
_check_interrupts(false, 0);
if (_weaken(__sig_check)) {
_weaken(__sig_check)(true);
}
} else {
rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0);
}

View file

@ -23,12 +23,6 @@
#include "libc/dce.h"
#include "libc/sysv/consts/sig.h"
/**
* Sets signal mask without strace logging.
*
* @param neu is new signal mask for process
* @return old signal mask
*/
sigset_t _sigsetmask(sigset_t neu) {
sigset_t res;
if (IsMetal() || IsWindows()) {

View file

@ -40,6 +40,7 @@
* @param ignore is a bitset of signals to block temporarily, which if
* NULL is equivalent to passing an empty signal set
* @return -1 w/ EINTR (or possibly EFAULT)
* @cancellationpoint
* @asyncsignalsafe
* @norestart
*/
@ -72,8 +73,7 @@ int sigsuspend(const sigset_t *ignore) {
ms = 0;
totoms = 0;
do {
if (_check_interrupts(false, g_fds.p)) {
rc = eintr();
if ((rc = _check_interrupts(false, g_fds.p))) {
break;
}
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {

View file

@ -40,6 +40,7 @@
* @raise EAGAIN if deadline expired
* @raise ENOSYS on Windows, XNU, OpenBSD, Metal
* @raise EFAULT if invalid memory was supplied
* @cancellationpoint
*/
int sigtimedwait(const sigset_t *set, siginfo_t *info,
const struct timespec *timeout) {

View file

@ -27,6 +27,7 @@
* @raise EINTR if an asynchronous signal was delivered instead
* @raise ENOSYS on OpenBSD, XNU, and Windows
* @see sigtimedwait()
* @cancellationpoint
*/
int sigwaitinfo(const sigset_t *mask, siginfo_t *si) {
return sigtimedwait(mask, si, 0);

View file

@ -29,6 +29,7 @@
* this function shall return the number of unslept seconds rounded
* using the ceiling function
* @see clock_nanosleep()
* @cancellationpoint
* @asyncsignalsafe
* @norestart
*/

View file

@ -18,17 +18,18 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/struct/statfs-meta.internal.h"
#include "libc/calls/struct/statfs.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/consts/at.h"
/**
* Returns information about filesystem.
* @return 0 on success, or -1 w/ errno
* @cancellationpoint
*/
int statfs(const char *path, struct statfs *sf) {
int rc;

View file

@ -18,6 +18,7 @@ char *sys_getcwd_xnu(char *, u64) hidden;
i32 __sys_dup3(i32, i32, i32) hidden;
i32 __sys_execve(const char *, char *const[], char *const[]) hidden;
i32 __sys_fcntl(i32, i32, ...) hidden;
i32 __sys_fcntl_cp(i32, i32, ...) hidden;
i32 __sys_fstat(i32, void *) hidden;
i32 __sys_fstatat(i32, const char *, void *, i32) hidden;
i32 __sys_gettid(i64 *) hidden;
@ -43,7 +44,7 @@ i32 sys_fchmod(i32, u32) hidden;
i32 sys_fchmodat(i32, const char *, u32, u32) hidden;
i32 sys_fchown(i64, u32, u32) hidden;
i32 sys_fchownat(i32, const char *, u32, u32, u32) hidden;
i32 sys_fcntl(i32, i32, u64) hidden;
i32 sys_fcntl(i32, i32, u64, i32 (*)(i32, i32, ...)) hidden;
i32 sys_fdatasync(i32) hidden;
i32 sys_flock(i32, i32) hidden;
i32 sys_fork(void) hidden;
@ -59,6 +60,7 @@ i32 sys_getresuid(u32 *, u32 *, u32 *) hidden;
i32 sys_getsid(i32) hidden;
i32 sys_gettid(void) hidden;
i32 sys_ioctl(i32, u64, ...) hidden;
i32 sys_ioctl_cp(i32, u64, ...) hidden;
i32 sys_issetugid(void) hidden;
i32 sys_kill(i32, i32, i32) hidden;
i32 sys_linkat(i32, const char *, i32, const char *, i32) hidden;

View file

@ -40,6 +40,7 @@ static textwindows int sys_tcdrain_nt(int fd) {
* @raise EIO if process group of writer is orphoned, calling thread is
* not blocking `SIGTTOU`, and process isn't ignoring `SIGTTOU`
* @raise ENOSYS on bare metal
* @cancellationpoint
* @asyncsignalsafe
*/
int tcdrain(int fd) {
@ -47,7 +48,7 @@ int tcdrain(int fd) {
if (IsMetal()) {
rc = enosys();
} else if (!IsWindows()) {
rc = sys_ioctl(fd, TCSBRK, (void *)(intptr_t)1);
rc = sys_ioctl_cp(fd, TCSBRK, (void *)(intptr_t)1);
} else {
rc = sys_tcdrain_nt(fd);
}

View file

@ -58,6 +58,7 @@
* @raise ETXTBSY if `path` is an executable being executed
* @raise EROFS if `path` is on a read-only filesystem
* @raise ENOSYS on bare metal
* @cancellationpoint
* @see ftruncate()
* @threadsafe
*/

View file

@ -27,6 +27,7 @@
* @return 0 on success, or -1 w/ errno
* @raise EINTR if a signal was delivered while sleeping
* @see clock_nanosleep()
* @cancellationpoint
* @norestart
*/
int usleep(uint32_t micros) {

View file

@ -24,6 +24,7 @@
* @param opt_out_wstatus optionally returns status code, and *wstatus
* may be inspected using WEEXITSTATUS(), etc.
* @return process id of terminated child or -1 w/ errno
* @cancellationpoint
* @asyncsignalsafe
* @restartable
* @vforksafe

View file

@ -27,6 +27,7 @@
* @param options can have WNOHANG, WUNTRACED, WCONTINUED, etc.
* @param opt_out_rusage optionally returns accounting data
* @return process id of terminated child or -1 w/ errno
* @cancellationpoint
* @asyncsignalsafe
* @restartable
*/

View file

@ -48,15 +48,15 @@
static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus,
int options,
struct rusage *opt_out_rusage) {
int pids[64];
int64_t handle;
int rc, pids[64];
int64_t handles[64];
uint32_t dwExitCode;
bool shouldinterrupt;
uint32_t i, j, base, count, timeout;
struct NtProcessMemoryCountersEx memcount;
struct NtFileTime createfiletime, exitfiletime, kernelfiletime, userfiletime;
if (_check_interrupts(true, g_fds.p)) return eintr();
if (_check_interrupts(true, g_fds.p)) return -1;
__fds_lock();
if (pid != -1 && pid != 0) {
if (pid < 0) {
@ -94,7 +94,7 @@ static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus,
}
__fds_unlock();
for (;;) {
if (_check_interrupts(true, 0)) return eintr();
if (_check_interrupts(true, 0)) return -1;
dwExitCode = kNtStillActive;
if (options & WNOHANG) {
i = WaitForMultipleObjects(count, handles, false, 0);

View file

@ -17,11 +17,11 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/struct/rusage.internal.h"
#include "libc/calls/wait4.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/errfuns.h"
/**
@ -34,6 +34,7 @@
* @param options can have WNOHANG, WUNTRACED, WCONTINUED, etc.
* @param opt_out_rusage optionally returns accounting data
* @return process id of terminated child or -1 w/ errno
* @cancellationpoint
* @asyncsignalsafe
* @restartable
*/

View file

@ -27,6 +27,7 @@
* may be inspected using WEXITSTATUS(), etc.
* @param options can have WNOHANG, WUNTRACED, WCONTINUED, etc.
* @return process id of terminated child or -1 w/ errno
* @cancellationpoint
* @asyncsignalsafe
* @restartable
*/

View file

@ -57,6 +57,7 @@
* as a general possibility; whereas other system don't specify it
* @raise ENXIO is specified only by POSIX and XNU when a request is
* made of a nonexistent device or outside device capabilities
* @cancellationpoint
* @asyncsignalsafe
* @restartable
* @vforksafe

View file

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