LibC: Implement pthread cancelation

This code is not tested at all but it looks correct xD
This commit is contained in:
Bananymous 2025-06-01 00:59:22 +03:00
parent 56fdf6002c
commit dbdefa0f4a
15 changed files with 165 additions and 16 deletions

View File

@ -351,8 +351,11 @@ namespace Kernel
.master_tls_addr = reinterpret_cast<void*>(master_addr), .master_tls_addr = reinterpret_cast<void*>(master_addr),
.master_tls_size = master_size, .master_tls_size = master_size,
.cleanup_stack = nullptr, .cleanup_stack = nullptr,
.id = -1, .id = 0,
.errno_ = 0, .errno_ = 0,
.cancel_type = 0,
.cancel_state = 0,
.canceled = 0,
}; };
const uintptr_t dtv[2] { 1, region->vaddr() }; const uintptr_t dtv[2] { 1, region->vaddr() };

View File

@ -525,6 +525,8 @@ namespace Kernel
// Ignore the signal // Ignore the signal
case SIGCHLD: case SIGCHLD:
case SIGURG: case SIGURG:
case SIGWINCH:
case SIGCANCEL:
break; break;
// Stop the process: // Stop the process:

View File

@ -1,15 +1,19 @@
#include <fcntl.h> #include <fcntl.h>
#include <pthread.h>
#include <stdarg.h> #include <stdarg.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
int creat(const char* path, mode_t mode) int creat(const char* path, mode_t mode)
{ {
pthread_testcancel();
return open(path, O_WRONLY | O_CREAT | O_TRUNC, mode); return open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
} }
int open(const char* path, int oflag, ...) int open(const char* path, int oflag, ...)
{ {
pthread_testcancel();
va_list args; va_list args;
va_start(args, oflag); va_start(args, oflag);
mode_t mode = va_arg(args, mode_t); 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, ...) int openat(int fd, const char* path, int oflag, ...)
{ {
pthread_testcancel();
va_list args; va_list args;
va_start(args, oflag); va_start(args, oflag);
mode_t mode = va_arg(args, mode_t); 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, ...) int fcntl(int fildes, int cmd, ...)
{ {
pthread_testcancel();
va_list args; va_list args;
va_start(args, cmd); va_start(args, cmd);
int extra = va_arg(args, int); int extra = va_arg(args, int);

View File

@ -32,10 +32,13 @@ struct uthread
struct _pthread_cleanup_t* cleanup_stack; struct _pthread_cleanup_t* cleanup_stack;
pthread_t id; pthread_t id;
int errno_; int errno_;
int cancel_type;
int cancel_state;
int canceled;
uintptr_t dtv[]; uintptr_t dtv[];
}; };
#define PTHREAD_CANCELED 1 #define PTHREAD_CANCELED (void*)1
#define PTHREAD_CANCEL_ASYNCHRONOUS 1 #define PTHREAD_CANCEL_ASYNCHRONOUS 1
#define PTHREAD_CANCEL_DEFERRED 0 #define PTHREAD_CANCEL_DEFERRED 0
@ -43,6 +46,9 @@ struct uthread
#define PTHREAD_CANCEL_DISABLE 0 #define PTHREAD_CANCEL_DISABLE 0
#define PTHREAD_CANCEL_ENABLE 1 #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_INHERIT 1
#define PTHREAD_PRIO_NONE 0 #define PTHREAD_PRIO_NONE 0
#define PTHREAD_PRIO_PROTECT 2 #define PTHREAD_PRIO_PROTECT 2

View File

@ -106,7 +106,8 @@ struct sigevent
#define SIGXCPU 27 #define SIGXCPU 27
#define SIGXFSZ 28 #define SIGXFSZ 28
#define SIGWINCH 29 #define SIGWINCH 29
#define SIGRTMIN 30 #define SIGCANCEL 30
#define SIGRTMIN 31
#define SIGRTMAX (SIGRTMIN+32) #define SIGRTMAX (SIGRTMIN+32)
#define NSIG SIGRTMAX #define NSIG SIGRTMAX

View File

@ -1,9 +1,11 @@
#include <poll.h> #include <poll.h>
#include <pthread.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout) int poll(struct pollfd fds[], nfds_t nfds, int timeout)
{ {
pthread_testcancel();
if (timeout < 0) if (timeout < 0)
return ppoll(fds, nfds, nullptr, nullptr); return ppoll(fds, nfds, nullptr, nullptr);
const timespec timeout_ts { 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) 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); return syscall(SYS_PPOLL, fds, nfds, timeout, sigmask);
} }

View File

@ -48,8 +48,6 @@ extern "C" void _pthread_trampoline_cpp(void* arg)
{ {
auto info = *reinterpret_cast<pthread_trampoline_info_t*>(arg); auto info = *reinterpret_cast<pthread_trampoline_info_t*>(arg);
info.uthread->id = syscall(SYS_PTHREAD_SELF); info.uthread->id = syscall(SYS_PTHREAD_SELF);
info.uthread->errno_ = 0;
info.uthread->cleanup_stack = nullptr;
syscall(SYS_SET_TLS, info.uthread); syscall(SYS_SET_TLS, info.uthread);
free(arg); free(arg);
pthread_exit(info.start_routine(info.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; 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) 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<pthread_trampoline_info_t*>(malloc(sizeof(pthread_trampoline_info_t))); auto* info = static_cast<pthread_trampoline_info_t*>(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, .cleanup_stack = nullptr,
.id = -1, .id = -1,
.errno_ = 0, .errno_ = 0,
.cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = false,
}; };
uthread->dtv[0] = 0; uthread->dtv[0] = 0;
@ -376,6 +369,9 @@ int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __rest
.cleanup_stack = nullptr, .cleanup_stack = nullptr,
.id = -1, .id = -1,
.errno_ = 0, .errno_ = 0,
.cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = 0,
}; };
const uintptr_t self_addr = reinterpret_cast<uintptr_t>(self); const uintptr_t self_addr = reinterpret_cast<uintptr_t>(self);
@ -449,6 +445,7 @@ int pthread_equal(pthread_t t1, pthread_t t2)
int pthread_join(pthread_t thread, void** value_ptr) int pthread_join(pthread_t thread, void** value_ptr)
{ {
pthread_testcancel();
return syscall(SYS_PTHREAD_JOIN, thread, value_ptr); 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; 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) int pthread_spin_destroy(pthread_spinlock_t* lock)
{ {
(void)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) 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); 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) 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 = constexpr auto has_timed_out =
[](const struct timespec* abstime, clockid_t clock_id) -> bool [](const struct timespec* abstime, clockid_t clock_id) -> bool
{ {

View File

@ -1,3 +1,4 @@
#include <pthread.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
@ -25,6 +26,7 @@ int munmap(void* addr, size_t len)
int msync(void* addr, size_t len, int flags) int msync(void* addr, size_t len, int flags)
{ {
pthread_testcancel();
return syscall(SYS_MSYNC, addr, len, flags); return syscall(SYS_MSYNC, addr, len, flags);
} }

View File

@ -1,9 +1,11 @@
#include <pthread.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
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) 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 { sys_pselect_t arguments {
.nfds = nfds, .nfds = nfds,
.readfds = readfds, .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) 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* pts = nullptr;
timespec ts; timespec ts;
if (timeout) if (timeout)

View File

@ -1,14 +1,17 @@
#include <pthread.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
int accept(int socket, struct sockaddr* __restrict address, socklen_t* __restrict address_len) int accept(int socket, struct sockaddr* __restrict address, socklen_t* __restrict address_len)
{ {
pthread_testcancel();
return accept4(socket, address, address_len, 0); return accept4(socket, address, address_len, 0);
} }
int accept4(int socket, struct sockaddr* __restrict address, socklen_t* __restrict address_len, int flags) 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); 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) int connect(int socket, const struct sockaddr* address, socklen_t address_len)
{ {
pthread_testcancel();
return syscall(SYS_CONNECT, socket, address, address_len); 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) ssize_t recv(int socket, void* __restrict buffer, size_t length, int flags)
{ {
pthread_testcancel();
return recvfrom(socket, buffer, length, flags, nullptr, nullptr); 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) 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 { sys_recvfrom_t arguments {
.socket = socket, .socket = socket,
.buffer = buffer, .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) ssize_t send(int socket, const void* message, size_t length, int flags)
{ {
pthread_testcancel();
return sendto(socket, message, length, flags, nullptr, 0); 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) 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 { sys_sendto_t arguments {
.socket = socket, .socket = socket,
.message = message, .message = message,

View File

@ -1,8 +1,11 @@
#include <pthread.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <unistd.h> #include <unistd.h>
ssize_t readv(int fildes, const struct iovec* iov, int iovcnt) ssize_t readv(int fildes, const struct iovec* iov, int iovcnt)
{ {
pthread_testcancel();
size_t result = 0; size_t result = 0;
for (int i = 0; i < iovcnt; i++) 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) ssize_t writev(int fildes, const struct iovec* iov, int iovcnt)
{ {
pthread_testcancel();
size_t result = 0; size_t result = 0;
for (int i = 0; i < iovcnt; i++) for (int i = 0; i < iovcnt; i++)
{ {

View File

@ -1,13 +1,16 @@
#include <pthread.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
pid_t wait(int* stat_loc) pid_t wait(int* stat_loc)
{ {
pthread_testcancel();
return waitpid(-1, stat_loc, 0); return waitpid(-1, stat_loc, 0);
} }
pid_t waitpid(pid_t pid, int* stat_loc, int options) pid_t waitpid(pid_t pid, int* stat_loc, int options)
{ {
pthread_testcancel();
return (pid_t)syscall(SYS_WAIT, pid, stat_loc, options); return (pid_t)syscall(SYS_WAIT, pid, stat_loc, options);
} }

View File

@ -2,6 +2,7 @@
#include <BAN/Debug.h> #include <BAN/Debug.h>
#include <errno.h> #include <errno.h>
#include <pthread.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
@ -64,9 +65,19 @@ int cfsetospeed(struct termios* termios, speed_t speed)
return 0; 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) int tcflush(int fd, int queue_selector)
{ {
@ -81,7 +92,11 @@ int tcgetattr(int fildes, struct termios* termios)
pid_t tcgetsid(int); 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) int tcsetattr(int fildes, int optional_actions, const struct termios* termios)
{ {

View File

@ -4,6 +4,7 @@
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <langinfo.h> #include <langinfo.h>
#include <pthread.h>
#include <string.h> #include <string.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <time.h> #include <time.h>
@ -20,6 +21,7 @@ int clock_gettime(clockid_t clock_id, struct timespec* tp)
int nanosleep(const struct timespec* rqtp, struct timespec* rmtp) int nanosleep(const struct timespec* rqtp, struct timespec* rmtp)
{ {
pthread_testcancel();
return syscall(SYS_NANOSLEEP, rqtp, rmtp); return syscall(SYS_NANOSLEEP, rqtp, rmtp);
} }

View File

@ -39,6 +39,9 @@ extern "C" void _init_libc(char** environ, init_funcs_t init_funcs, init_funcs_t
self->cleanup_stack = nullptr; self->cleanup_stack = nullptr;
self->id = syscall(SYS_PTHREAD_SELF); self->id = syscall(SYS_PTHREAD_SELF);
self->errno_ = 0; self->errno_ = 0;
self->cancel_type = PTHREAD_CANCEL_DEFERRED;
self->cancel_state = PTHREAD_CANCEL_ENABLE;
self->canceled = false;
} }
else else
{ {
@ -52,6 +55,9 @@ extern "C" void _init_libc(char** environ, init_funcs_t init_funcs, init_funcs_t
.cleanup_stack = nullptr, .cleanup_stack = nullptr,
.id = static_cast<pthread_t>(syscall(SYS_PTHREAD_SELF)), .id = static_cast<pthread_t>(syscall(SYS_PTHREAD_SELF)),
.errno_ = 0, .errno_ = 0,
.cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = false,
}; };
uthread.dtv[0] = 0; uthread.dtv[0] = 0;
@ -108,16 +114,19 @@ long syscall(long syscall, ...)
int close(int fd) int close(int fd)
{ {
pthread_testcancel();
return syscall(SYS_CLOSE, fd); return syscall(SYS_CLOSE, fd);
} }
ssize_t read(int fildes, void* buf, size_t nbyte) ssize_t read(int fildes, void* buf, size_t nbyte)
{ {
pthread_testcancel();
return syscall(SYS_READ, fildes, buf, nbyte); return syscall(SYS_READ, fildes, buf, nbyte);
} }
ssize_t write(int fildes, const void* buf, size_t nbyte) ssize_t write(int fildes, const void* buf, size_t nbyte)
{ {
pthread_testcancel();
return syscall(SYS_WRITE, fildes, buf, nbyte); 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) ssize_t pread(int fildes, void* buf, size_t nbyte, off_t offset)
{ {
pthread_testcancel();
return syscall(SYS_PREAD, fildes, buf, nbyte, offset); return syscall(SYS_PREAD, fildes, buf, nbyte, offset);
} }
ssize_t pwrite(int fildes, const void* buf, size_t nbyte, off_t 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); return syscall(SYS_PWRITE, fildes, buf, nbyte, offset);
} }
@ -153,6 +164,7 @@ int ftruncate(int fildes, off_t length)
int fsync(int fildes) int fsync(int fildes)
{ {
pthread_testcancel();
return syscall(SYS_FSYNC, fildes); return syscall(SYS_FSYNC, fildes);
} }
@ -421,6 +433,7 @@ int pipe(int fildes[2])
unsigned int sleep(unsigned int seconds) unsigned int sleep(unsigned int seconds)
{ {
pthread_testcancel();
unsigned int ret = syscall(SYS_SLEEP, seconds); unsigned int ret = syscall(SYS_SLEEP, seconds);
if (ret > 0) if (ret > 0)
errno = EINTR; errno = EINTR;
@ -489,6 +502,15 @@ void syncsync(int should_block)
syscall(SYS_SYNC, 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) int unlink(const char* path)
{ {
return syscall(SYS_UNLINK, path); return syscall(SYS_UNLINK, path);