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