Implement support for POSIX thread cancellations

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

View file

@ -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 {

View file

@ -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

View file

@ -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())) {

View file

@ -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);

View file

@ -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);