[398] | 1 | #define _GNU_SOURCE
|
---|
| 2 | #include <string.h>
|
---|
| 3 | #include "pthread_impl.h"
|
---|
| 4 | #include "syscall.h"
|
---|
| 5 | #include "libc.h"
|
---|
| 6 |
|
---|
| 7 | __attribute__((__visibility__("hidden")))
|
---|
| 8 | long __cancel(), __syscall_cp_asm(), __syscall_cp_c();
|
---|
| 9 |
|
---|
| 10 | long __cancel()
|
---|
| 11 | {
|
---|
| 12 | pthread_t self = __pthread_self();
|
---|
| 13 | if (self->canceldisable == PTHREAD_CANCEL_ENABLE || self->cancelasync)
|
---|
| 14 | pthread_exit(PTHREAD_CANCELED);
|
---|
| 15 | self->canceldisable = PTHREAD_CANCEL_DISABLE;
|
---|
| 16 | return -ECANCELED;
|
---|
| 17 | }
|
---|
| 18 |
|
---|
| 19 | long __syscall_cp_asm(volatile void *, syscall_arg_t,
|
---|
| 20 | syscall_arg_t, syscall_arg_t, syscall_arg_t,
|
---|
| 21 | syscall_arg_t, syscall_arg_t, syscall_arg_t);
|
---|
| 22 |
|
---|
| 23 | long __syscall_cp_c(syscall_arg_t nr,
|
---|
| 24 | syscall_arg_t u, syscall_arg_t v, syscall_arg_t w,
|
---|
| 25 | syscall_arg_t x, syscall_arg_t y, syscall_arg_t z)
|
---|
| 26 | {
|
---|
| 27 | pthread_t self;
|
---|
| 28 | long r;
|
---|
| 29 | int st;
|
---|
| 30 |
|
---|
| 31 | if ((st=(self=__pthread_self())->canceldisable)
|
---|
| 32 | && (st==PTHREAD_CANCEL_DISABLE || nr==SYS_close))
|
---|
| 33 | return __syscall_nr(nr, u, v, w, x, y, z);
|
---|
| 34 |
|
---|
| 35 | r = __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z);
|
---|
| 36 | if (r==-EINTR && nr!=SYS_close && self->cancel &&
|
---|
| 37 | self->canceldisable != PTHREAD_CANCEL_DISABLE)
|
---|
| 38 | r = __cancel();
|
---|
| 39 | return r;
|
---|
| 40 | }
|
---|
| 41 |
|
---|
| 42 | static void _sigaddset(sigset_t *set, int sig)
|
---|
| 43 | {
|
---|
| 44 | unsigned s = sig-1;
|
---|
| 45 | set->__bits[s/8/sizeof *set->__bits] |= 1UL<<(s&8*sizeof *set->__bits-1);
|
---|
| 46 | }
|
---|
| 47 |
|
---|
| 48 | __attribute__((__visibility__("hidden")))
|
---|
| 49 | extern const char __cp_begin[1], __cp_end[1], __cp_cancel[1];
|
---|
| 50 |
|
---|
| 51 | static void cancel_handler(int sig, siginfo_t *si, void *ctx)
|
---|
| 52 | {
|
---|
| 53 | pthread_t self = __pthread_self();
|
---|
| 54 | ucontext_t *uc = ctx;
|
---|
| 55 | uintptr_t pc = uc->uc_mcontext.MC_PC;
|
---|
| 56 |
|
---|
| 57 | a_barrier();
|
---|
| 58 | if (!self->cancel || self->canceldisable == PTHREAD_CANCEL_DISABLE) return;
|
---|
| 59 |
|
---|
| 60 | _sigaddset(&uc->uc_sigmask, SIGCANCEL);
|
---|
| 61 |
|
---|
| 62 | if (self->cancelasync || pc >= (uintptr_t)__cp_begin && pc < (uintptr_t)__cp_end) {
|
---|
| 63 | uc->uc_mcontext.MC_PC = (uintptr_t)__cp_cancel;
|
---|
| 64 | return;
|
---|
| 65 | }
|
---|
| 66 |
|
---|
| 67 | __syscall(SYS_tkill, self->tid, SIGCANCEL);
|
---|
| 68 | }
|
---|
| 69 |
|
---|
| 70 | void __testcancel()
|
---|
| 71 | {
|
---|
| 72 | pthread_t self = __pthread_self();
|
---|
| 73 | if (self->cancel && !self->canceldisable)
|
---|
| 74 | __cancel();
|
---|
| 75 | }
|
---|
| 76 |
|
---|
| 77 | static void init_cancellation()
|
---|
| 78 | {
|
---|
| 79 | struct sigaction sa = {
|
---|
| 80 | .sa_flags = SA_SIGINFO | SA_RESTART,
|
---|
| 81 | .sa_sigaction = cancel_handler
|
---|
| 82 | };
|
---|
| 83 | memset(&sa.sa_mask, -1, _NSIG/8);
|
---|
| 84 | __libc_sigaction(SIGCANCEL, &sa, 0);
|
---|
| 85 | }
|
---|
| 86 |
|
---|
| 87 | int pthread_cancel(pthread_t t)
|
---|
| 88 | {
|
---|
| 89 | static int init;
|
---|
| 90 | if (!init) {
|
---|
| 91 | init_cancellation();
|
---|
| 92 | init = 1;
|
---|
| 93 | }
|
---|
| 94 | a_store(&t->cancel, 1);
|
---|
| 95 | if (t == pthread_self() && !t->cancelasync) return 0;
|
---|
| 96 | return pthread_kill(t, SIGCANCEL);
|
---|
| 97 | }
|
---|