5ce545d7597c0730d1420bb8a997e25e4c75aac0
[musl] / src / thread / cancel_impl.c
1 #include "pthread_impl.h"
2
3 long __syscall_cp_asm(volatile void *, long, long, long, long, long, long, long);
4
5 long (__syscall_cp)(long nr, long u, long v, long w, long x, long y, long z)
6 {
7         pthread_t self;
8         uintptr_t old_sp, old_ip;
9         long r;
10
11         if (!libc.lock || (self = __pthread_self())->canceldisable)
12                 return __syscall(nr, u, v, w, x, y, z);
13
14         old_sp = self->cp_sp;
15         old_ip = self->cp_ip;
16         self->cp_sp = 0;
17         self->cp_ip = 0;
18         r = __syscall_cp_asm(&self->cp_sp, nr, u, v, w, x, y, z);
19         self->cp_sp = old_sp;
20         self->cp_ip = old_ip;
21         if (r == -EINTR && self->cancel) pthread_exit(PTHREAD_CANCELED);
22         return r;
23 }
24
25 static void cancel_handler(int sig, siginfo_t *si, void *ctx)
26 {
27         pthread_t self = __pthread_self();
28         ucontext_t *uc = ctx;
29         uintptr_t sp = ((uintptr_t *)&uc->uc_mcontext)[CANCEL_REG_SP];
30         uintptr_t ip = ((uintptr_t *)&uc->uc_mcontext)[CANCEL_REG_IP];
31
32         if (!self->cancel || self->canceldisable) return;
33
34         if (self->cancelasync) pthread_exit(PTHREAD_CANCELED);
35
36         if (sp != self->cp_sp) {
37                 if (!sp) return;
38                 sigaddset(&uc->uc_sigmask, SIGCANCEL);
39                 __syscall(SYS_tgkill, self->pid, self->tid, SIGCANCEL);
40                 return;
41         }
42
43         if (ip <= self->cp_ip) pthread_exit(PTHREAD_CANCELED);
44 }
45
46 static void testcancel()
47 {
48         pthread_t self = __pthread_self();
49         if (self->cancel && !self->canceldisable)
50                 pthread_exit(PTHREAD_CANCELED);
51 }
52
53 static void init_cancellation()
54 {
55         struct sigaction sa = {
56                 .sa_flags = SA_SIGINFO | SA_RESTART,
57                 .sa_sigaction = cancel_handler
58         };
59         sigfillset(&sa.sa_mask);
60         __libc_sigaction(SIGCANCEL, &sa, 0);
61         libc.testcancel = testcancel;
62 }
63
64 int pthread_cancel(pthread_t t)
65 {
66         static pthread_once_t once;
67         pthread_once(&once, init_cancellation);
68         a_store(&t->cancel, 1);
69         return pthread_kill(t, SIGCANCEL);
70 }