diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 1c281fdff2..7df76eddad 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -351,8 +351,11 @@ namespace Kernel .master_tls_addr = reinterpret_cast(master_addr), .master_tls_size = master_size, .cleanup_stack = nullptr, - .id = -1, + .id = 0, .errno_ = 0, + .cancel_type = 0, + .cancel_state = 0, + .canceled = 0, }; const uintptr_t dtv[2] { 1, region->vaddr() }; diff --git a/kernel/kernel/Thread.cpp b/kernel/kernel/Thread.cpp index 542e4f38f4..baadcd9d7b 100644 --- a/kernel/kernel/Thread.cpp +++ b/kernel/kernel/Thread.cpp @@ -525,6 +525,8 @@ namespace Kernel // Ignore the signal case SIGCHLD: case SIGURG: + case SIGWINCH: + case SIGCANCEL: break; // Stop the process: diff --git a/userspace/libraries/LibC/fcntl.cpp b/userspace/libraries/LibC/fcntl.cpp index a48199ff0e..a679eaed7d 100644 --- a/userspace/libraries/LibC/fcntl.cpp +++ b/userspace/libraries/LibC/fcntl.cpp @@ -1,15 +1,19 @@ #include +#include #include #include #include int creat(const char* path, mode_t mode) { + pthread_testcancel(); return open(path, O_WRONLY | O_CREAT | O_TRUNC, mode); } int open(const char* path, int oflag, ...) { + pthread_testcancel(); + va_list args; va_start(args, oflag); mode_t mode = va_arg(args, mode_t); @@ -20,6 +24,8 @@ int open(const char* path, int oflag, ...) int openat(int fd, const char* path, int oflag, ...) { + pthread_testcancel(); + va_list args; va_start(args, oflag); mode_t mode = va_arg(args, mode_t); @@ -30,6 +36,8 @@ int openat(int fd, const char* path, int oflag, ...) int fcntl(int fildes, int cmd, ...) { + pthread_testcancel(); + va_list args; va_start(args, cmd); int extra = va_arg(args, int); diff --git a/userspace/libraries/LibC/include/pthread.h b/userspace/libraries/LibC/include/pthread.h index bab9d0da2d..d654056ae8 100644 --- a/userspace/libraries/LibC/include/pthread.h +++ b/userspace/libraries/LibC/include/pthread.h @@ -32,10 +32,13 @@ struct uthread struct _pthread_cleanup_t* cleanup_stack; pthread_t id; int errno_; + int cancel_type; + int cancel_state; + int canceled; uintptr_t dtv[]; }; -#define PTHREAD_CANCELED 1 +#define PTHREAD_CANCELED (void*)1 #define PTHREAD_CANCEL_ASYNCHRONOUS 1 #define PTHREAD_CANCEL_DEFERRED 0 @@ -43,6 +46,9 @@ struct uthread #define PTHREAD_CANCEL_DISABLE 0 #define PTHREAD_CANCEL_ENABLE 1 +// cancellation points +// https://pubs.opengroup.org/onlinepubs/9799919799/functions/V2_chap02.html#tag_16_09_05_02 + #define PTHREAD_PRIO_INHERIT 1 #define PTHREAD_PRIO_NONE 0 #define PTHREAD_PRIO_PROTECT 2 diff --git a/userspace/libraries/LibC/include/signal.h b/userspace/libraries/LibC/include/signal.h index 7a525d860b..07c082722c 100644 --- a/userspace/libraries/LibC/include/signal.h +++ b/userspace/libraries/LibC/include/signal.h @@ -106,7 +106,8 @@ struct sigevent #define SIGXCPU 27 #define SIGXFSZ 28 #define SIGWINCH 29 -#define SIGRTMIN 30 +#define SIGCANCEL 30 +#define SIGRTMIN 31 #define SIGRTMAX (SIGRTMIN+32) #define NSIG SIGRTMAX diff --git a/userspace/libraries/LibC/poll.cpp b/userspace/libraries/LibC/poll.cpp index 290978d696..8f0e9e5693 100644 --- a/userspace/libraries/LibC/poll.cpp +++ b/userspace/libraries/LibC/poll.cpp @@ -1,9 +1,11 @@ #include +#include #include #include int poll(struct pollfd fds[], nfds_t nfds, int timeout) { + pthread_testcancel(); if (timeout < 0) return ppoll(fds, nfds, nullptr, nullptr); const timespec timeout_ts { @@ -15,5 +17,6 @@ int poll(struct pollfd fds[], nfds_t nfds, int timeout) int ppoll(struct pollfd fds[], nfds_t nfds, const struct timespec* timeout, const sigset_t* sigmask) { + pthread_testcancel(); return syscall(SYS_PPOLL, fds, nfds, timeout, sigmask); } diff --git a/userspace/libraries/LibC/pthread.cpp b/userspace/libraries/LibC/pthread.cpp index cea7510361..6e553761a6 100644 --- a/userspace/libraries/LibC/pthread.cpp +++ b/userspace/libraries/LibC/pthread.cpp @@ -48,8 +48,6 @@ extern "C" void _pthread_trampoline_cpp(void* arg) { auto info = *reinterpret_cast(arg); info.uthread->id = syscall(SYS_PTHREAD_SELF); - info.uthread->errno_ = 0; - info.uthread->cleanup_stack = nullptr; syscall(SYS_SET_TLS, info.uthread); free(arg); pthread_exit(info.start_routine(info.arg)); @@ -315,14 +313,6 @@ int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize) return 0; } -int pthread_setcancelstate(int state, int* oldstate) -{ - (void)state; - (void)oldstate; - dwarnln("TODO: pthread_setcancelstate"); - return 0; -} - int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __restrict attr, void* (*start_routine)(void*), void* __restrict arg) { auto* info = static_cast(malloc(sizeof(pthread_trampoline_info_t))); @@ -350,6 +340,9 @@ int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __rest .cleanup_stack = nullptr, .id = -1, .errno_ = 0, + .cancel_type = PTHREAD_CANCEL_DEFERRED, + .cancel_state = PTHREAD_CANCEL_ENABLE, + .canceled = false, }; uthread->dtv[0] = 0; @@ -376,6 +369,9 @@ int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __rest .cleanup_stack = nullptr, .id = -1, .errno_ = 0, + .cancel_type = PTHREAD_CANCEL_DEFERRED, + .cancel_state = PTHREAD_CANCEL_ENABLE, + .canceled = 0, }; const uintptr_t self_addr = reinterpret_cast(self); @@ -449,6 +445,7 @@ int pthread_equal(pthread_t t1, pthread_t t2) int pthread_join(pthread_t thread, void** value_ptr) { + pthread_testcancel(); return syscall(SYS_PTHREAD_JOIN, thread, value_ptr); } @@ -473,6 +470,72 @@ int pthread_once(pthread_once_t* once_control, void (*init_routine)(void)) return 0; } +static void pthread_cancel_handler(int) +{ + uthread* uthread = _get_uthread(); + BAN::atomic_store(uthread->canceled, true); + if (BAN::atomic_load(uthread->cancel_state) != PTHREAD_CANCEL_ENABLE) + return; + switch (BAN::atomic_load(uthread->cancel_type)) + { + case PTHREAD_CANCEL_ASYNCHRONOUS: + pthread_exit(PTHREAD_CANCELED); + case PTHREAD_CANCEL_DEFERRED: + return; + } + ASSERT_NOT_REACHED(); +} + +int pthread_cancel(pthread_t thread) +{ + signal(SIGCANCEL, &pthread_cancel_handler); + return pthread_kill(thread, SIGCANCEL); +} + +int pthread_setcancelstate(int state, int* oldstate) +{ + switch (state) + { + case PTHREAD_CANCEL_ENABLE: + case PTHREAD_CANCEL_DISABLE: + break; + default: + return EINVAL; + } + + BAN::atomic_exchange(_get_uthread()->cancel_state, state); + if (oldstate) + *oldstate = state; + return 0; +} + +int pthread_setcanceltype(int type, int* oldtype) +{ + switch (type) + { + case PTHREAD_CANCEL_DEFERRED: + case PTHREAD_CANCEL_ASYNCHRONOUS: + break; + default: + return EINVAL; + } + + BAN::atomic_exchange(_get_uthread()->cancel_type, type); + if (oldtype) + *oldtype = type; + return 0; +} + +void pthread_testcancel(void) +{ + uthread* uthread = _get_uthread(); + if (BAN::atomic_load(uthread->cancel_state) != PTHREAD_CANCEL_ENABLE) + return; + if (!BAN::atomic_load(uthread->canceled)) + return; + pthread_exit(PTHREAD_CANCELED); +} + int pthread_spin_destroy(pthread_spinlock_t* lock) { (void)lock; @@ -909,11 +972,14 @@ int pthread_cond_signal(pthread_cond_t* cond) int pthread_cond_wait(pthread_cond_t* __restrict cond, pthread_mutex_t* __restrict mutex) { + // pthread_testcancel in pthread_cond_timedwait return pthread_cond_timedwait(cond, mutex, nullptr); } int pthread_cond_timedwait(pthread_cond_t* __restrict cond, pthread_mutex_t* __restrict mutex, const struct timespec* __restrict abstime) { + pthread_testcancel(); + constexpr auto has_timed_out = [](const struct timespec* abstime, clockid_t clock_id) -> bool { diff --git a/userspace/libraries/LibC/sys/mman.cpp b/userspace/libraries/LibC/sys/mman.cpp index 4dc38ae2c0..b84b900617 100644 --- a/userspace/libraries/LibC/sys/mman.cpp +++ b/userspace/libraries/LibC/sys/mman.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -25,6 +26,7 @@ int munmap(void* addr, size_t len) int msync(void* addr, size_t len, int flags) { + pthread_testcancel(); return syscall(SYS_MSYNC, addr, len, flags); } diff --git a/userspace/libraries/LibC/sys/select.cpp b/userspace/libraries/LibC/sys/select.cpp index f2cfea7839..691eb9aae2 100644 --- a/userspace/libraries/LibC/sys/select.cpp +++ b/userspace/libraries/LibC/sys/select.cpp @@ -1,9 +1,11 @@ +#include #include #include #include int pselect(int nfds, fd_set* __restrict readfds, fd_set* __restrict writefds, fd_set* __restrict errorfds, const struct timespec* __restrict timeout, const sigset_t* __restrict sigmask) { + pthread_testcancel(); sys_pselect_t arguments { .nfds = nfds, .readfds = readfds, @@ -17,6 +19,7 @@ int pselect(int nfds, fd_set* __restrict readfds, fd_set* __restrict writefds, f int select(int nfds, fd_set* __restrict readfds, fd_set* __restrict writefds, fd_set* __restrict errorfds, struct timeval* __restrict timeout) { + pthread_testcancel(); timespec* pts = nullptr; timespec ts; if (timeout) diff --git a/userspace/libraries/LibC/sys/socket.cpp b/userspace/libraries/LibC/sys/socket.cpp index f3770cc829..d6a5cd5942 100644 --- a/userspace/libraries/LibC/sys/socket.cpp +++ b/userspace/libraries/LibC/sys/socket.cpp @@ -1,14 +1,17 @@ +#include #include #include #include int accept(int socket, struct sockaddr* __restrict address, socklen_t* __restrict address_len) { + pthread_testcancel(); return accept4(socket, address, address_len, 0); } int accept4(int socket, struct sockaddr* __restrict address, socklen_t* __restrict address_len, int flags) { + pthread_testcancel(); return syscall(SYS_ACCEPT, socket, address, address_len, flags); } @@ -19,6 +22,7 @@ int bind(int socket, const struct sockaddr* address, socklen_t address_len) int connect(int socket, const struct sockaddr* address, socklen_t address_len) { + pthread_testcancel(); return syscall(SYS_CONNECT, socket, address, address_len); } @@ -29,11 +33,13 @@ int listen(int socket, int backlog) ssize_t recv(int socket, void* __restrict buffer, size_t length, int flags) { + pthread_testcancel(); return recvfrom(socket, buffer, length, flags, nullptr, nullptr); } ssize_t recvfrom(int socket, void* __restrict buffer, size_t length, int flags, struct sockaddr* __restrict address, socklen_t* __restrict address_len) { + pthread_testcancel(); sys_recvfrom_t arguments { .socket = socket, .buffer = buffer, @@ -47,11 +53,13 @@ ssize_t recvfrom(int socket, void* __restrict buffer, size_t length, int flags, ssize_t send(int socket, const void* message, size_t length, int flags) { + pthread_testcancel(); return sendto(socket, message, length, flags, nullptr, 0); } ssize_t sendto(int socket, const void* message, size_t length, int flags, const struct sockaddr* dest_addr, socklen_t dest_len) { + pthread_testcancel(); sys_sendto_t arguments { .socket = socket, .message = message, diff --git a/userspace/libraries/LibC/sys/uio.cpp b/userspace/libraries/LibC/sys/uio.cpp index 3871d2a8e5..65e44c947d 100644 --- a/userspace/libraries/LibC/sys/uio.cpp +++ b/userspace/libraries/LibC/sys/uio.cpp @@ -1,8 +1,11 @@ +#include #include #include ssize_t readv(int fildes, const struct iovec* iov, int iovcnt) { + pthread_testcancel(); + size_t result = 0; for (int i = 0; i < iovcnt; i++) { @@ -25,6 +28,8 @@ ssize_t readv(int fildes, const struct iovec* iov, int iovcnt) ssize_t writev(int fildes, const struct iovec* iov, int iovcnt) { + pthread_testcancel(); + size_t result = 0; for (int i = 0; i < iovcnt; i++) { diff --git a/userspace/libraries/LibC/sys/wait.cpp b/userspace/libraries/LibC/sys/wait.cpp index 95b403d420..10c91f0fba 100644 --- a/userspace/libraries/LibC/sys/wait.cpp +++ b/userspace/libraries/LibC/sys/wait.cpp @@ -1,13 +1,16 @@ +#include #include #include #include pid_t wait(int* stat_loc) { + pthread_testcancel(); return waitpid(-1, stat_loc, 0); } pid_t waitpid(pid_t pid, int* stat_loc, int options) { + pthread_testcancel(); return (pid_t)syscall(SYS_WAIT, pid, stat_loc, options); } diff --git a/userspace/libraries/LibC/termios.cpp b/userspace/libraries/LibC/termios.cpp index 28974701f3..d6f1b250e1 100644 --- a/userspace/libraries/LibC/termios.cpp +++ b/userspace/libraries/LibC/termios.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -64,9 +65,19 @@ int cfsetospeed(struct termios* termios, speed_t speed) return 0; } -int tcdrain(int); +int tcdrain(int fd) +{ + pthread_testcancel(); -int tcflow(int, int); + dwarnln("TODO: tcdrain({})", fd); + return 0; +} + +int tcflow(int fd, int action) +{ + dwarnln("TODO: tcflow({}, {})", fd, action); + return -1; +} int tcflush(int fd, int queue_selector) { @@ -81,7 +92,11 @@ int tcgetattr(int fildes, struct termios* termios) pid_t tcgetsid(int); -int tcsendbreak(int, int); +int tcsendbreak(int fd, int duration) +{ + dwarnln("FIXME: tcsendbreak({}, {})", fd, duration); + return -1; +} int tcsetattr(int fildes, int optional_actions, const struct termios* termios) { diff --git a/userspace/libraries/LibC/time.cpp b/userspace/libraries/LibC/time.cpp index c775924fe3..b966625de1 100644 --- a/userspace/libraries/LibC/time.cpp +++ b/userspace/libraries/LibC/time.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ int clock_gettime(clockid_t clock_id, struct timespec* tp) int nanosleep(const struct timespec* rqtp, struct timespec* rmtp) { + pthread_testcancel(); return syscall(SYS_NANOSLEEP, rqtp, rmtp); } diff --git a/userspace/libraries/LibC/unistd.cpp b/userspace/libraries/LibC/unistd.cpp index b9f79113f6..485a52359a 100644 --- a/userspace/libraries/LibC/unistd.cpp +++ b/userspace/libraries/LibC/unistd.cpp @@ -39,6 +39,9 @@ extern "C" void _init_libc(char** environ, init_funcs_t init_funcs, init_funcs_t self->cleanup_stack = nullptr; self->id = syscall(SYS_PTHREAD_SELF); self->errno_ = 0; + self->cancel_type = PTHREAD_CANCEL_DEFERRED; + self->cancel_state = PTHREAD_CANCEL_ENABLE; + self->canceled = false; } else { @@ -52,6 +55,9 @@ extern "C" void _init_libc(char** environ, init_funcs_t init_funcs, init_funcs_t .cleanup_stack = nullptr, .id = static_cast(syscall(SYS_PTHREAD_SELF)), .errno_ = 0, + .cancel_type = PTHREAD_CANCEL_DEFERRED, + .cancel_state = PTHREAD_CANCEL_ENABLE, + .canceled = false, }; uthread.dtv[0] = 0; @@ -108,16 +114,19 @@ long syscall(long syscall, ...) int close(int fd) { + pthread_testcancel(); return syscall(SYS_CLOSE, fd); } ssize_t read(int fildes, void* buf, size_t nbyte) { + pthread_testcancel(); return syscall(SYS_READ, fildes, buf, nbyte); } ssize_t write(int fildes, const void* buf, size_t nbyte) { + pthread_testcancel(); return syscall(SYS_WRITE, fildes, buf, nbyte); } @@ -133,11 +142,13 @@ ssize_t readlinkat(int fd, const char* __restrict path, char* __restrict buf, si ssize_t pread(int fildes, void* buf, size_t nbyte, off_t offset) { + pthread_testcancel(); return syscall(SYS_PREAD, fildes, buf, nbyte, offset); } ssize_t pwrite(int fildes, const void* buf, size_t nbyte, off_t offset) { + pthread_testcancel(); return syscall(SYS_PWRITE, fildes, buf, nbyte, offset); } @@ -153,6 +164,7 @@ int ftruncate(int fildes, off_t length) int fsync(int fildes) { + pthread_testcancel(); return syscall(SYS_FSYNC, fildes); } @@ -421,6 +433,7 @@ int pipe(int fildes[2]) unsigned int sleep(unsigned int seconds) { + pthread_testcancel(); unsigned int ret = syscall(SYS_SLEEP, seconds); if (ret > 0) errno = EINTR; @@ -489,6 +502,15 @@ void syncsync(int should_block) syscall(SYS_SYNC, should_block); } +int fdatasync(int fildes) +{ + pthread_testcancel(); + + (void)fildes; + dprintln("TODO: fdatasync"); + return syscall(SYS_SYNC, true); +} + int unlink(const char* path) { return syscall(SYS_UNLINK, path);