diff options
Diffstat (limited to 'libc-top-half/musl/src/thread/pthread_cancel.c')
-rw-r--r-- | libc-top-half/musl/src/thread/pthread_cancel.c | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/libc-top-half/musl/src/thread/pthread_cancel.c b/libc-top-half/musl/src/thread/pthread_cancel.c new file mode 100644 index 0000000..2f9d5e9 --- /dev/null +++ b/libc-top-half/musl/src/thread/pthread_cancel.c @@ -0,0 +1,101 @@ +#define _GNU_SOURCE +#include <string.h> +#include "pthread_impl.h" +#include "syscall.h" + +hidden long __cancel(), __syscall_cp_asm(), __syscall_cp_c(); + +long __cancel() +{ + pthread_t self = __pthread_self(); + if (self->canceldisable == PTHREAD_CANCEL_ENABLE || self->cancelasync) + pthread_exit(PTHREAD_CANCELED); + self->canceldisable = PTHREAD_CANCEL_DISABLE; + return -ECANCELED; +} + +long __syscall_cp_asm(volatile void *, syscall_arg_t, + syscall_arg_t, syscall_arg_t, syscall_arg_t, + syscall_arg_t, syscall_arg_t, syscall_arg_t); + +long __syscall_cp_c(syscall_arg_t nr, + syscall_arg_t u, syscall_arg_t v, syscall_arg_t w, + syscall_arg_t x, syscall_arg_t y, syscall_arg_t z) +{ + pthread_t self; + long r; + int st; + + if ((st=(self=__pthread_self())->canceldisable) + && (st==PTHREAD_CANCEL_DISABLE || nr==SYS_close)) + return __syscall(nr, u, v, w, x, y, z); + + r = __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z); + if (r==-EINTR && nr!=SYS_close && self->cancel && + self->canceldisable != PTHREAD_CANCEL_DISABLE) + r = __cancel(); + return r; +} + +static void _sigaddset(sigset_t *set, int sig) +{ + unsigned s = sig-1; + set->__bits[s/8/sizeof *set->__bits] |= 1UL<<(s&8*sizeof *set->__bits-1); +} + +extern hidden const char __cp_begin[1], __cp_end[1], __cp_cancel[1]; + +static void cancel_handler(int sig, siginfo_t *si, void *ctx) +{ + pthread_t self = __pthread_self(); + ucontext_t *uc = ctx; + uintptr_t pc = uc->uc_mcontext.MC_PC; + + a_barrier(); + if (!self->cancel || self->canceldisable == PTHREAD_CANCEL_DISABLE) return; + + _sigaddset(&uc->uc_sigmask, SIGCANCEL); + + if (self->cancelasync || pc >= (uintptr_t)__cp_begin && pc < (uintptr_t)__cp_end) { + uc->uc_mcontext.MC_PC = (uintptr_t)__cp_cancel; +#ifdef CANCEL_GOT + uc->uc_mcontext.MC_GOT = CANCEL_GOT; +#endif + return; + } + + __syscall(SYS_tkill, self->tid, SIGCANCEL); +} + +void __testcancel() +{ + pthread_t self = __pthread_self(); + if (self->cancel && !self->canceldisable) + __cancel(); +} + +static void init_cancellation() +{ + struct sigaction sa = { + .sa_flags = SA_SIGINFO | SA_RESTART, + .sa_sigaction = cancel_handler + }; + memset(&sa.sa_mask, -1, _NSIG/8); + __libc_sigaction(SIGCANCEL, &sa, 0); +} + +int pthread_cancel(pthread_t t) +{ + static int init; + if (!init) { + init_cancellation(); + init = 1; + } + a_store(&t->cancel, 1); + if (t == pthread_self()) { + if (t->canceldisable == PTHREAD_CANCEL_ENABLE && t->cancelasync) + pthread_exit(PTHREAD_CANCELED); + return 0; + } + return pthread_kill(t, SIGCANCEL); +} |