]>
Commit | Line | Data |
---|---|---|
320054e8 DG |
1 | #include "pthread_impl.h" |
2 | #include <semaphore.h> | |
320054e8 | 3 | #include <string.h> |
f41256b6 DG |
4 | |
5 | static void dummy_0(void) | |
6 | { | |
7 | } | |
8 | ||
9 | weak_alias(dummy_0, __tl_lock); | |
10 | weak_alias(dummy_0, __tl_unlock); | |
11 | ||
12 | static int target_tid; | |
320054e8 | 13 | static void (*callback)(void *), *context; |
f41256b6 DG |
14 | static sem_t target_sem, caller_sem; |
15 | ||
16 | static void dummy(void *p) | |
17 | { | |
18 | } | |
320054e8 DG |
19 | |
20 | static void handler(int sig) | |
21 | { | |
f41256b6 | 22 | if (__pthread_self()->tid != target_tid) return; |
320054e8 | 23 | |
f41256b6 | 24 | int old_errno = errno; |
320054e8 | 25 | |
f41256b6 DG |
26 | /* Inform caller we have received signal and wait for |
27 | * the caller to let us make the callback. */ | |
28 | sem_post(&caller_sem); | |
29 | sem_wait(&target_sem); | |
320054e8 | 30 | |
f41256b6 | 31 | callback(context); |
320054e8 | 32 | |
f41256b6 DG |
33 | /* Inform caller we've complered the callback and wait |
34 | * for the caller to release us to return. */ | |
35 | sem_post(&caller_sem); | |
36 | sem_wait(&target_sem); | |
320054e8 | 37 | |
f41256b6 DG |
38 | /* Inform caller we are returning and state is destroyable. */ |
39 | sem_post(&caller_sem); | |
320054e8 DG |
40 | |
41 | errno = old_errno; | |
42 | } | |
43 | ||
44 | void __synccall(void (*func)(void *), void *ctx) | |
45 | { | |
46 | sigset_t oldmask; | |
f41256b6 | 47 | int cs, i, r; |
320054e8 | 48 | struct sigaction sa = { .sa_flags = SA_RESTART, .sa_handler = handler }; |
f41256b6 DG |
49 | pthread_t self = __pthread_self(), td; |
50 | int count = 0; | |
320054e8 DG |
51 | |
52 | /* Blocking signals in two steps, first only app-level signals | |
53 | * before taking the lock, then all signals after taking the lock, | |
54 | * is necessary to achieve AS-safety. Blocking them all first would | |
55 | * deadlock if multiple threads called __synccall. Waiting to block | |
56 | * any until after the lock would allow re-entry in the same thread | |
57 | * with the lock already held. */ | |
58 | __block_app_sigs(&oldmask); | |
f41256b6 | 59 | __tl_lock(); |
320054e8 DG |
60 | __block_all_sigs(0); |
61 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); | |
62 | ||
f41256b6 DG |
63 | sem_init(&target_sem, 0, 0); |
64 | sem_init(&caller_sem, 0, 0); | |
320054e8 | 65 | |
f41256b6 | 66 | if (!libc.threads_minus_1) goto single_threaded; |
320054e8 DG |
67 | |
68 | callback = func; | |
69 | context = ctx; | |
70 | ||
320054e8 DG |
71 | /* Block even implementation-internal signals, so that nothing |
72 | * interrupts the SIGSYNCCALL handlers. The main possible source | |
73 | * of trouble is asynchronous cancellation. */ | |
74 | memset(&sa.sa_mask, -1, sizeof sa.sa_mask); | |
75 | __libc_sigaction(SIGSYNCCALL, &sa, 0); | |
76 | ||
f41256b6 DG |
77 | |
78 | for (td=self->next; td!=self; td=td->next) { | |
79 | target_tid = td->tid; | |
80 | while ((r = -__syscall(SYS_tkill, td->tid, SIGSYNCCALL)) == EAGAIN); | |
81 | if (r) { | |
82 | /* If we failed to signal any thread, nop out the | |
83 | * callback to abort the synccall and just release | |
84 | * any threads already caught. */ | |
85 | callback = func = dummy; | |
86 | break; | |
320054e8 | 87 | } |
f41256b6 DG |
88 | sem_wait(&caller_sem); |
89 | count++; | |
320054e8 | 90 | } |
f41256b6 | 91 | target_tid = 0; |
320054e8 | 92 | |
f41256b6 DG |
93 | /* Serialize execution of callback in caught threads, or just |
94 | * release them all if synccall is being aborted. */ | |
95 | for (i=0; i<count; i++) { | |
96 | sem_post(&target_sem); | |
97 | sem_wait(&caller_sem); | |
320054e8 DG |
98 | } |
99 | ||
100 | sa.sa_handler = SIG_IGN; | |
101 | __libc_sigaction(SIGSYNCCALL, &sa, 0); | |
102 | ||
103 | single_threaded: | |
104 | func(ctx); | |
105 | ||
106 | /* Only release the caught threads once all threads, including the | |
107 | * caller, have returned from the callback function. */ | |
f41256b6 DG |
108 | for (i=0; i<count; i++) |
109 | sem_post(&target_sem); | |
110 | for (i=0; i<count; i++) | |
111 | sem_wait(&caller_sem); | |
320054e8 | 112 | |
f41256b6 DG |
113 | sem_destroy(&caller_sem); |
114 | sem_destroy(&target_sem); | |
320054e8 DG |
115 | |
116 | pthread_setcancelstate(cs, 0); | |
f41256b6 | 117 | __tl_unlock(); |
320054e8 DG |
118 | __restore_sigs(&oldmask); |
119 | } |