From 2278327eba9132810ae5faeb20fa7d5f0907bd73 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 4 Nov 2022 01:04:43 -0700 Subject: [PATCH] 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. --- Makefile | 2 +- libc/calls/__sig2.c | 1 + libc/calls/clock_nanosleep-nt.c | 4 +- libc/calls/clock_nanosleep.c | 1 + libc/calls/fcntl-sysv.c | 6 +- libc/calls/fcntl.c | 7 +- libc/calls/fdatasync.c | 1 + libc/calls/flock.c | 3 +- libc/calls/fstatfs.c | 3 +- libc/calls/fsync.c | 1 + libc/calls/ftruncate.c | 2 +- libc/calls/internal.h | 2 +- libc/calls/interrupts-nt.c | 17 ++- libc/calls/nanosleep.c | 1 + libc/calls/ntcontext2linux.c | 2 + libc/calls/open.c | 1 + libc/calls/openat.c | 1 + libc/calls/pause-nt.c | 5 +- libc/calls/pause.c | 3 +- libc/calls/poll-nt.c | 7 +- libc/calls/poll.c | 1 + libc/calls/ppoll.c | 1 + libc/calls/pread.c | 1 + libc/calls/preadv.c | 1 + libc/calls/pwrite.c | 1 + libc/calls/pwritev.c | 1 + libc/calls/read-nt.c | 4 +- libc/calls/read.c | 1 + libc/calls/readv.c | 1 + libc/calls/sigblockall.c | 9 +- libc/calls/sigenter-netbsd.c | 5 + libc/calls/sigenter-openbsd.c | 1 + libc/calls/sigenter-xnu.c | 1 + libc/calls/sigprocmask.c | 5 +- libc/calls/sigsetmask.c | 6 - libc/calls/sigsuspend.c | 4 +- libc/calls/sigtimedwait.c | 1 + libc/calls/sigwaitinfo.c | 1 + libc/calls/sleep.c | 1 + libc/calls/statfs.c | 3 +- libc/calls/syscall-sysv.internal.h | 4 +- libc/calls/tcdrain.c | 3 +- libc/calls/truncate.c | 1 + libc/calls/usleep.c | 1 + libc/calls/wait.c | 1 + libc/calls/wait3.c | 1 + libc/calls/wait4-nt.c | 6 +- libc/calls/wait4.c | 3 +- libc/calls/waitpid.c | 1 + libc/calls/write.c | 1 + libc/calls/writev.c | 1 + libc/intrin/ksignalnames.S | 1 + libc/intrin/sigaddset.c | 7 +- libc/intrin/sigisprecious.inc | 1 + libc/runtime/msync.c | 1 + libc/sock/accept-nt.c | 4 +- libc/sock/accept.c | 1 + libc/sock/accept4.c | 1 + libc/sock/connect.c | 1 + libc/sock/epoll.c | 1 + libc/sock/pselect.c | 7 +- libc/sock/recv-nt.c | 2 +- libc/sock/recvfrom-nt.c | 2 +- libc/sock/recvfrom.c | 1 + libc/sock/recvmsg.c | 1 + libc/sock/select.c | 7 +- libc/sock/send-nt.c | 2 +- libc/sock/sendfile.c | 3 +- libc/sock/sendmsg.c | 1 + libc/sock/sendto-nt.c | 2 +- libc/sock/sendto.c | 1 + libc/sock/wsablock.c | 3 +- libc/stdio/dirstream.c | 33 ++-- libc/stdio/getrandom.c | 1 + libc/stdio/popen.c | 10 +- libc/stdio/posix_spawn.c | 5 + libc/stdio/system.c | 6 + libc/sysv/calls/__sys_accept.s | 2 +- libc/sysv/calls/__sys_accept4.s | 2 +- libc/sysv/calls/__sys_connect.s | 2 +- libc/sysv/calls/__sys_fcntl_cp.s | 2 + libc/sysv/calls/__sys_openat.s | 2 +- libc/sysv/calls/__sys_wait4.s | 2 +- libc/sysv/calls/posix_fallocate.s | 2 +- libc/sysv/calls/sys_clock_nanosleep.s | 2 +- libc/sysv/calls/sys_epoll_wait.s | 2 +- libc/sysv/calls/sys_fallocate.s | 2 +- libc/sysv/calls/sys_fdatasync.s | 2 +- libc/sysv/calls/sys_flock.s | 2 +- libc/sysv/calls/sys_fstatfs.s | 2 +- libc/sysv/calls/sys_fsync.s | 2 +- libc/sysv/calls/sys_ftruncate.s | 2 +- libc/sysv/calls/sys_getrandom.s | 2 +- libc/sysv/calls/sys_ioctl_cp.s | 2 + libc/sysv/calls/sys_msgrcv.s | 2 +- libc/sysv/calls/sys_msgsnd.s | 2 +- libc/sysv/calls/sys_msync.s | 2 +- libc/sysv/calls/sys_nanosleep.s | 2 +- libc/sysv/calls/sys_open.s | 2 +- libc/sysv/calls/sys_poll.s | 2 +- libc/sysv/calls/sys_ppoll.s | 2 +- libc/sysv/calls/sys_pread.s | 2 +- libc/sysv/calls/sys_preadv.s | 2 +- libc/sysv/calls/sys_pselect.s | 2 +- libc/sysv/calls/sys_pwrite.s | 2 +- libc/sysv/calls/sys_pwritev.s | 2 +- libc/sysv/calls/sys_read.s | 2 +- libc/sysv/calls/sys_readv.s | 2 +- libc/sysv/calls/sys_recvfrom.s | 2 +- libc/sysv/calls/sys_recvmsg.s | 2 +- libc/sysv/calls/sys_select.s | 2 +- libc/sysv/calls/sys_sendmsg.s | 2 +- libc/sysv/calls/sys_sendto.s | 2 +- libc/sysv/calls/sys_sigsuspend.s | 2 +- libc/sysv/calls/sys_sigtimedwait.s | 2 +- libc/sysv/calls/sys_statfs.s | 2 +- libc/sysv/calls/sys_truncate.s | 2 +- libc/sysv/calls/sys_waitid.s | 2 +- libc/sysv/calls/sys_write.s | 2 +- libc/sysv/calls/sys_writev.s | 2 +- libc/sysv/consts.sh | 3 +- libc/sysv/consts/EDEVERR.s | 2 +- libc/sysv/consts/SIGCANCEL.s | 2 + libc/sysv/consts/sig.h | 2 + libc/sysv/syscalls.sh | 98 ++++++------ libc/sysv/systemfive.S | 77 ++++++---- libc/thread/posixthread.internal.h | 20 ++- libc/thread/pthread_cancel.c | 204 ++++++++++++++++++++----- libc/thread/pthread_cond_timedwait.c | 1 + libc/thread/pthread_cond_wait.c | 1 + libc/thread/pthread_create.c | 4 +- libc/thread/pthread_join.c | 3 + libc/thread/pthread_setcancelstate.c | 31 +++- libc/thread/pthread_setcanceltype.c | 16 +- libc/thread/sem_timedwait.c | 1 + libc/thread/sem_wait.c | 1 + libc/thread/thread.h | 3 + libc/thread/thread.mk | 5 - libc/zipos/open.c | 8 + test/libc/calls/sigaction_test.c | 28 ++++ test/libc/intrin/describesigset_test.c | 14 +- test/libc/intrin/strsignal_r_test.c | 1 - test/libc/thread/pthread_cancel_test.c | 111 ++++++++++++++ third_party/musl/lockf.c | 5 + third_party/nsync/futex.c | 6 +- 145 files changed, 715 insertions(+), 265 deletions(-) create mode 100644 libc/sysv/calls/__sys_fcntl_cp.s create mode 100644 libc/sysv/calls/sys_ioctl_cp.s create mode 100644 libc/sysv/consts/SIGCANCEL.s create mode 100644 test/libc/thread/pthread_cancel_test.c diff --git a/Makefile b/Makefile index 073ba83d3..0e492dc50 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ SHELL = build/bootstrap/cocmd.com HOSTS ?= freebsd openbsd netbsd rhel7 rhel5 xnu win10 win10:31336 -MAKEFLAGS += -j --no-builtin-rules +MAKEFLAGS += --no-builtin-rules .SUFFIXES: .DELETE_ON_ERROR: diff --git a/libc/calls/__sig2.c b/libc/calls/__sig2.c index cf820f486..a6ea8d4eb 100644 --- a/libc/calls/__sig2.c +++ b/libc/calls/__sig2.c @@ -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 { diff --git a/libc/calls/clock_nanosleep-nt.c b/libc/calls/clock_nanosleep-nt.c index e25605f8d..8aa89a1a9 100644 --- a/libc/calls/clock_nanosleep-nt.c +++ b/libc/calls/clock_nanosleep-nt.c @@ -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))), diff --git a/libc/calls/clock_nanosleep.c b/libc/calls/clock_nanosleep.c index 8b93f0740..faa004dff 100644 --- a/libc/calls/clock_nanosleep.c +++ b/libc/calls/clock_nanosleep.c @@ -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 */ diff --git a/libc/calls/fcntl-sysv.c b/libc/calls/fcntl-sysv.c index 2cfbf8d0c..42957aa84 100644 --- a/libc/calls/fcntl-sysv.c +++ b/libc/calls/fcntl-sysv.c @@ -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; } diff --git a/libc/calls/fcntl.c b/libc/calls/fcntl.c index 06369e9ba..f27beb4e9 100644 --- a/libc/calls/fcntl.c +++ b/libc/calls/fcntl.c @@ -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); } diff --git a/libc/calls/fdatasync.c b/libc/calls/fdatasync.c index 5153eb5e9..3fff9e44f 100644 --- a/libc/calls/fdatasync.c +++ b/libc/calls/fdatasync.c @@ -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) { diff --git a/libc/calls/flock.c b/libc/calls/flock.c index 460c105bc..c75017e89 100644 --- a/libc/calls/flock.c +++ b/libc/calls/flock.c @@ -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) { diff --git a/libc/calls/fstatfs.c b/libc/calls/fstatfs.c index 1e47c174e..52591f915 100644 --- a/libc/calls/fstatfs.c +++ b/libc/calls/fstatfs.c @@ -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; diff --git a/libc/calls/fsync.c b/libc/calls/fsync.c index 38afd2faa..98ff4ced2 100644 --- a/libc/calls/fsync.c +++ b/libc/calls/fsync.c @@ -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) { diff --git a/libc/calls/ftruncate.c b/libc/calls/ftruncate.c index 944540d37..854114667 100644 --- a/libc/calls/ftruncate.c +++ b/libc/calls/ftruncate.c @@ -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 */ diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 68123098d..0b43a4a56 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -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_ diff --git a/libc/calls/interrupts-nt.c b/libc/calls/interrupts-nt.c index 18c5ea65f..b30c2fcb1 100644 --- a/libc/calls/interrupts-nt.c +++ b/libc/calls/interrupts-nt.c @@ -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; } diff --git a/libc/calls/nanosleep.c b/libc/calls/nanosleep.c index 9349320bf..2cffc641d 100644 --- a/libc/calls/nanosleep.c +++ b/libc/calls/nanosleep.c @@ -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) { diff --git a/libc/calls/ntcontext2linux.c b/libc/calls/ntcontext2linux.c index 818682d9e..1c1fc0fe0 100644 --- a/libc/calls/ntcontext2linux.c +++ b/libc/calls/ntcontext2linux.c @@ -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; diff --git a/libc/calls/open.c b/libc/calls/open.c index 48262b7d8..9ac2b3986 100644 --- a/libc/calls/open.c +++ b/libc/calls/open.c @@ -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 diff --git a/libc/calls/openat.c b/libc/calls/openat.c index 5c1f7d618..bd4fc3c95 100644 --- a/libc/calls/openat.c +++ b/libc/calls/openat.c @@ -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 diff --git a/libc/calls/pause-nt.c b/libc/calls/pause-nt.c index 072f8f550..b30d032fe 100644 --- a/libc/calls/pause-nt.c +++ b/libc/calls/pause-nt.c @@ -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) { diff --git a/libc/calls/pause.c b/libc/calls/pause.c index 4c3466654..3d0a475a8 100644 --- a/libc/calls/pause.c +++ b/libc/calls/pause.c @@ -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 */ diff --git a/libc/calls/poll-nt.c b/libc/calls/poll-nt.c index 726558c7e..b906b796c 100644 --- a/libc/calls/poll-nt.c +++ b/libc/calls/poll-nt.c @@ -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; } } diff --git a/libc/calls/poll.c b/libc/calls/poll.c index 7fe7a9bc8..8b20f9451 100644 --- a/libc/calls/poll.c +++ b/libc/calls/poll.c @@ -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 diff --git a/libc/calls/ppoll.c b/libc/calls/ppoll.c index 21a638ce6..87cea08dd 100644 --- a/libc/calls/ppoll.c +++ b/libc/calls/ppoll.c @@ -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 diff --git a/libc/calls/pread.c b/libc/calls/pread.c index 6c1a6bf6f..3d3732d14 100644 --- a/libc/calls/pread.c +++ b/libc/calls/pread.c @@ -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 diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index 0657395f1..1493f3007 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -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 */ diff --git a/libc/calls/pwrite.c b/libc/calls/pwrite.c index a8a12cf9b..fc502c6da 100644 --- a/libc/calls/pwrite.c +++ b/libc/calls/pwrite.c @@ -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 diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index 3197ca6c9..f8dd10fd8 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -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 */ diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index 35e531c0d..45fd0e5b6 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -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) { diff --git a/libc/calls/read.c b/libc/calls/read.c index e250c3c1d..2be56bb00 100644 --- a/libc/calls/read.c +++ b/libc/calls/read.c @@ -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 diff --git a/libc/calls/readv.c b/libc/calls/readv.c index f7c4d7e4b..5a05d51f3 100644 --- a/libc/calls/readv.c +++ b/libc/calls/readv.c @@ -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) { diff --git a/libc/calls/sigblockall.c b/libc/calls/sigblockall.c index 9ca00277c..7c0efd50c 100644 --- a/libc/calls/sigblockall.c +++ b/libc/calls/sigblockall.c @@ -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); } diff --git a/libc/calls/sigenter-netbsd.c b/libc/calls/sigenter-netbsd.c index 585076c43..14ecaa7fc 100644 --- a/libc/calls/sigenter-netbsd.c +++ b/libc/calls/sigenter-netbsd.c @@ -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; diff --git a/libc/calls/sigenter-openbsd.c b/libc/calls/sigenter-openbsd.c index f9b277ee8..b99b3abfb 100644 --- a/libc/calls/sigenter-openbsd.c +++ b/libc/calls/sigenter-openbsd.c @@ -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; diff --git a/libc/calls/sigenter-xnu.c b/libc/calls/sigenter-xnu.c index 80ff59d89..6804b1b75 100644 --- a/libc/calls/sigenter-xnu.c +++ b/libc/calls/sigenter-xnu.c @@ -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)) { diff --git a/libc/calls/sigprocmask.c b/libc/calls/sigprocmask.c index caab7e275..268490e59 100644 --- a/libc/calls/sigprocmask.c +++ b/libc/calls/sigprocmask.c @@ -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); } diff --git a/libc/calls/sigsetmask.c b/libc/calls/sigsetmask.c index 5cebef6f5..a6618ae0c 100644 --- a/libc/calls/sigsetmask.c +++ b/libc/calls/sigsetmask.c @@ -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()) { diff --git a/libc/calls/sigsuspend.c b/libc/calls/sigsuspend.c index 529ef7da5..7e2da624d 100644 --- a/libc/calls/sigsuspend.c +++ b/libc/calls/sigsuspend.c @@ -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) { diff --git a/libc/calls/sigtimedwait.c b/libc/calls/sigtimedwait.c index 3096d71af..c2063f49c 100644 --- a/libc/calls/sigtimedwait.c +++ b/libc/calls/sigtimedwait.c @@ -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) { diff --git a/libc/calls/sigwaitinfo.c b/libc/calls/sigwaitinfo.c index 816f243b9..394077983 100644 --- a/libc/calls/sigwaitinfo.c +++ b/libc/calls/sigwaitinfo.c @@ -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); diff --git a/libc/calls/sleep.c b/libc/calls/sleep.c index ab9962fc2..2631a82c3 100644 --- a/libc/calls/sleep.c +++ b/libc/calls/sleep.c @@ -29,6 +29,7 @@ * this function shall return the number of unslept seconds rounded * using the ceiling function * @see clock_nanosleep() + * @cancellationpoint * @asyncsignalsafe * @norestart */ diff --git a/libc/calls/statfs.c b/libc/calls/statfs.c index 02dad040e..97aa6be7a 100644 --- a/libc/calls/statfs.c +++ b/libc/calls/statfs.c @@ -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; diff --git a/libc/calls/syscall-sysv.internal.h b/libc/calls/syscall-sysv.internal.h index cd7f36ee4..42d1e7f57 100644 --- a/libc/calls/syscall-sysv.internal.h +++ b/libc/calls/syscall-sysv.internal.h @@ -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; diff --git a/libc/calls/tcdrain.c b/libc/calls/tcdrain.c index 5c6d2a6e3..40e5a41bd 100644 --- a/libc/calls/tcdrain.c +++ b/libc/calls/tcdrain.c @@ -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); } diff --git a/libc/calls/truncate.c b/libc/calls/truncate.c index d4a8915bb..3af89ab91 100644 --- a/libc/calls/truncate.c +++ b/libc/calls/truncate.c @@ -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 */ diff --git a/libc/calls/usleep.c b/libc/calls/usleep.c index a20c1ca67..820786004 100644 --- a/libc/calls/usleep.c +++ b/libc/calls/usleep.c @@ -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) { diff --git a/libc/calls/wait.c b/libc/calls/wait.c index f322cb9f5..6b9150293 100644 --- a/libc/calls/wait.c +++ b/libc/calls/wait.c @@ -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 diff --git a/libc/calls/wait3.c b/libc/calls/wait3.c index 9932081c4..74ae4eeb0 100644 --- a/libc/calls/wait3.c +++ b/libc/calls/wait3.c @@ -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 */ diff --git a/libc/calls/wait4-nt.c b/libc/calls/wait4-nt.c index bd68c737e..643f52a20 100644 --- a/libc/calls/wait4-nt.c +++ b/libc/calls/wait4-nt.c @@ -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); diff --git a/libc/calls/wait4.c b/libc/calls/wait4.c index b8b9672eb..d9afd2b66 100644 --- a/libc/calls/wait4.c +++ b/libc/calls/wait4.c @@ -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 */ diff --git a/libc/calls/waitpid.c b/libc/calls/waitpid.c index 9f457f066..e333134aa 100644 --- a/libc/calls/waitpid.c +++ b/libc/calls/waitpid.c @@ -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 */ diff --git a/libc/calls/write.c b/libc/calls/write.c index 9b421b460..8185c0fe6 100644 --- a/libc/calls/write.c +++ b/libc/calls/write.c @@ -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 diff --git a/libc/calls/writev.c b/libc/calls/writev.c index 292570c79..863cbb31a 100644 --- a/libc/calls/writev.c +++ b/libc/calls/writev.c @@ -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) { diff --git a/libc/intrin/ksignalnames.S b/libc/intrin/ksignalnames.S index 8336b3d5d..97fffaeb8 100644 --- a/libc/intrin/ksignalnames.S +++ b/libc/intrin/ksignalnames.S @@ -62,6 +62,7 @@ kSignalNames: .e SIGIO,"SIGIO" .e SIGSYS,"SIGSYS" .e SIGINFO,"SIGINFO" + .e SIGCANCEL,"SIGCANCEL" .e SIGRTMAX,"SIGRTMAX" .e SIGRTMIN,"SIGRTMIN" .e SIGEMT,"SIGEMT" diff --git a/libc/intrin/sigaddset.c b/libc/intrin/sigaddset.c index b19ac04cf..5f6902db8 100644 --- a/libc/intrin/sigaddset.c +++ b/libc/intrin/sigaddset.c @@ -33,12 +33,7 @@ int sigaddset(sigset_t *set, int sig) { _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); if (1 <= sig && sig <= NSIG) { if (1 <= sig && sig <= _NSIG) { - if ( -#define M(x) sig != x && -#include "libc/intrin/sigisprecious.inc" - 1) { - set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); - } + set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); } return 0; } else { diff --git a/libc/intrin/sigisprecious.inc b/libc/intrin/sigisprecious.inc index 5c08fd00b..923cc86b2 100644 --- a/libc/intrin/sigisprecious.inc +++ b/libc/intrin/sigisprecious.inc @@ -1,3 +1,4 @@ M(SIGKILL) M(SIGABRT) M(SIGSTOP) +M(SIGCANCEL) diff --git a/libc/runtime/msync.c b/libc/runtime/msync.c index 0f7c34e1f..c9c2ab193 100644 --- a/libc/runtime/msync.c +++ b/libc/runtime/msync.c @@ -34,6 +34,7 @@ * @param addr needs to be 4096-byte page aligned * @param flags needs MS_ASYNC or MS_SYNC and can have MS_INVALIDATE * @return 0 on success or -1 w/ errno + * @cancellationpoint */ int msync(void *addr, size_t size, int flags) { int rc; diff --git a/libc/sock/accept-nt.c b/libc/sock/accept-nt.c index 81a398cac..f2087b9b5 100644 --- a/libc/sock/accept-nt.c +++ b/libc/sock/accept-nt.c @@ -32,10 +32,10 @@ textwindows int sys_accept_nt(struct Fd *fd, void *addr, uint32_t *addrsize, int flags) { int64_t h; - int client, oflags; + int rc, client, oflags; struct SockFd *sockfd, *sockfd2; sockfd = (struct SockFd *)fd->extra; - if (_check_interrupts(true, g_fds.p)) return eintr(); + if (_check_interrupts(true, g_fds.p)) return -1; for (;;) { if (!WSAPoll(&(struct sys_pollfd_nt){fd->handle, POLLIN}, 1, __SIG_POLLING_INTERVAL_MS)) { diff --git a/libc/sock/accept.c b/libc/sock/accept.c index e4a08f0f6..ae2be472b 100644 --- a/libc/sock/accept.c +++ b/libc/sock/accept.c @@ -26,6 +26,7 @@ * @param out_addr will receive the remote address * @param inout_addrsize provides and receives addr's byte length * @return client fd which needs close(), or -1 w/ errno + * @cancellationpoint * @asyncsignalsafe * @restartable (unless SO_RCVTIMEO) */ diff --git a/libc/sock/accept4.c b/libc/sock/accept4.c index bc030e6d0..0499c65bb 100644 --- a/libc/sock/accept4.c +++ b/libc/sock/accept4.c @@ -36,6 +36,7 @@ * @param flags can have SOCK_{CLOEXEC,NONBLOCK}, which may apply to * both the newly created socket and the server one * @return client fd which needs close(), or -1 w/ errno + * @cancellationpoint * @asyncsignalsafe * @restartable (unless SO_RCVTIMEO) */ diff --git a/libc/sock/connect.c b/libc/sock/connect.c index d9d7b2c24..15db1cc03 100644 --- a/libc/sock/connect.c +++ b/libc/sock/connect.c @@ -35,6 +35,7 @@ * also means getsockname() can be called to retrieve routing details. * * @return 0 on success or -1 w/ errno + * @cancellationpoint * @asyncsignalsafe * @restartable (unless SO_RCVTIMEO) */ diff --git a/libc/sock/epoll.c b/libc/sock/epoll.c index 596b6f87e..8622e86d7 100644 --- a/libc/sock/epoll.c +++ b/libc/sock/epoll.c @@ -1499,6 +1499,7 @@ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev) { * @param maxevents is array length of events * @param timeoutms is milliseconds, 0 to not block, or -1 for forever * @return number of events stored, 0 on timeout, or -1 w/ errno + * @cancellationpoint * @norestart */ int epoll_wait(int epfd, struct epoll_event *events, int maxevents, diff --git a/libc/sock/pselect.c b/libc/sock/pselect.c index ba9311889..124ca1647 100644 --- a/libc/sock/pselect.c +++ b/libc/sock/pselect.c @@ -16,12 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/strace.internal.h" #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timeval.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/strace.internal.h" #include "libc/sock/internal.h" #include "libc/sock/select.h" #include "libc/sysv/errfuns.h" @@ -44,6 +44,11 @@ * * This system call is supported on all platforms. It's like select() * except that it atomically changes the sigprocmask() during the op. + * + * @cancellationpoint + * @asyncsignalsafe + * @threadsafe + * @norestart */ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask) { diff --git a/libc/sock/recv-nt.c b/libc/sock/recv-nt.c index 2239bbb6b..a0f734c53 100644 --- a/libc/sock/recv-nt.c +++ b/libc/sock/recv-nt.c @@ -34,7 +34,7 @@ textwindows ssize_t sys_recv_nt(struct Fd *fd, const struct iovec *iov, struct SockFd *sockfd; struct NtIovec iovnt[16]; struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()}; - if (_check_interrupts(true, g_fds.p)) return eintr(); + if (_check_interrupts(true, g_fds.p)) return -1; err = errno; if (!WSARecv(fd->handle, iovnt, __iovec2nt(iovnt, iov, iovlen), &got, &flags, &overlapped, NULL)) { diff --git a/libc/sock/recvfrom-nt.c b/libc/sock/recvfrom-nt.c index 39d8c6c84..1a2099db6 100644 --- a/libc/sock/recvfrom-nt.c +++ b/libc/sock/recvfrom-nt.c @@ -35,7 +35,7 @@ textwindows ssize_t sys_recvfrom_nt(struct Fd *fd, const struct iovec *iov, struct SockFd *sockfd; struct NtIovec iovnt[16]; struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()}; - if (_check_interrupts(true, g_fds.p)) return eintr(); + if (_check_interrupts(true, g_fds.p)) return -1; err = errno; if (!WSARecvFrom(fd->handle, iovnt, __iovec2nt(iovnt, iov, iovlen), &got, &flags, opt_out_srcaddr, opt_inout_srcaddrsize, &overlapped, diff --git a/libc/sock/recvfrom.c b/libc/sock/recvfrom.c index d34bcb1c5..d47991ce7 100644 --- a/libc/sock/recvfrom.c +++ b/libc/sock/recvfrom.c @@ -44,6 +44,7 @@ * @return number of bytes received, 0 on remote close, or -1 w/ errno * @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable), * EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc. + * @cancellationpoint * @asyncsignalsafe * @restartable (unless SO_RCVTIMEO) */ diff --git a/libc/sock/recvmsg.c b/libc/sock/recvmsg.c index 16ebd370c..6e44829e9 100644 --- a/libc/sock/recvmsg.c +++ b/libc/sock/recvmsg.c @@ -41,6 +41,7 @@ * @return number of bytes received, or -1 w/ errno * @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable), * EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc. + * @cancellationpoint * @asyncsignalsafe * @restartable (unless SO_RCVTIMEO) */ diff --git a/libc/sock/select.c b/libc/sock/select.c index a8e0219b6..f11a1e435 100644 --- a/libc/sock/select.c +++ b/libc/sock/select.c @@ -16,11 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/strace.internal.h" #include "libc/calls/struct/timeval.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/strace.internal.h" #include "libc/sock/internal.h" #include "libc/sock/select.h" #include "libc/sysv/errfuns.h" @@ -31,6 +31,11 @@ * This system call is supported on all platforms. However, on Windows, * this is polyfilled to translate into poll(). So it's recommended that * poll() be used instead. + * + * @cancellationpoint + * @asyncsignalsafe + * @threadsafe + * @norestart */ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { diff --git a/libc/sock/send-nt.c b/libc/sock/send-nt.c index b69df9fdb..2a0f4aedf 100644 --- a/libc/sock/send-nt.c +++ b/libc/sock/send-nt.c @@ -30,7 +30,7 @@ textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen, struct SockFd *sockfd; struct NtIovec iovnt[16]; struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()}; - if (_check_interrupts(true, g_fds.p)) return eintr(); + if (_check_interrupts(true, g_fds.p)) return -1; if (!WSASend(g_fds.p[fd].handle, iovnt, __iovec2nt(iovnt, iov, iovlen), &sent, flags, &overlapped, NULL)) { rc = sent; diff --git a/libc/sock/sendfile.c b/libc/sock/sendfile.c index 2b6fd919f..53b2f8311 100644 --- a/libc/sock/sendfile.c +++ b/libc/sock/sendfile.c @@ -43,6 +43,7 @@ // sendfile() isn't specified as raising eintr static textwindows int SendfileBlock(int64_t handle, struct NtOverlapped *overlapped) { + int rc; uint32_t i, got, flags = 0; if (WSAGetLastError() != kNtErrorIoPending && WSAGetLastError() != WSAEINPROGRESS) { @@ -56,7 +57,7 @@ static textwindows int SendfileBlock(int64_t handle, NTTRACE("WSAWaitForMultipleEvents failed %lm"); return __winsockerr(); } else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) { - _check_interrupts(true, g_fds.p); + if (_check_interrupts(true, g_fds.p)) return -1; #if _NTTRACE POLLTRACE("WSAWaitForMultipleEvents..."); #endif diff --git a/libc/sock/sendmsg.c b/libc/sock/sendmsg.c index 486930ab2..67a555d36 100644 --- a/libc/sock/sendmsg.c +++ b/libc/sock/sendmsg.c @@ -43,6 +43,7 @@ * @return number of bytes transmitted, or -1 w/ errno * @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable), * EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc. + * @cancellationpoint * @asyncsignalsafe * @restartable (unless SO_RCVTIMEO) */ diff --git a/libc/sock/sendto-nt.c b/libc/sock/sendto-nt.c index 1fdbc3fba..d870fa149 100644 --- a/libc/sock/sendto-nt.c +++ b/libc/sock/sendto-nt.c @@ -31,7 +31,7 @@ textwindows ssize_t sys_sendto_nt(int fd, const struct iovec *iov, struct SockFd *sockfd; struct NtIovec iovnt[16]; struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()}; - if (_check_interrupts(true, g_fds.p)) return eintr(); + if (_check_interrupts(true, g_fds.p)) return -1; if (!WSASendTo(g_fds.p[fd].handle, iovnt, __iovec2nt(iovnt, iov, iovlen), &sent, flags, opt_in_addr, in_addrsize, &overlapped, NULL)) { rc = sent; diff --git a/libc/sock/sendto.c b/libc/sock/sendto.c index b7bf87e2d..43ac49e50 100644 --- a/libc/sock/sendto.c +++ b/libc/sock/sendto.c @@ -49,6 +49,7 @@ * @return number of bytes transmitted, or -1 w/ errno * @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable), * EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc. + * @cancellationpoint * @asyncsignalsafe * @restartable (unless SO_RCVTIMEO) */ diff --git a/libc/sock/wsablock.c b/libc/sock/wsablock.c index 130baa518..a7669eb03 100644 --- a/libc/sock/wsablock.c +++ b/libc/sock/wsablock.c @@ -28,6 +28,7 @@ textwindows int __wsablock(int64_t handle, struct NtOverlapped *overlapped, uint32_t *flags, bool restartable, uint32_t timeout) { + int rc; uint32_t i, got; if (WSAGetLastError() != kNtErrorIoPending) { NTTRACE("sock i/o failed %lm"); @@ -41,7 +42,7 @@ textwindows int __wsablock(int64_t handle, struct NtOverlapped *overlapped, return __winsockerr(); } else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) { if (_check_interrupts(restartable, g_fds.p)) { - return eintr(); + return -1; } if (timeout) { if (timeout <= __SIG_POLLING_INTERVAL_MS) { diff --git a/libc/stdio/dirstream.c b/libc/stdio/dirstream.c index a7a545eb2..4c3c8f606 100644 --- a/libc/stdio/dirstream.c +++ b/libc/stdio/dirstream.c @@ -255,14 +255,20 @@ static textwindows dontinline struct dirent *readdir_nt(DIR *dir) { * * @returns newly allocated DIR object, or NULL w/ errno * @errors ENOENT, ENOTDIR, EACCES, EMFILE, ENFILE, ENOMEM + * @cancellationpoint * @see glob() */ DIR *opendir(const char *name) { - int fd; DIR *res; + int fd, rc; struct stat st; struct Zipos *zip; struct ZiposUri zipname; + if (_weaken(pthread_testcancel_np) && + (rc = _weaken(pthread_testcancel_np)())) { + errno = rc; + return 0; + } if (!name || (IsAsan() && !__asan_is_valid_str(name))) { efault(); res = 0; @@ -271,20 +277,21 @@ DIR *opendir(const char *name) { if (_weaken(__zipos_stat)(&zipname, &st) != -1) { if (S_ISDIR(st.st_mode)) { zip = _weaken(__zipos_get)(); - res = calloc(1, sizeof(DIR)); - res->iszip = true; - res->fd = -1; - res->zip.offset = GetZipCdirOffset(zip->cdir); - res->zip.records = GetZipCdirRecords(zip->cdir); - res->zip.prefix = malloc(zipname.len + 2); - memcpy(res->zip.prefix, zipname.path, zipname.len); - if (zipname.len && res->zip.prefix[zipname.len - 1] != '/') { - res->zip.prefix[zipname.len++] = '/'; + if ((res = calloc(1, sizeof(DIR)))) { + res->iszip = true; + res->fd = -1; + res->zip.offset = GetZipCdirOffset(zip->cdir); + res->zip.records = GetZipCdirRecords(zip->cdir); + res->zip.prefix = malloc(zipname.len + 2); + memcpy(res->zip.prefix, zipname.path, zipname.len); + if (zipname.len && res->zip.prefix[zipname.len - 1] != '/') { + res->zip.prefix[zipname.len++] = '/'; + } + res->zip.prefix[zipname.len] = '\0'; + res->zip.prefixlen = zipname.len; } - res->zip.prefix[zipname.len] = '\0'; - res->zip.prefixlen = zipname.len; } else { - errno = ENOTDIR; + enotdir(); res = 0; } } else { diff --git a/libc/stdio/getrandom.c b/libc/stdio/getrandom.c index 14d766a5d..1acef9f25 100644 --- a/libc/stdio/getrandom.c +++ b/libc/stdio/getrandom.c @@ -70,6 +70,7 @@ static bool have_getrandom; * @note this function takes around 900 cycles * @raise EINVAL if `f` is invalid * @raise ENOSYS on bare metal + * @cancellationpoint * @asyncsignalsafe * @restartable * @vforksafe diff --git a/libc/stdio/popen.c b/libc/stdio/popen.c index ffd5c71c2..5fb577315 100644 --- a/libc/stdio/popen.c +++ b/libc/stdio/popen.c @@ -19,6 +19,7 @@ #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/errno.h" +#include "libc/intrin/weaken.h" #include "libc/paths.h" #include "libc/runtime/runtime.h" #include "libc/stdio/internal.h" @@ -27,6 +28,7 @@ #include "libc/sysv/consts/fd.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/thread.h" /** * Spawns subprocess and returns pipe stream. @@ -35,11 +37,12 @@ * Bourne-like syntax on all platforms including Windows. * * @see pclose() + * @cancellationpoint * @threadsafe */ FILE *popen(const char *cmdline, const char *mode) { FILE *f; - int e, pid, dir, flags, pipefds[2]; + int e, rc, pid, dir, flags, pipefds[2]; flags = fopenflags(mode); if ((flags & O_ACCMODE) == O_RDONLY) { dir = 0; @@ -49,6 +52,11 @@ FILE *popen(const char *cmdline, const char *mode) { einval(); return NULL; } + if (_weaken(pthread_testcancel_np) && + (rc = _weaken(pthread_testcancel_np)())) { + errno = rc; + return 0; + } if (pipe2(pipefds, O_CLOEXEC) == -1) return NULL; if ((f = fdopen(pipefds[dir], mode))) { switch ((pid = fork())) { diff --git a/libc/stdio/posix_spawn.c b/libc/stdio/posix_spawn.c index a754e25bf..953f55961 100644 --- a/libc/stdio/posix_spawn.c +++ b/libc/stdio/posix_spawn.c @@ -25,6 +25,7 @@ #include "libc/stdio/posix_spawn.h" #include "libc/stdio/posix_spawn.internal.h" #include "libc/sysv/consts/sig.h" +#include "libc/thread/thread.h" #include "libc/thread/tls.h" static int RunFileActions(struct _posix_faction *a) { @@ -67,6 +68,7 @@ static int RunFileActions(struct _posix_faction *a) { * @param envp is environment variables, or `environ` if null * @return 0 on success or error number on failure * @see posix_spawnp() for `$PATH` searching + * @cancellationpoint * @tlsrequired * @threadsafe */ @@ -79,6 +81,9 @@ int posix_spawn(int *pid, const char *path, int s, child, policy; struct sched_param param; struct sigaction dfl = {0}; + if (_weaken(pthread_testcancel)) { + _weaken(pthread_testcancel)(); + } if (!(child = vfork())) { if (attrp && *attrp) { posix_spawnattr_getflags(attrp, &flags); diff --git a/libc/stdio/system.c b/libc/stdio/system.c index 571401bd8..8b820ab79 100644 --- a/libc/stdio/system.c +++ b/libc/stdio/system.c @@ -21,12 +21,14 @@ #include "libc/calls/struct/sigaction.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/weaken.h" #include "libc/log/log.h" #include "libc/paths.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/ok.h" #include "libc/sysv/consts/sig.h" +#include "libc/thread/thread.h" /** * Launches program with system command interpreter. @@ -37,6 +39,7 @@ * @param cmdline is an interpreted Turing-complete command * @return -1 if child process couldn't be created, otherwise a wait * status that can be accessed using macros like WEXITSTATUS(s) + * @cancellationpoint * @threadsafe */ int system(const char *cmdline) { @@ -44,6 +47,9 @@ int system(const char *cmdline) { sigset_t chldmask, savemask; struct sigaction ignore, saveint, savequit; if (!cmdline) return 1; + if (_weaken(pthread_testcancel)) { + _weaken(pthread_testcancel)(); + } ignore.sa_flags = 0; ignore.sa_handler = SIG_IGN; sigemptyset(&ignore.sa_mask); diff --git a/libc/sysv/calls/__sys_accept.s b/libc/sysv/calls/__sys_accept.s index 1a9fbdb18..52e9a836b 100644 --- a/libc/sysv/calls/__sys_accept.s +++ b/libc/sysv/calls/__sys_accept.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall __sys_accept,0x01e01e21d201e02b,globl,hidden +.scall __sys_accept,0x81e81ea1d281e82b,globl,hidden diff --git a/libc/sysv/calls/__sys_accept4.s b/libc/sysv/calls/__sys_accept4.s index 941c30684..5ae850522 100644 --- a/libc/sysv/calls/__sys_accept4.s +++ b/libc/sysv/calls/__sys_accept4.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall __sys_accept4,0xfff05d21dffff120,globl,hidden +.scall __sys_accept4,0xfff85da1dffff920,globl,hidden diff --git a/libc/sysv/calls/__sys_connect.s b/libc/sysv/calls/__sys_connect.s index 25c89e111..dd07beae6 100644 --- a/libc/sysv/calls/__sys_connect.s +++ b/libc/sysv/calls/__sys_connect.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall __sys_connect,0x062062062206202a,globl,hidden +.scall __sys_connect,0x862862862286282a,globl,hidden diff --git a/libc/sysv/calls/__sys_fcntl_cp.s b/libc/sysv/calls/__sys_fcntl_cp.s new file mode 100644 index 000000000..dd46a0d5c --- /dev/null +++ b/libc/sysv/calls/__sys_fcntl_cp.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall __sys_fcntl_cp,0x85c85c85c285c848,globl,hidden diff --git a/libc/sysv/calls/__sys_openat.s b/libc/sysv/calls/__sys_openat.s index 127fd0967..40b3ccebd 100644 --- a/libc/sysv/calls/__sys_openat.s +++ b/libc/sysv/calls/__sys_openat.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall __sys_openat,0x1d41411f321cf101,globl,hidden +.scall __sys_openat,0x9d49419f329cf901,globl,hidden diff --git a/libc/sysv/calls/__sys_wait4.s b/libc/sysv/calls/__sys_wait4.s index 15180408c..96c889e9b 100644 --- a/libc/sysv/calls/__sys_wait4.s +++ b/libc/sysv/calls/__sys_wait4.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall __sys_wait4,0x1c100b007200703d,globl,hidden +.scall __sys_wait4,0x9c180b807280783d,globl,hidden diff --git a/libc/sysv/calls/posix_fallocate.s b/libc/sysv/calls/posix_fallocate.s index 41815859a..0e65e104e 100644 --- a/libc/sysv/calls/posix_fallocate.s +++ b/libc/sysv/calls/posix_fallocate.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall posix_fallocate,0x1dffff212fffffff,globl,hidden +.scall posix_fallocate,0x9dffffa12fffffff,globl,hidden diff --git a/libc/sysv/calls/sys_clock_nanosleep.s b/libc/sysv/calls/sys_clock_nanosleep.s index 70fef5259..82c21586c 100644 --- a/libc/sysv/calls/sys_clock_nanosleep.s +++ b/libc/sysv/calls/sys_clock_nanosleep.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_clock_nanosleep,0x1ddfff0f4ffff0e6,globl,hidden +.scall sys_clock_nanosleep,0x9ddfff8f4ffff8e6,globl,hidden diff --git a/libc/sysv/calls/sys_epoll_wait.s b/libc/sysv/calls/sys_epoll_wait.s index f38c97423..af328026e 100644 --- a/libc/sysv/calls/sys_epoll_wait.s +++ b/libc/sysv/calls/sys_epoll_wait.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_epoll_wait,0xfffffffffffff0e8,globl,hidden +.scall sys_epoll_wait,0xfffffffffffff8e8,globl,hidden diff --git a/libc/sysv/calls/sys_fallocate.s b/libc/sysv/calls/sys_fallocate.s index fbd91988f..52181e8d4 100644 --- a/libc/sysv/calls/sys_fallocate.s +++ b/libc/sysv/calls/sys_fallocate.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_fallocate,0xfffffffffffff11d,globl +.scall sys_fallocate,0xfffffffffffff91d,globl diff --git a/libc/sysv/calls/sys_fdatasync.s b/libc/sysv/calls/sys_fdatasync.s index d050c1aea..dbe9435a5 100644 --- a/libc/sysv/calls/sys_fdatasync.s +++ b/libc/sysv/calls/sys_fdatasync.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_fdatasync,0x0f105f22620bb04b,globl,hidden +.scall sys_fdatasync,0x8f185fa2628bb84b,globl,hidden diff --git a/libc/sysv/calls/sys_flock.s b/libc/sysv/calls/sys_flock.s index 2626a05d7..b49d322f1 100644 --- a/libc/sysv/calls/sys_flock.s +++ b/libc/sysv/calls/sys_flock.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_flock,0x0830830832083049,globl,hidden +.scall sys_flock,0x8838838832883849,globl,hidden diff --git a/libc/sysv/calls/sys_fstatfs.s b/libc/sysv/calls/sys_fstatfs.s index 1cc25d2c3..de5c1cccf 100644 --- a/libc/sysv/calls/sys_fstatfs.s +++ b/libc/sysv/calls/sys_fstatfs.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_fstatfs,0x09e04022c215a08a,globl,hidden +.scall sys_fstatfs,0x89e840a2c295a88a,globl,hidden diff --git a/libc/sysv/calls/sys_fsync.s b/libc/sysv/calls/sys_fsync.s index 99f60f440..da50a6244 100644 --- a/libc/sysv/calls/sys_fsync.s +++ b/libc/sysv/calls/sys_fsync.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_fsync,0x05f05f05f205f04a,globl,hidden +.scall sys_fsync,0x85f85f85f285f84a,globl,hidden diff --git a/libc/sysv/calls/sys_ftruncate.s b/libc/sysv/calls/sys_ftruncate.s index a5809ee92..8c5e6d2dc 100644 --- a/libc/sysv/calls/sys_ftruncate.s +++ b/libc/sysv/calls/sys_ftruncate.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_ftruncate,0x0c90c91e020c904d,globl,hidden +.scall sys_ftruncate,0x8c98c99e028c984d,globl,hidden diff --git a/libc/sysv/calls/sys_getrandom.s b/libc/sysv/calls/sys_getrandom.s index f6ddb33f1..fb8772447 100644 --- a/libc/sysv/calls/sys_getrandom.s +++ b/libc/sysv/calls/sys_getrandom.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_getrandom,0xfff00723321f413e,globl,hidden +.scall sys_getrandom,0xfff807a3329f493e,globl,hidden diff --git a/libc/sysv/calls/sys_ioctl_cp.s b/libc/sysv/calls/sys_ioctl_cp.s new file mode 100644 index 000000000..3c29af533 --- /dev/null +++ b/libc/sysv/calls/sys_ioctl_cp.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_ioctl_cp,0x8368368362836810,globl,hidden diff --git a/libc/sysv/calls/sys_msgrcv.s b/libc/sysv/calls/sys_msgrcv.s index 6b2b4374b..fd1749054 100644 --- a/libc/sysv/calls/sys_msgrcv.s +++ b/libc/sysv/calls/sys_msgrcv.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_msgrcv,0x0e30e30e32105046,globl +.scall sys_msgrcv,0x8e38e38e32905846,globl diff --git a/libc/sysv/calls/sys_msgsnd.s b/libc/sysv/calls/sys_msgsnd.s index d1062e639..cb30eee9b 100644 --- a/libc/sysv/calls/sys_msgsnd.s +++ b/libc/sysv/calls/sys_msgsnd.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_msgsnd,0x0e20e20e22104045,globl +.scall sys_msgsnd,0x8e28e28e22904845,globl diff --git a/libc/sysv/calls/sys_msync.s b/libc/sysv/calls/sys_msync.s index 3dd327fbc..0295e1431 100644 --- a/libc/sysv/calls/sys_msync.s +++ b/libc/sysv/calls/sys_msync.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_msync,0x115100041204101a,globl,hidden +.scall sys_msync,0x915900841284181a,globl,hidden diff --git a/libc/sysv/calls/sys_nanosleep.s b/libc/sysv/calls/sys_nanosleep.s index af2f8b156..e5dc327b4 100644 --- a/libc/sysv/calls/sys_nanosleep.s +++ b/libc/sysv/calls/sys_nanosleep.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_nanosleep,0x1ae05b0f0ffff023,globl,hidden +.scall sys_nanosleep,0x9ae85b8f0ffff823,globl,hidden diff --git a/libc/sysv/calls/sys_open.s b/libc/sysv/calls/sys_open.s index ff76ad2b1..329edd4ab 100644 --- a/libc/sysv/calls/sys_open.s +++ b/libc/sysv/calls/sys_open.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_open,0x0050050052005002,globl,hidden +.scall sys_open,0x8058058052805802,globl,hidden diff --git a/libc/sysv/calls/sys_poll.s b/libc/sysv/calls/sys_poll.s index 31799ccbf..5724fca7c 100644 --- a/libc/sysv/calls/sys_poll.s +++ b/libc/sysv/calls/sys_poll.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_poll,0x0d10fc0d120e6007,globl,hidden +.scall sys_poll,0x8d18fc8d128e6807,globl,hidden diff --git a/libc/sysv/calls/sys_ppoll.s b/libc/sysv/calls/sys_ppoll.s index 425c2eea4..d67abbd13 100644 --- a/libc/sysv/calls/sys_ppoll.s +++ b/libc/sysv/calls/sys_ppoll.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_ppoll,0xfff06d221ffff10f,globl,hidden +.scall sys_ppoll,0xfff86da21ffff90f,globl,hidden diff --git a/libc/sysv/calls/sys_pread.s b/libc/sysv/calls/sys_pread.s index bce4dfa06..8b447da4b 100644 --- a/libc/sysv/calls/sys_pread.s +++ b/libc/sysv/calls/sys_pread.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_pread,0x0ad0ad1db2099011,globl,hidden +.scall sys_pread,0x8ad8ad9db2899811,globl,hidden diff --git a/libc/sysv/calls/sys_preadv.s b/libc/sysv/calls/sys_preadv.s index 3a5f810fd..7f4c57db5 100644 --- a/libc/sysv/calls/sys_preadv.s +++ b/libc/sysv/calls/sys_preadv.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_preadv,0x12110b121221c127,globl,hidden +.scall sys_preadv,0x92190b9212a1c927,globl,hidden diff --git a/libc/sysv/calls/sys_pselect.s b/libc/sysv/calls/sys_pselect.s index ce670ff94..1f72f24bf 100644 --- a/libc/sysv/calls/sys_pselect.s +++ b/libc/sysv/calls/sys_pselect.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_pselect,0x1b406e20a218a10e,globl,hidden +.scall sys_pselect,0x9b486ea0a298a90e,globl,hidden diff --git a/libc/sysv/calls/sys_pwrite.s b/libc/sysv/calls/sys_pwrite.s index 703fb9de7..9e568cdab 100644 --- a/libc/sysv/calls/sys_pwrite.s +++ b/libc/sysv/calls/sys_pwrite.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_pwrite,0x0ae0ae1dc209a012,globl,hidden +.scall sys_pwrite,0x8ae8ae9dc289a812,globl,hidden diff --git a/libc/sysv/calls/sys_pwritev.s b/libc/sysv/calls/sys_pwritev.s index 985983a7d..a769d7bd6 100644 --- a/libc/sysv/calls/sys_pwritev.s +++ b/libc/sysv/calls/sys_pwritev.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_pwritev,0x12210c122221d128,globl,hidden +.scall sys_pwritev,0x92290c9222a1d928,globl,hidden diff --git a/libc/sysv/calls/sys_read.s b/libc/sysv/calls/sys_read.s index 0f8185e48..5736be16d 100644 --- a/libc/sysv/calls/sys_read.s +++ b/libc/sysv/calls/sys_read.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_read,0x0030030032003000,globl,hidden +.scall sys_read,0x8038038032803800,globl,hidden diff --git a/libc/sysv/calls/sys_readv.s b/libc/sysv/calls/sys_readv.s index 0d985abc2..2675256c9 100644 --- a/libc/sysv/calls/sys_readv.s +++ b/libc/sysv/calls/sys_readv.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_readv,0x0780780782078013,globl,hidden +.scall sys_readv,0x8788788782878813,globl,hidden diff --git a/libc/sysv/calls/sys_recvfrom.s b/libc/sysv/calls/sys_recvfrom.s index cfeab7b7b..cfb1d4997 100644 --- a/libc/sysv/calls/sys_recvfrom.s +++ b/libc/sysv/calls/sys_recvfrom.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_recvfrom,0x01d01d01d201d02d,globl,hidden +.scall sys_recvfrom,0x81d81d81d281d82d,globl,hidden diff --git a/libc/sysv/calls/sys_recvmsg.s b/libc/sysv/calls/sys_recvmsg.s index 32177e64a..661d6bdb2 100644 --- a/libc/sysv/calls/sys_recvmsg.s +++ b/libc/sysv/calls/sys_recvmsg.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_recvmsg,0x01b01b01b201b02f,globl,hidden +.scall sys_recvmsg,0x81b81b81b281b82f,globl,hidden diff --git a/libc/sysv/calls/sys_select.s b/libc/sysv/calls/sys_select.s index bb8184dcc..099846215 100644 --- a/libc/sysv/calls/sys_select.s +++ b/libc/sysv/calls/sys_select.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_select,0x1a104705d205d017,globl,hidden +.scall sys_select,0x9a184785d285d817,globl,hidden diff --git a/libc/sysv/calls/sys_sendmsg.s b/libc/sysv/calls/sys_sendmsg.s index 015899bd3..aaf95bed8 100644 --- a/libc/sysv/calls/sys_sendmsg.s +++ b/libc/sysv/calls/sys_sendmsg.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_sendmsg,0x01c01c01c201c02e,globl,hidden +.scall sys_sendmsg,0x81c81c81c281c82e,globl,hidden diff --git a/libc/sysv/calls/sys_sendto.s b/libc/sysv/calls/sys_sendto.s index b0162e9b1..8a0056186 100644 --- a/libc/sysv/calls/sys_sendto.s +++ b/libc/sysv/calls/sys_sendto.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_sendto,0x085085085208502c,globl,hidden +.scall sys_sendto,0x885885885288582c,globl,hidden diff --git a/libc/sysv/calls/sys_sigsuspend.s b/libc/sysv/calls/sys_sigsuspend.s index cbf2b659e..9974260d0 100644 --- a/libc/sysv/calls/sys_sigsuspend.s +++ b/libc/sysv/calls/sys_sigsuspend.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_sigsuspend,0x12606f155206f082,globl,hidden +.scall sys_sigsuspend,0x92686f955286f882,globl,hidden diff --git a/libc/sysv/calls/sys_sigtimedwait.s b/libc/sysv/calls/sys_sigtimedwait.s index 33ba49272..2113a46e2 100644 --- a/libc/sysv/calls/sys_sigtimedwait.s +++ b/libc/sysv/calls/sys_sigtimedwait.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_sigtimedwait,0x1affff159ffff080,globl,hidden +.scall sys_sigtimedwait,0x9affff959ffff880,globl,hidden diff --git a/libc/sysv/calls/sys_statfs.s b/libc/sysv/calls/sys_statfs.s index a1990e83f..aec6af5e7 100644 --- a/libc/sysv/calls/sys_statfs.s +++ b/libc/sysv/calls/sys_statfs.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_statfs,0x09d03f22b2159089,globl,hidden +.scall sys_statfs,0x89d83fa2b2959889,globl,hidden diff --git a/libc/sysv/calls/sys_truncate.s b/libc/sysv/calls/sys_truncate.s index e2710fe4e..768200310 100644 --- a/libc/sysv/calls/sys_truncate.s +++ b/libc/sysv/calls/sys_truncate.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_truncate,0x0c80c81df20c804c,globl,hidden +.scall sys_truncate,0x8c88c89df28c884c,globl,hidden diff --git a/libc/sysv/calls/sys_waitid.s b/libc/sysv/calls/sys_waitid.s index eab72d6a0..b77e7a078 100644 --- a/libc/sysv/calls/sys_waitid.s +++ b/libc/sysv/calls/sys_waitid.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_waitid,0xfffffffff20ad0f7,globl +.scall sys_waitid,0xfffffffff28ad8f7,globl diff --git a/libc/sysv/calls/sys_write.s b/libc/sysv/calls/sys_write.s index 4162549d1..fd196f011 100644 --- a/libc/sysv/calls/sys_write.s +++ b/libc/sysv/calls/sys_write.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_write,0x0040040042004001,globl,hidden +.scall sys_write,0x8048048042804801,globl,hidden diff --git a/libc/sysv/calls/sys_writev.s b/libc/sysv/calls/sys_writev.s index 45b8beb55..11f8ffb20 100644 --- a/libc/sysv/calls/sys_writev.s +++ b/libc/sysv/calls/sys_writev.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_writev,0x0790790792079014,globl,hidden +.scall sys_writev,0x8798798792879814,globl,hidden diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 58e7e37bb..20334d0d5 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -168,9 +168,10 @@ syscon sig SIGPROF 27 27 27 27 27 27 # profiling timer expired; syscon sig SIGWINCH 28 28 28 28 28 28 # terminal resized; unix consensus & faked on nt syscon sig SIGIO 29 23 23 23 23 29 # bsd consensus syscon sig SIGSYS 31 12 12 12 12 31 # wut; bsd consensus +syscon sig SIGINFO 0 29 29 29 29 0 # bsd consensus syscon sig SIGEMT 0 7 7 7 7 0 # not implemented in most community editions of system five; consider doing this using SIGUSR1 or SIGUSR2 instead syscon sig SIGPWR 30 30 30 30 32 30 # not implemented in most community editions of system five; consider doing this using SIGUSR1 or SIGUSR2 instead -syscon sig SIGINFO 0 29 29 29 29 0 # bsd consensus +syscon sig SIGCANCEL 32 7 65 7 33 32 # SIGRTMIN+0; faked as SIGEMT on XNU and OpenBSD syscon sig SIGRTMIN 32 0 65 0 33 32 syscon sig SIGRTMAX 64 0 126 0 63 64 syscon compat SIGPOLL 29 23 23 23 23 29 # same as SIGIO diff --git a/libc/sysv/consts/EDEVERR.s b/libc/sysv/consts/EDEVERR.s index 1deaa2ea7..b9fd0c96c 100644 --- a/libc/sysv/consts/EDEVERR.s +++ b/libc/sysv/consts/EDEVERR.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon errno,EDEVERR,311,83,311,311,311,0 +.syscon errno,EDEVERR,311,83,311,311,311,483 diff --git a/libc/sysv/consts/SIGCANCEL.s b/libc/sysv/consts/SIGCANCEL.s new file mode 100644 index 000000000..b7ab88be7 --- /dev/null +++ b/libc/sysv/consts/SIGCANCEL.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/consts/syscon.internal.inc" +.syscon sig,SIGCANCEL,32,7,65,7,33,32 diff --git a/libc/sysv/consts/sig.h b/libc/sysv/consts/sig.h index 46e58f763..a2f996688 100644 --- a/libc/sysv/consts/sig.h +++ b/libc/sysv/consts/sig.h @@ -7,6 +7,7 @@ COSMOPOLITAN_C_START_ extern const int SIGABRT; extern const int SIGALRM; extern const int SIGBUS; +extern const int SIGCANCEL; extern const int SIGCHLD; extern const int SIGCONT; extern const int SIGEMT; @@ -72,6 +73,7 @@ COSMOPOLITAN_C_END_ #define SIGXFSZ LITERALLY(25) #define SIGBUS SYMBOLIC(SIGBUS) +#define SIGCANCEL SYMBOLIC(SIGCANCEL) #define SIGCHLD SYMBOLIC(SIGCHLD) #define SIGCONT SYMBOLIC(SIGCONT) #define SIGEMT SYMBOLIC(SIGEMT) diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index bcba2eea7..8cab83c6a 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -22,43 +22,44 @@ dir=libc/sysv/calls # The Fifth Bell System Interface, Community Edition ┌─────────────────────────┐ # » so many numbers │ legend │ # ├─────────────────────────┤ -# GNU/Systemd┐ │ ffff │ unavailable │ -# 2.6.18+│ │ sys_ │ wrapped │ -# Mac OS X┐ │ │ __sys_ │ wrapped twice │ -# 15.6+│ │ └─────────────────────────┘ -# FreeBSD┐ │ │ -# 12+│ ┌─│──│── XnuClass{1:Mach,2:Unix} -# OpenBSD┐ │ │ │ │ +# GNU/Systemd┐ │ fff │ unavailable │ +# 2.6.18+│ │ 800 │ cancellable │ +# Mac OS X┐ │ │ sys_ │ wrapped │ +# 15.6+│ │ │ __sys_ │ wrapped twice │ +# FreeBSD┐ │ │ └─────────────────────────┘ +# 12+│ │ │ +# OpenBSD┐ │ ┌─│──│── XnuClass{1:Mach,2:Unix} # 6.4+│ │ │ │ │ # NetBSD┐ │ │ │ │ │ # 9.1+│ │ │ │ │ │ # Symbol ┌┴┐┌┴┐┌┴┐│┬┴┐┌┴┐ Directives & Commentary scall sys_exit 0x00100100120010e7 globl hidden # a.k.a. exit_group -scall sys_read 0x0030030032003000 globl hidden -scall sys_write 0x0040040042004001 globl hidden -scall sys_open 0x0050050052005002 globl hidden +scall sys_read 0x8038038032803800 globl hidden +scall sys_write 0x8048048042804801 globl hidden +scall sys_open 0x8058058052805802 globl hidden scall sys_close 0x0060060062006003 globl hidden scall __sys_stat 0x1b7026fff2152004 globl hidden # FreeBSD 11→12 fumble; use sys_fstatat(); blocked on Android scall __sys_fstat 0x1b80352272153005 globl hidden # needs __stat2linux() scall __sys_lstat 0x1b90280282154006 globl hidden # needs __stat2linux(); blocked on Android -scall sys_poll 0x0d10fc0d120e6007 globl hidden -scall sys_ppoll 0xfff06d221ffff10f globl hidden # consider INTON/INTOFF tutorial in examples/unbourne.c +scall sys_poll 0x8d18fc8d128e6807 globl hidden +scall sys_ppoll 0xfff86da21ffff90f globl hidden # consider INTON/INTOFF tutorial in examples/unbourne.c scall sys_lseek 0x0c70c71de20c7008 globl hidden # netbsd+openbsd:evilpad scall __sys_mmap 0x0c50c51dd20c5009 globl hidden # netbsd+openbsd:pad -scall sys_msync 0x115100041204101a globl hidden +scall sys_msync 0x915900841284181a globl hidden scall sys_mprotect 0x04a04a04a204a00a globl hidden scall __sys_munmap 0x049049049204900b globl hidden scall sys_sigaction 0x15402e1a0202e00d globl hidden # rt_sigaction on Lunix; __sigaction_sigtramp() on NetBSD scall __sys_sigprocmask 0x125030154214900e globl hidden # a.k.a. rt_sigprocmask, openbsd:byvalue, a.k.a. pthread_sigmask scall sys_ioctl 0x0360360362036010 globl hidden -scall sys_pread 0x0ad0ad1db2099011 globl hidden # a.k.a. pread64; netbsd+openbsd:pad -scall sys_pwrite 0x0ae0ae1dc209a012 globl hidden # a.k.a. pwrite64; netbsd+openbsd:pad -scall sys_readv 0x0780780782078013 globl hidden -scall sys_writev 0x0790790792079014 globl hidden +scall sys_ioctl_cp 0x8368368362836810 globl hidden +scall sys_pread 0x8ad8ad9db2899811 globl hidden # a.k.a. pread64; netbsd+openbsd:pad +scall sys_pwrite 0x8ae8ae9dc289a812 globl hidden # a.k.a. pwrite64; netbsd+openbsd:pad +scall sys_readv 0x8788788782878813 globl hidden +scall sys_writev 0x8798798792879814 globl hidden scall sys_access 0x0210210212021015 globl hidden scall __sys_pipe 0x02a10721e202a016 globl hidden # NOTE: pipe2() on FreeBSD; XNU is pipe(void)→eax:edx -scall sys_select 0x1a104705d205d017 globl hidden -scall sys_pselect 0x1b406e20a218a10e globl hidden # pselect6() on gnu/systemd +scall sys_select 0x9a184785d285d817 globl hidden +scall sys_pselect 0x9b486ea0a298a90e globl hidden # pselect6() on gnu/systemd scall sys_sched_yield 0x15e12a14bf25d018 globl hidden # select() on XNU (previously swtch() but removed in 12.4) scall __sys_mremap 0x19bffffffffff019 globl hidden scall sys_mincore 0x04e04e04e204e01b globl hidden @@ -69,20 +70,20 @@ scall sys_shmctl 0x1bb128200210701f globl # no wrapper; consider mmap scall sys_dup 0x0290290292029020 globl hidden scall sys_dup2 0x05a05a05a205a021 globl hidden scall sys_pause 0xfffffffffffff022 globl hidden -scall sys_nanosleep 0x1ae05b0f0ffff023 globl hidden -scall sys_clock_nanosleep 0x1ddfff0f4ffff0e6 globl hidden +scall sys_nanosleep 0x9ae85b8f0ffff823 globl hidden +scall sys_clock_nanosleep 0x9ddfff8f4ffff8e6 globl hidden scall sys_getitimer 0x1aa0460562056024 globl hidden scall sys_setitimer 0x1a90450532053026 globl hidden scall sys_alarm 0xfffffffffffff025 globl hidden scall sys_getpid 0x0140140142014027 globl hidden # netbsd returns ppid in edx scall sys_sendfile 0xffffff1892151028 globl hidden # Linux vs. XNU/BSD ABIs very different scall __sys_socket 0x18a0610612061029 globl hidden -scall __sys_connect 0x062062062206202a globl hidden -scall __sys_accept 0x01e01e21d201e02b globl hidden # accept4 on freebsd -scall sys_sendto 0x085085085208502c globl hidden -scall sys_recvfrom 0x01d01d01d201d02d globl hidden -scall sys_sendmsg 0x01c01c01c201c02e globl hidden -scall sys_recvmsg 0x01b01b01b201b02f globl hidden +scall __sys_connect 0x862862862286282a globl hidden +scall __sys_accept 0x81e81ea1d281e82b globl hidden # accept4 on freebsd +scall sys_sendto 0x885885885288582c globl hidden +scall sys_recvfrom 0x81d81d81d281d82d globl hidden +scall sys_sendmsg 0x81c81c81c281c82e globl hidden +scall sys_recvmsg 0x81b81b81b281b82f globl hidden scall sys_shutdown 0x0860860862086030 globl hidden scall __sys_bind 0x0680680682068031 globl hidden scall sys_listen 0x06a06a06a206a032 globl hidden @@ -95,7 +96,7 @@ scall __sys_fork 0x0020020022002039 globl hidden # xnu needs eax&=~-edx bc eax #scall vfork 0x042042042204203a globl # this syscall is from the moon so we implement it by hand in libc/runtime/vfork.S; probably removed from XNU in 12.5 scall sys_posix_spawn 0x1daffffff20f4fff globl hidden # good luck figuring out how xnu defines this scall __sys_execve 0x03b03b03b203b03b globl hidden -scall __sys_wait4 0x1c100b007200703d globl hidden +scall __sys_wait4 0x9c180b807280783d globl hidden scall sys_kill 0x02507a025202503e globl hidden # kill(pid, sig, 1) b/c xnu scall sys_killpg 0x092fff092fffffff globl hidden scall sys_clone 0x11fffffffffff038 globl hidden @@ -109,15 +110,16 @@ scall sys_semop 0x0de1220de2100041 globl # no wrapper; won't polyfill for windo scall sys_semctl 0xfff1271fe20fe042 globl # no wrapper; won't polyfill for windows scall sys_shmdt 0x0e60e60e62108043 globl # no wrapper; won't polyfill for windows scall sys_msgget 0x0e10e10e12103044 globl # no wrapper; won't polyfill for windows -scall sys_msgsnd 0x0e20e20e22104045 globl # no wrapper; won't polyfill for windows -scall sys_msgrcv 0x0e30e30e32105046 globl # no wrapper; won't polyfill for windows +scall sys_msgsnd 0x8e28e28e22904845 globl # no wrapper; won't polyfill for windows +scall sys_msgrcv 0x8e38e38e32905846 globl # no wrapper; won't polyfill for windows scall sys_msgctl 0x1bc1291ff2102047 globl # no wrapper; won't polyfill for windows scall __sys_fcntl 0x05c05c05c205c048 globl hidden -scall sys_flock 0x0830830832083049 globl hidden -scall sys_fsync 0x05f05f05f205f04a globl hidden -scall sys_fdatasync 0x0f105f22620bb04b globl hidden # fsync() on openbsd -scall sys_truncate 0x0c80c81df20c804c globl hidden # netbsd+openbsd:pad -scall sys_ftruncate 0x0c90c91e020c904d globl hidden # netbsd+openbsd:pad +scall __sys_fcntl_cp 0x85c85c85c285c848 globl hidden +scall sys_flock 0x8838838832883849 globl hidden +scall sys_fsync 0x85f85f85f285f84a globl hidden +scall sys_fdatasync 0x8f185fa2628bb84b globl hidden # fsync() on openbsd +scall sys_truncate 0x8c88c89df28c884c globl hidden # netbsd+openbsd:pad +scall sys_ftruncate 0x8c98c99e028c984d globl hidden # netbsd+openbsd:pad scall sys_getcwd 0x128130146ffff04f globl hidden scall sys_chdir 0x00c00c00c200c050 globl hidden scall sys_fchdir 0x00d00d00d200d051 globl hidden @@ -163,14 +165,14 @@ scall sys_setresgid 0xfff11c138ffff077 globl hidden # polyfilled for xnu scall sys_getresuid 0xfff119168ffff076 globl hidden # semantics aren't well-defined scall sys_getresgid 0xfff11b169ffff078 globl hidden # semantics aren't well-defined scall sys_sigpending 0x124034157203407f globl hidden # a.k.a. rt_sigpending on linux -scall sys_sigsuspend 0x12606f155206f082 globl hidden # a.k.a. rt_sigsuspend on Linux; openbsd:byvalue, sigsuspend_nocancel on XNU +scall sys_sigsuspend 0x92686f955286f882 globl hidden # a.k.a. rt_sigsuspend on Linux; openbsd:byvalue, sigsuspend_nocancel on XNU scall sys_sigaltstack 0x1191200352035083 globl hidden scall sys_mknod 0x1c200e00e200e085 globl hidden scall sys_mknodat 0x1cc14022fffff103 globl # no wrapper; FreeBSD 12+ scall sys_mkfifo 0x0840840842084fff globl hidden scall sys_mkfifoat 0x1cb13f1f1fffffff globl # no wrapper -scall sys_statfs 0x09d03f22b2159089 globl hidden -scall sys_fstatfs 0x09e04022c215a08a globl hidden +scall sys_statfs 0x89d83fa2b2959889 globl hidden +scall sys_fstatfs 0x89e840a2c295a88a globl hidden scall sys_getpriority 0x064064064206408c globl hidden scall sys_setpriority 0x060060060206008d globl hidden # modern nice() scall sys_mlock 0x0cb0cb0cb20cb095 globl # no wrapper @@ -191,7 +193,7 @@ scall sys_setfsuid 0xfffffffffffff07a globl hidden scall sys_setfsgid 0xfffffffffffff07b globl hidden scall sys_capget 0xfffffffffffff07d globl # no wrapper scall sys_capset 0xfffffffffffff07e globl # no wrapper -scall sys_sigtimedwait 0x1affff159ffff080 globl hidden +scall sys_sigtimedwait 0x9affff959ffff880 globl hidden scall sys_sigqueue 0xffffff1c8fffffff globl hidden scall sys_sigqueueinfo 0x0f5ffffffffff081 globl hidden # a.k.a. rt_sigqueueinfo on linux scall sys_personality 0xfffffffffffff087 globl # no wrapper @@ -242,7 +244,7 @@ scall sys_io_submit 0xfffffffffffff0d1 globl # no wrapper scall sys_io_cancel 0xfffffffffffff0d2 globl # no wrapper scall sys_lookup_dcookie 0xfffffffffffff0d4 globl # no wrapper scall sys_epoll_create 0xfffffffffffff0d5 globl hidden -scall sys_epoll_wait 0xfffffffffffff0e8 globl hidden +scall sys_epoll_wait 0xfffffffffffff8e8 globl hidden scall sys_epoll_ctl 0xfffffffffffff0e9 globl hidden scall sys_getdents 0x18606311020c40d9 globl hidden # use opendir/readdir; four args b/c xnu, getdirentries on xnu, 32-bit on xnu/freebsd, a.k.a. getdents64 on linux, 64-bit on openbsd scall sys_set_tid_address 0xfffffffffffff0da globl # no wrapper @@ -273,7 +275,7 @@ scall sys_mq_timedreceive 0x1b1ffffffffff0f3 globl # won't polyfill scall sys_mq_notify 0x106ffffffffff0f4 globl # won't polyfill scall sys_mq_getsetattr 0xfffffffffffff0f5 globl # won't polyfill scall sys_kexec_load 0xfffffffffffff0f6 globl # no wrapper -scall sys_waitid 0xfffffffff20ad0f7 globl # Linux 2.6.9+; no wrapper +scall sys_waitid 0xfffffffff28ad8f7 globl # Linux 2.6.9+; no wrapper scall sys_add_key 0xfffffffffffff0f8 globl # no wrapper scall sys_request_key 0xfffffffffffff0f9 globl # no wrapper scall sys_keyctl 0xfffffffffffff0fa globl # no wrapper @@ -282,7 +284,7 @@ scall ioprio_get 0xfffffffffffff0fc globl scall sys_inotify_init 0xfffffffffffff0fd globl # wicked # no wrapper scall sys_inotify_add_watch 0xfffffffffffff0fe globl # no wrapper scall sys_inotify_rm_watch 0xfffffffffffff0ff globl # no wrapper -scall __sys_openat 0x1d41411f321cf101 globl hidden # Linux 2.6.16+ (c. 2007) +scall __sys_openat 0x9d49419f329cf901 globl hidden # Linux 2.6.16+ (c. 2007) scall sys_mkdirat 0x1cd13e1f021db102 globl hidden scall sys_fchownat 0x1d013b1eb21d4104 globl hidden # @asyncsignalsafe scall sys_utime 0xfffffffffffff084 globl hidden @@ -306,12 +308,12 @@ scall sys_vmsplice 0xfffffffffffff116 globl hidden scall sys_migrate_pages 0xfffffffffffff100 globl # no wrapper; numa numa yay scall sys_move_pages 0xfffffffffffff117 globl # no wrapper; NOTE: We view Red Hat versions as "epochs" for all distros. #──────────────────────RHEL 5.0 LIMIT──────────────────────── # ←┬─ last distro with gplv2 licensed compiler c. 2007 -scall sys_preadv 0x12110b121221c127 globl hidden # ├─ last distro with system v shell script init -scall sys_pwritev 0x12210c122221d128 globl hidden # ├─ rob landley unleashes busybox gpl lawsuits +scall sys_preadv 0x92190b9212a1c927 globl hidden # ├─ last distro with system v shell script init +scall sys_pwritev 0x92290c9222a1d928 globl hidden # ├─ rob landley unleashes busybox gpl lawsuits scall __sys_utimensat 0x1d3054223ffff118 globl hidden # ├─ python modules need this due to pep513 -scall sys_fallocate 0xfffffffffffff11d globl # ├─ end of life 2020-11-30 (extended) -scall posix_fallocate 0x1dffff212fffffff globl hidden # └─ cosmopolitan supports rhel5+ -scall __sys_accept4 0xfff05d21dffff120 globl hidden # Linux 2.6.28+ +scall sys_fallocate 0xfffffffffffff91d globl # ├─ end of life 2020-11-30 (extended) +scall posix_fallocate 0x9dffffa12fffffff globl hidden # └─ cosmopolitan supports rhel5+ +scall __sys_accept4 0xfff85da1dffff920 globl hidden # Linux 2.6.28+ scall __sys_dup3 0x1c6066fffffff124 globl hidden # Linux 2.6.27+ scall __sys_pipe2 0x1c506521effff125 globl hidden # Linux 2.6.27+ scall sys_epoll_pwait 0xfffffffffffff119 globl # no wrapper @@ -347,7 +349,7 @@ scall sys_sched_setattr 0xfffffffffffff13a globl # ├─ desktop replaced with scall sys_sched_getattr 0xfffffffffffff13b globl # ├─ karen sandler requires systemd init and boot for tablet gui scall sys_renameat2 0xfffffffffffff13c globl # └─ debian founder ian murdock found strangled with vacuum cord #scall seccomp 0xfffffffffffff13d globl # wrapped manually -scall sys_getrandom 0xfff00723321f413e globl hidden # Linux 3.17+ and getentropy() on XNU/OpenBSD, coming to NetBSD in 9.2 +scall sys_getrandom 0xfff807a3329f493e globl hidden # Linux 3.17+ and getentropy() on XNU/OpenBSD, coming to NetBSD in 9.2 scall sys_memfd_create 0xfffffffffffff13f globl hidden scall sys_kexec_file_load 0xfffffffffffff140 globl # no wrapper scall sys_bpf 0xfffffffffffff141 globl # no wrapper diff --git a/libc/sysv/systemfive.S b/libc/sysv/systemfive.S index a16b00d1b..08b0e4b38 100644 --- a/libc/sysv/systemfive.S +++ b/libc/sysv/systemfive.S @@ -21,6 +21,7 @@ #include "libc/macros.internal.h" #include "libc/nexgen32e/macros.h" #include "libc/sysv/consts/sig.h" +#include "libc/thread/posixthread.internal.h" #include "libc/sysv/consts/nr.h" #define SIG_IGN 1 @@ -107,19 +108,60 @@ __pid: .quad 0 .endobj __pid,globl,hidden .previous - .privileged +systemfive_cancellable: # our pthread_cancel() miracle code + cmpb $0,__tls_enabled(%rip) # inspired by the musl libc design! + je 1f # we handle linux and bsd together! + mov %fs:0,%r10 # CosmoTib::tib_self + mov 0x28(%r10),%r10 # CosmoTib::tib_pthread + test %r10,%r10 # is it a posix thread? + jz 1f # it's spawn() probably + testb $PT_NOCANCEL,0x00(%r10) # PosixThread::flags + jnz 1f # canceler no cancelling + cmp $0,0x04(%r10) # PosixThread::cancelled + jne _pthread_cancel_sys # tail call for masked mode + .weak _pthread_cancel_sys # must be linked if we're cancelled +1: mov %rcx,%r10 # move the fourth argument + clc # no cancellable system calls exist + syscall # that have 7+ args on the bsd OSes +systemfive_cancellable_end: # i/o calls park here for long time + jc systemfive_errno + jmp 0f + jc 3f # we're now out of the limbo state! +1: cmp $-4095,%rax # but we still check again on eintr + jae 2f + ret # done if the system call succeeded +2: neg %eax # now examine the nature of failure +3: cmp EINTR(%rip),%eax # did SIGCANCEL cancel our i/o call + jne systemfive_errno # werent interrupted by OnSigCancel + cmpb $0,__tls_enabled(%rip) # make sure it's safe to grab %fs:0 + je systemfive_errno # tls is disabled we can't continue + mov %fs:0,%rcx # CosmoTib::tib_self + mov 0x28(%rcx),%rcx # CosmoTib::tib_pthread + test %rcx,%rcx # is it a posix thread? + jz systemfive_errno # it's spawn() probably + testb $PT_NOCANCEL,0x00(%rcx) # PosixThread::flags + jnz systemfive_errno # cancellation is disabled + cmp $0,0x04(%rcx) # PosixThread::cancelled + je systemfive_errno # we aren't actually cancelled + jmp _pthread_cancel_sys # now we are in fact cancelled + .endfn systemfive_cancellable,globl,hidden + .globl systemfive_cancellable_end + .hidden systemfive_cancellable_end + .Lanchorpoint: #if SupportsLinux() || SupportsMetal() systemfive_linux: - and $0xfff,%eax - cmp $0xfff,%eax + and $0xfff,%eax # remove nonlinux bits from ordinal + cmp $0xfff,%eax # checks if unsupported by platform je systemfive_enosys # never taken branches cost nothing + btr $11,%eax # 0x800 means a call is cancellable + jc systemfive_cancellable # it is handled by the holiest code mov %rcx,%r10 # syscall instruction clobbers %rcx push %rbp # linux never reads args from stack mov %rsp,%rbp # having frame will help backtraces syscall # this is known as a context switch pop %rbp # next we check to see if it failed - cmp $-4095,%rax # system five nexgen32e abi § a.2.1 +0: cmp $-4095,%rax # system five nexgen32e abi § a.2.1 jae systemfive_error # encodes errno as neg return value ret .endfn systemfive_linux,globl,hidden @@ -167,17 +209,10 @@ systemfive_bsdscrub: systemfive_bsd: cmp $0xfff,%ax je systemfive_enosys + btr $11,%eax # checks/reset the 800 cancellable bit + jc systemfive_cancellable mov %rcx,%r10 - push %rbx - push 32(%rsp) - push 24(%rsp) - push 16(%rsp) - mov %rax,%rbx # save ordinal for SIGSYS crash report syscall # bsd will need arg on stack sometimes - pop %rbx - pop %rbx - pop %rbx - pop %rbx jc systemfive_errno # bsd sets carry flag if %rax is errno ret .endfn systemfive_bsd @@ -391,22 +426,6 @@ _init_systemfive_sigsys: pop %rdi 1: .endfn _init_systemfive_sigsys #endif -#if SupportsSystemv() && !defined(TINY) -_init_systemfive_syscall: - mov __NR_msyscall,%eax # syscall origin protect - cmp $0xfff,%ax # openbsd is pretty cool - jae _init_systemfive_done - push %rdi - push %rsi - .weak __privileged_addr - .weak __privileged_size - mov $__privileged_addr,%edi - mov $__privileged_size,%esi - syscall - pop %rsi - pop %rdi -// 𝑠𝑙𝑖𝑑𝑒 -#endif /* TINY */ _init_systemfive_done: nop .init.end 300,_init_systemfive,globl,hidden diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index d7cb697c9..b63df05af 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -8,14 +8,13 @@ #define PT_OWNSTACK 1 #define PT_MAINTHREAD 2 +#define PT_ASYNC 4 +#define PT_NOCANCEL 8 +#define PT_MASKED 16 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -/** - * @fileoverview Cosmopolitan POSIX Thread Internals - */ - // LEGAL TRANSITIONS ┌──> TERMINATED // pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ──> ZOMBIE // └──────────────┘ @@ -60,15 +59,13 @@ enum PosixThreadStatus { }; struct PosixThread { + int flags; // 0x00: see PT_* constants + _Atomic(int) cancelled; // 0x04: thread has bad beliefs _Atomic(enum PosixThreadStatus) status; - void *(*start_routine)(void *); - void *arg; // start_routine's parameter - void *rc; // start_routine's return value int tid; // clone parent tid - int flags; // see PT_* constants - _Atomic(int) cancelled; // thread has bad beliefs - char cancelasync; // PTHREAD_CANCEL_DEFERRED/ASYNCHRONOUS - char canceldisable; // PTHREAD_CANCEL_ENABLE/DISABLE + void *(*start)(void *); // creation callback + void *arg; // start's parameter + void *rc; // start's return value char *altstack; // thread sigaltstack char *tls; // bottom of tls allocation struct CosmoTib *tib; // middle of tls allocation @@ -98,6 +95,7 @@ void _pthread_key_destruct(void) hidden; void _pthread_onfork_prepare(void) hidden; void _pthread_onfork_parent(void) hidden; void _pthread_onfork_child(void) hidden; +int _pthread_cancel_sys(void) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/thread/pthread_cancel.c b/libc/thread/pthread_cancel.c index 520f0646f..79371dd93 100644 --- a/libc/thread/pthread_cancel.c +++ b/libc/thread/pthread_cancel.c @@ -16,52 +16,184 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/kprintf.h" +#include "libc/assert.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/struct/sigset.h" +#include "libc/calls/ucontext.h" +#include "libc/errno.h" +#include "libc/intrin/atomic.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" +#include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" +#include "libc/thread/tls.h" + +extern const char systemfive_cancellable[] hidden; +extern const char systemfive_cancellable_end[] hidden; + +int _pthread_cancel_sys(void) { + struct PosixThread *pt; + pt = (struct PosixThread *)__get_tls()->tib_pthread; + if (!(pt->flags & (PT_NOCANCEL | PT_MASKED)) || (pt->flags & PT_ASYNC)) { + pthread_exit(PTHREAD_CANCELED); + } + pt->flags |= PT_NOCANCEL; + return ecanceled(); +} + +static void OnSigCancel(int sig, siginfo_t *si, void *ctx) { + ucontext_t *uc = ctx; + struct CosmoTib *tib = __get_tls(); + struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread; + if (pt && !(pt->flags & PT_NOCANCEL) && + atomic_load_explicit(&pt->cancelled, memory_order_acquire)) { + sigaddset(&uc->uc_sigmask, sig); + if ((pt->flags & PT_ASYNC) || + (systemfive_cancellable <= (char *)uc->uc_mcontext.rip && + (char *)uc->uc_mcontext.rip < systemfive_cancellable_end)) { + uc->uc_mcontext.rip = (intptr_t)_pthread_cancel_sys; + } else { + tkill(atomic_load_explicit(&tib->tib_tid, memory_order_relaxed), sig); + } + } +} + +static void ListenForSigCancel(void) { + struct sigaction sa; + sa.sa_sigaction = OnSigCancel; + sa.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK; + memset(&sa.sa_mask, -1, sizeof(sa.sa_mask)); + _npassert(!sigaction(SIGCANCEL, &sa, 0)); +} /** * Cancels thread. * - * This function currently isn't supported. In order to support this - * function we'd need to redesign the system call interface, and add - * bloat and complexity to every function that can return EINTR. You - * might want to consider using `nsync_note` instead, which provides - * much better cancellations because posix cancellations is a broken - * design. If you need to cancel i/o operations, try doing this: + * When a thread is cancelled, it'll interrupt blocking i/o calls, + * invoke any cleanup handlers that were pushed on the thread's stack + * as well as key destructors, and then the thread exits. * - * _Thread_local bool gotusr1; - * void OnUsr1(int sig) { gotusr1 = true; } - * struct sigaction sa = {.sa_handler = OnUsr1}; - * sigaction(SIGUSR1, &sa, 0); - * pthread_kill(thread, SIGUSR1); + * By default, pthread_cancel() can only take effect when a thread is + * blocked on a @cancellationpoint, which is any system call that's + * specified as raising `EINTR`. For example, `openat`, `poll`, `ppoll`, + * `select`, `pselect`, `read`, `readv`, `pread`, `preadv`, `write`, + * `writev`, `pwrite`, `pwritev`, `accept`, `connect`, `recvmsg`, + * `sendmsg`, `recv`, `send`, `tcdrain`, `clock_nanosleep`, `fsync`, + * `fdatasync`, `fcntl(F_SETLKW)`, `epoll`, `sigsuspend`, `msync`, + * `wait4`, `getrandom`, `pthread_cond_timedwait` are most cancellation + * points, plus many userspace libraries that call the above functions, + * unless they're using pthread_setcancelstate() to temporarily disable + * the cancellation mechanism. Some userspace functions, e.g. system() + * and popen() will eagerly call pthread_testcancel_np() to help avoid + * the potential for resource leaks later on. * - * The above code should successfully cancel a thread's blocking io - * operations in most cases, e.g. + * It's possible to put a thread in asynchronous cancellation mode using + * pthread_setcanceltype(), thus allowing a cancellation to occur at any + * assembly opcode. Please be warned that doing so is risky since it can + * easily result in resource leaks. For example, a cancellation might be + * triggered between calling open() and pthread_cleanup_push(), in which + * case the application will leak a file descriptor. * - * void *MyThread(void *arg) { - * sigset_t ss; - * sigfillset(&ss); - * sigdelset(&ss, SIGUSR1); - * sigprocmask(SIG_SETMASK, &ss, 0); - * while (!gotusr1) { - * char buf[512]; - * ssize_t rc = read(0, buf, sizeof(buf)); - * if (rc == -1 && errno == EINTR) continue; - * write(1, buf, rc); - * } - * return 0; - * } + * If none of the above options seem savory to you, then a third way is + * offered for doing cancellations. Cosmopolitan Libc supports the Musl + * Libc `PTHREAD_CANCEL_MASKED` non-POSIX extension. Any thread may pass + * this setting to pthread_setcancelstate(), in which case threads won't + * be abruptly destroyed upon cancellation and have their stack unwound; + * instead, cancellation points will simply raise an `ECANCELED` error, + * which can be more safely and intuitively handled for many use cases. * - * This has the same correctness issue as glibc, but it's usually - * "good enough" if you only need cancellations to perform things - * like server shutdown and socket options like `SO_RCVTIMEO` can - * ensure it's even safer, since it can't possibly block forever. - * - * @see https://sourceware.org/bugzilla/show_bug.cgi?id=12683 + * @return 0 on success, or errno on error + * @raise ESRCH if thread isn't alive */ int pthread_cancel(pthread_t thread) { - kprintf("error: pthread_cancel() isn't supported, please see the" - " cosmopolitan libc documentation for further details\n"); - _Exit(1); + int e, rc, tid; + static bool once; + struct PosixThread *pt; + __require_tls(); + if (!once) ListenForSigCancel(), once = true; + pt = (struct PosixThread *)thread; + switch (atomic_load_explicit(&pt->status, memory_order_acquire)) { + case kPosixThreadZombie: + case kPosixThreadTerminated: + return ESRCH; + default: + break; + } + atomic_store_explicit(&pt->cancelled, 1, memory_order_release); + if (thread == __get_tls()->tib_pthread) { + if (!(pt->flags & PT_NOCANCEL) && (pt->flags & PT_ASYNC)) { + pthread_exit(PTHREAD_CANCELED); + } + return 0; + } + if (IsWindows()) return 0; // TODO(jart): Should we do this? + tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire); + if (tid <= 0) return 0; // TODO(jart): Do we need this? + e = errno; + if (!tkill(tid, SIGCANCEL)) { + return 0; + } else { + rc = errno; + errno = e; + return rc; + } + return 0; +} + +/** + * Creates cancellation point in calling thread. + * + * This function can be used to force `PTHREAD_CANCEL_DEFERRED` threads + * to cancel without needing to invoke an interruptible system call. If + * the calling thread is in the `PTHREAD_CANCEL_DISABLE` then this will + * do nothing. If the calling thread hasn't yet been cancelled, this'll + * do nothing. In `PTHREAD_CANCEL_MASKED` mode, this also does nothing. + * + * @see pthread_testcancel_np() + */ +void pthread_testcancel(void) { + struct PosixThread *pt; + if (!__tls_enabled) return; + pt = (struct PosixThread *)__get_tls()->tib_pthread; + if (pt->flags & PT_NOCANCEL) return; + if ((!(pt->flags & PT_MASKED) || (pt->flags & PT_ASYNC)) && + atomic_load_explicit(&pt->cancelled, memory_order_acquire)) { + pthread_exit(PTHREAD_CANCELED); + } +} + +/** + * Creates cancellation point in calling thread. + * + * This function can be used to force `PTHREAD_CANCEL_DEFERRED` threads + * to cancel without needing to invoke an interruptible system call. If + * the calling thread is in the `PTHREAD_CANCEL_DISABLE` then this will + * do nothing. If the calling thread hasn't yet been cancelled, this'll + * do nothing. If the calling thread uses `PTHREAD_CANCEL_MASKED`, then + * this function returns `ECANCELED` if a cancellation occurred, rather + * than the normal behavior which is to destroy and cleanup the thread. + * Any `ECANCELED` result must not be ignored, because the thread shall + * have cancellations disabled once it occurs. + * + * @return 0 if not cancelled or cancellation is blocked or `ECANCELED` + * in masked mode when the calling thread has been cancelled + */ +int pthread_testcancel_np(void) { + int rc; + struct PosixThread *pt; + if (!__tls_enabled) return 0; + pt = (struct PosixThread *)__get_tls()->tib_pthread; + if (pt->flags & PT_NOCANCEL) return 0; + if (!atomic_load_explicit(&pt->cancelled, memory_order_acquire)) return 0; + if (!(pt->flags & PT_MASKED) || (pt->flags & PT_ASYNC)) { + pthread_exit(PTHREAD_CANCELED); + } else { + pt->flags |= PT_NOCANCEL; + return ECANCELED; + } } diff --git a/libc/thread/pthread_cond_timedwait.c b/libc/thread/pthread_cond_timedwait.c index 77da43c44..03de7b369 100644 --- a/libc/thread/pthread_cond_timedwait.c +++ b/libc/thread/pthread_cond_timedwait.c @@ -43,6 +43,7 @@ * @raise EINVAL if `0 ≤ abstime->tv_nsec < 1000000000` wasn't the case * @see pthread_cond_broadcast * @see pthread_cond_signal + * @cancellationpoint */ errno_t pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) { diff --git a/libc/thread/pthread_cond_wait.c b/libc/thread/pthread_cond_wait.c index 260a4ce20..6606f7787 100644 --- a/libc/thread/pthread_cond_wait.c +++ b/libc/thread/pthread_cond_wait.c @@ -36,6 +36,7 @@ * @see pthread_cond_timedwait * @see pthread_cond_broadcast * @see pthread_cond_signal + * @cancellationpoint */ errno_t pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { return pthread_cond_timedwait(cond, mutex, 0); diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index 408a793ee..af8396466 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -98,7 +98,7 @@ static int PosixThread(void *arg, int tid) { if (!setjmp(pt->exiter)) { __get_tls()->tib_pthread = (pthread_t)pt; _sigsetmask(pt->sigmask); - pt->rc = pt->start_routine(pt->arg); + pt->rc = pt->start(pt->arg); // ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup _npassert(!pt->cleanup); } @@ -150,7 +150,7 @@ static errno_t pthread_create_impl(pthread_t *thread, errno = e; return EAGAIN; } - pt->start_routine = start_routine; + pt->start = start_routine; pt->arg = arg; // create thread local storage memory diff --git a/libc/thread/pthread_join.c b/libc/thread/pthread_join.c index c5b7389a2..082854970 100644 --- a/libc/thread/pthread_join.c +++ b/libc/thread/pthread_join.c @@ -25,9 +25,12 @@ * Waits for thread to terminate. * * @param value_ptr if non-null will receive pthread_exit() argument + * if the thread called pthread_exit(), or `PTHREAD_CANCELED` if + * pthread_cancel() destroyed the thread instead * @return 0 on success, or errno with error * @raise EDEADLK if `thread` is the current thread * @raise EINVAL if `thread` is detached + * @cancellationpoint * @returnserrno * @threadsafe */ diff --git a/libc/thread/pthread_setcancelstate.c b/libc/thread/pthread_setcancelstate.c index ddb2c66aa..7889c07a2 100644 --- a/libc/thread/pthread_setcancelstate.c +++ b/libc/thread/pthread_setcancelstate.c @@ -24,22 +24,47 @@ /** * Sets cancelability state. * + * This function may be used to temporarily disable cancellation for the + * calling thread, which is necessary in cases when a @cancellationpoint + * function is invoked from an @asyncsignalsafe function. + * + * Cosmopolitan Libc supports the Musl Libc `PTHREAD_CANCEL_MASKED` + * non-POSIX extension. Any thread may use this setting, in which case + * the thread won't be abruptly destroyed upon a cancellation and have + * its stack unwound; instead, the thread will encounter an `ECANCELED` + * errno the next time it calls a cancellation point. + * * @param state may be one of: * - `PTHREAD_CANCEL_ENABLE` (default) * - `PTHREAD_CANCEL_DISABLE` + * - `PTHREAD_CANCEL_MASKED` * @param oldstate optionally receives old value * @return 0 on success, or errno on error * @raise EINVAL if `state` has bad value - * @see pthread_cancel() for docs + * @asyncsignalsafe */ int pthread_setcancelstate(int state, int *oldstate) { struct PosixThread *pt; switch (state) { case PTHREAD_CANCEL_ENABLE: case PTHREAD_CANCEL_DISABLE: + case PTHREAD_CANCEL_MASKED: pt = (struct PosixThread *)__get_tls()->tib_pthread; - if (oldstate) *oldstate = pt->canceldisable; - pt->canceldisable = state; + if (oldstate) { + if (pt->flags & PT_NOCANCEL) { + *oldstate = PTHREAD_CANCEL_DISABLE; + } else if (pt->flags & PT_MASKED) { + *oldstate = PTHREAD_CANCEL_MASKED; + } else { + *oldstate = PTHREAD_CANCEL_ENABLE; + } + } + pt->flags &= ~(PT_NOCANCEL | PT_MASKED); + if (state == PTHREAD_CANCEL_MASKED) { + pt->flags |= PT_MASKED; + } else if (state == PTHREAD_CANCEL_DISABLE) { + pt->flags |= PT_NOCANCEL; + } return 0; default: return EINVAL; diff --git a/libc/thread/pthread_setcanceltype.c b/libc/thread/pthread_setcanceltype.c index d95d3bb81..65c7d7f2a 100644 --- a/libc/thread/pthread_setcanceltype.c +++ b/libc/thread/pthread_setcanceltype.c @@ -26,7 +26,7 @@ * * @param type may be one of: * - `PTHREAD_CANCEL_DEFERRED` (default) - * - `PTHREAD_CANCEL_ASYNCHRONOUS` + * - `PTHREAD_CANCEL_ASYNCHRONOUS` (cray cray) * @param oldtype optionally receives old value * @return 0 on success, or errno on error * @raise EINVAL if `type` has bad value @@ -38,8 +38,18 @@ int pthread_setcanceltype(int type, int *oldtype) { case PTHREAD_CANCEL_DEFERRED: case PTHREAD_CANCEL_ASYNCHRONOUS: pt = (struct PosixThread *)__get_tls()->tib_pthread; - if (oldtype) *oldtype = pt->cancelasync; - pt->cancelasync = type; + if (oldtype) { + if (pt->flags & PT_ASYNC) { + *oldtype = PTHREAD_CANCEL_ASYNCHRONOUS; + } else { + *oldtype = PTHREAD_CANCEL_DEFERRED; + } + } + if (type == PTHREAD_CANCEL_DEFERRED) { + pt->flags &= ~PT_ASYNC; + } else { + pt->flags |= PT_ASYNC; + } return 0; default: return EINVAL; diff --git a/libc/thread/sem_timedwait.c b/libc/thread/sem_timedwait.c index bdcb9a46a..dd098c044 100644 --- a/libc/thread/sem_timedwait.c +++ b/libc/thread/sem_timedwait.c @@ -60,6 +60,7 @@ static struct timespec *sem_timeout(struct timespec *memory, * @raise EDEADLK if deadlock was detected * @raise ETIMEDOUT if deadline expired * @raise EINVAL if `sem` is invalid + * @cancellationpoint */ int sem_timedwait(sem_t *sem, const struct timespec *abstime) { int e, i, v, rc; diff --git a/libc/thread/sem_wait.c b/libc/thread/sem_wait.c index 5aabfa571..e32b41ed3 100644 --- a/libc/thread/sem_wait.c +++ b/libc/thread/sem_wait.c @@ -25,6 +25,7 @@ * @raise EINTR if signal was delivered instead * @raise EDEADLK if deadlock was detected * @raise EINVAL if `sem` is invalid + * @cancellationpoint */ int sem_wait(sem_t *sem) { return sem_timedwait(sem, 0); diff --git a/libc/thread/thread.h b/libc/thread/thread.h index b38171bbc..b3f70496b 100644 --- a/libc/thread/thread.h +++ b/libc/thread/thread.h @@ -27,6 +27,7 @@ #define PTHREAD_CANCEL_ENABLE 0 #define PTHREAD_CANCEL_DISABLE 1 +#define PTHREAD_CANCEL_MASKED 2 #define PTHREAD_CANCEL_DEFERRED 0 #define PTHREAD_CANCEL_ASYNCHRONOUS 1 @@ -109,6 +110,8 @@ int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); int pthread_yield(void); +void pthread_testcancel(void); +int pthread_testcancel_np(void); void pthread_exit(void *) wontreturn; pthread_t pthread_self(void) pureconst; pthread_id_np_t pthread_getthreadid_np(void); diff --git a/libc/thread/thread.mk b/libc/thread/thread.mk index ce9740284..704c99e19 100644 --- a/libc/thread/thread.mk +++ b/libc/thread/thread.mk @@ -50,11 +50,6 @@ $(LIBC_THREAD_A).pkg: \ $(LIBC_THREAD_A_OBJS) \ $(foreach x,$(LIBC_THREAD_A_DIRECTDEPS),$($(x)_A).pkg) -$(LIBC_THREAD_A_OBJS): private \ - OVERRIDE_CCFLAGS += \ - -ffunction-sections \ - -fdata-sections - LIBC_THREAD_LIBS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x))) LIBC_THREAD_SRCS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_SRCS)) LIBC_THREAD_HDRS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/zipos/open.c b/libc/zipos/open.c index 097b4eb3f..fa7d2ef29 100644 --- a/libc/zipos/open.c +++ b/libc/zipos/open.c @@ -30,6 +30,7 @@ #include "libc/intrin/cmpxchg.h" #include "libc/intrin/directmap.internal.h" #include "libc/intrin/extend.internal.h" +#include "libc/intrin/weaken.h" #include "libc/nexgen32e/crc32.h" #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" @@ -40,6 +41,7 @@ #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/thread.h" #include "libc/zip.h" #include "libc/zipos/zipos.internal.h" @@ -188,6 +190,7 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags, * Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store. * * @param uri is obtained via __zipos_parseuri() + * @cancellationpoint * @asyncsignalsafe * @threadsafe */ @@ -195,6 +198,11 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) { int rc; ssize_t cf; struct Zipos *zipos; + if (_weaken(pthread_testcancel_np) && + (rc = _weaken(pthread_testcancel_np)())) { + errno = rc; + return -1; + } BLOCK_SIGNALS; if ((flags & O_ACCMODE) == O_RDONLY) { if ((zipos = __zipos_get())) { diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c index 6157d0cbb..86806b071 100644 --- a/test/libc/calls/sigaction_test.c +++ b/test/libc/calls/sigaction_test.c @@ -21,6 +21,7 @@ #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/sigset.h" +#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall_support-sysv.internal.h" #include "libc/calls/ucontext.h" #include "libc/dce.h" @@ -28,6 +29,7 @@ #include "libc/nexgen32e/nexgen32e.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/consts/uc.h" @@ -211,3 +213,29 @@ TEST(sigaction, enosys_returnsErrnoRatherThanSigsysByDefault) { if (IsOpenbsd()) return; // TODO: Why does OpenBSD raise SIGABRT? ASSERT_SYS(ENOSYS, -1, sys_bogus()); } + +sig_atomic_t gotusr1; + +void OnSigMask(int sig, struct siginfo *si, void *ctx) { + ucontext_t *uc = ctx; + sigaddset(&uc->uc_sigmask, sig); + gotusr1 = true; +} + +TEST(uc_sigmask, signalHandlerCanChangeSignalMaskOfTrappedThread) { + if (IsWindows()) return; // TODO(jart): uc_sigmask support on windows + sigset_t want, got; + struct sigaction oldsa; + struct sigaction sa = {.sa_sigaction = OnSigMask, .sa_flags = SA_SIGINFO}; + sigemptyset(&want); + ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &want, 0)); + ASSERT_SYS(0, 0, sigaction(SIGUSR1, &sa, &oldsa)); + ASSERT_SYS(0, 0, raise(SIGUSR1)); + ASSERT_TRUE(gotusr1); + ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, 0, &got)); + sigaddset(&want, SIGUSR1); + ASSERT_STREQ(DescribeSigset(0, &want), DescribeSigset(0, &got)); + ASSERT_SYS(0, 0, sigaction(SIGUSR1, &oldsa, 0)); + sigdelset(&want, SIGUSR1); + ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &want, 0)); +} diff --git a/test/libc/intrin/describesigset_test.c b/test/libc/intrin/describesigset_test.c index 66c35a302..567734487 100644 --- a/test/libc/intrin/describesigset_test.c +++ b/test/libc/intrin/describesigset_test.c @@ -26,7 +26,11 @@ TEST(DescribeSigset, full) { sigset_t ss; sigfillset(&ss); - EXPECT_STREQ("~{ABRT,KILL,STOP}", DescribeSigset(0, &ss)); + if (IsXnu() || IsOpenbsd()) { + EXPECT_STREQ("~{ABRT,CANCEL,KILL,STOP}", DescribeSigset(0, &ss)); + } else { + EXPECT_STREQ("~{ABRT,KILL,STOP,CANCEL}", DescribeSigset(0, &ss)); + } } TEST(DescribeSigset, present) { @@ -42,9 +46,11 @@ TEST(DescribeSigset, absent) { sigfillset(&ss); sigdelset(&ss, SIGINT); sigdelset(&ss, SIGUSR1); - if (IsBsd()) { - EXPECT_STREQ("~{INT,ABRT,KILL,STOP,USR1}", DescribeSigset(0, &ss)); + if (IsOpenbsd() || IsXnu()) { + EXPECT_STREQ("~{INT,ABRT,CANCEL,KILL,STOP,USR1}", DescribeSigset(0, &ss)); + } else if (IsFreebsd() || IsNetbsd()) { + EXPECT_STREQ("~{INT,ABRT,KILL,STOP,USR1,CANCEL}", DescribeSigset(0, &ss)); } else { - EXPECT_STREQ("~{INT,ABRT,KILL,USR1,STOP}", DescribeSigset(0, &ss)); + EXPECT_STREQ("~{INT,ABRT,KILL,USR1,STOP,CANCEL}", DescribeSigset(0, &ss)); } } diff --git a/test/libc/intrin/strsignal_r_test.c b/test/libc/intrin/strsignal_r_test.c index e4c4a2cd1..e0636cdb0 100644 --- a/test/libc/intrin/strsignal_r_test.c +++ b/test/libc/intrin/strsignal_r_test.c @@ -34,6 +34,5 @@ TEST(strsignal, test) { TEST(strsignal, realtime) { if (!SIGRTMIN) return; - ASSERT_STREQ("SIGRTMIN", strsignal(SIGRTMIN)); ASSERT_STREQ("SIGRTMIN+1", strsignal(SIGRTMIN + 1)); } diff --git a/test/libc/thread/pthread_cancel_test.c b/test/libc/thread/pthread_cancel_test.c new file mode 100644 index 000000000..16eb3e0bf --- /dev/null +++ b/test/libc/thread/pthread_cancel_test.c @@ -0,0 +1,111 @@ +/*-*- 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/atomic.h" +#include "libc/calls/blocksigs.internal.h" +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/runtime/runtime.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" + +int pfds[2]; +atomic_bool gotcleanup; +char testlib_enable_tmp_setup_teardown; + +void SetUp(void) { + gotcleanup = false; +} + +void OnCleanup(void *arg) { + gotcleanup = true; +} + +void *Worker(void *arg) { + int n; + char buf[8]; + pthread_cleanup_push(OnCleanup, 0); + read(pfds[0], buf, sizeof(buf)); + pthread_cleanup_pop(0); + return 0; +} + +TEST(pthread_cancel, synchronous) { + void *rc; + pthread_t th; + ASSERT_SYS(0, 0, pipe(pfds)); + ASSERT_EQ(0, pthread_create(&th, 0, Worker, 0)); + ASSERT_EQ(0, pthread_cancel(th)); + ASSERT_EQ(0, pthread_join(th, &rc)); + ASSERT_EQ(PTHREAD_CANCELED, rc); + ASSERT_TRUE(gotcleanup); + ASSERT_SYS(0, 0, close(pfds[1])); + ASSERT_SYS(0, 0, close(pfds[0])); +} + +TEST(pthread_cancel, synchronous_delayed) { + void *rc; + pthread_t th; + ASSERT_SYS(0, 0, pipe(pfds)); + ASSERT_EQ(0, pthread_create(&th, 0, Worker, 0)); + usleep(10); + ASSERT_EQ(0, pthread_cancel(th)); + ASSERT_EQ(0, pthread_join(th, &rc)); + ASSERT_EQ(PTHREAD_CANCELED, rc); + ASSERT_TRUE(gotcleanup); + ASSERT_SYS(0, 0, close(pfds[1])); + ASSERT_SYS(0, 0, close(pfds[0])); +} + +void *DisabledWorker(void *arg) { + int n; + char buf[8]; + pthread_setcancelstate(PTHREAD_CANCEL_MASKED, 0); + pthread_cleanup_push(OnCleanup, 0); + ASSERT_SYS(ECANCELED, -1, read(pfds[0], buf, sizeof(buf))); + pthread_cleanup_pop(0); + return 0; +} + +TEST(pthread_cancel, masked) { + void *rc; + pthread_t th; + ASSERT_SYS(0, 0, pipe(pfds)); + ASSERT_EQ(0, pthread_create(&th, 0, DisabledWorker, 0)); + ASSERT_EQ(0, pthread_cancel(th)); + ASSERT_EQ(0, pthread_join(th, &rc)); + ASSERT_EQ(0, rc); + ASSERT_FALSE(gotcleanup); + ASSERT_SYS(0, 0, close(pfds[1])); + ASSERT_SYS(0, 0, close(pfds[0])); +} + +TEST(pthread_cancel, masked_delayed) { + void *rc; + pthread_t th; + ASSERT_SYS(0, 0, pipe(pfds)); + ASSERT_EQ(0, pthread_create(&th, 0, DisabledWorker, 0)); + usleep(10); + ASSERT_EQ(0, pthread_cancel(th)); + ASSERT_EQ(0, pthread_join(th, &rc)); + ASSERT_EQ(0, rc); + ASSERT_FALSE(gotcleanup); + ASSERT_SYS(0, 0, close(pfds[1])); + ASSERT_SYS(0, 0, close(pfds[0])); +} diff --git a/third_party/musl/lockf.c b/third_party/musl/lockf.c index 2e6a26b6e..8d729554e 100644 --- a/third_party/musl/lockf.c +++ b/third_party/musl/lockf.c @@ -39,6 +39,11 @@ Copyright 2005-2014 Rich Felker, et. al.\""); asm(".include \"libc/disclaimer.inc\""); // clang-format off +/** + * Locks file. + * + * @cancellationpoint when `op` is `F_LOCK` + */ int lockf(int fd, int op, off_t size) { struct flock l = { diff --git a/third_party/nsync/futex.c b/third_party/nsync/futex.c index 377402b97..8f5c5c0b0 100644 --- a/third_party/nsync/futex.c +++ b/third_party/nsync/futex.c @@ -152,7 +152,7 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *ti int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec *timeout) { uint32_t ms; - int rc, op, fop; + int e, rc, op, fop; op = FUTEX_WAIT_; if (pshare == PTHREAD_PROCESS_PRIVATE) { @@ -170,8 +170,10 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, struct timespec * if (pshare) { goto Polyfill; } + e = errno; if (_check_interrupts (false, 0)) { - rc = -EINTR; + rc = -errno; + errno = e; } else { if (timeout) { ms = _timespec_tomillis (*timeout);