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