source: asp3_tinet_ecnl_rx/trunk/musl-1.1.18/src/thread/synccall.c@ 337

Last change on this file since 337 was 337, checked in by coas-nagasima, 6 years ago

ASP3版ECNLを追加

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 5.1 KB
Line 
1#include "pthread_impl.h"
2#include <semaphore.h>
3#include <unistd.h>
4#include <dirent.h>
5#include <string.h>
6#include <ctype.h>
7#include "futex.h"
8#include "atomic.h"
9#include "../dirent/__dirent.h"
10
11static struct chain {
12 struct chain *next;
13 int tid;
14 sem_t target_sem, caller_sem;
15} *volatile head;
16
17static volatile int synccall_lock[2];
18static volatile int target_tid;
19static void (*callback)(void *), *context;
20#ifndef __c2__
21static volatile int dummy = 0;
22weak_alias(dummy, __block_new_threads);
23#else
24extern volatile int __block_new_threads;
25#endif
26
27static void handler(int sig)
28{
29 struct chain ch;
30 int old_errno = errno;
31
32 sem_init(&ch.target_sem, 0, 0);
33 sem_init(&ch.caller_sem, 0, 0);
34
35 ch.tid = __syscall(SYS_gettid);
36
37 do ch.next = head;
38 while (a_cas_p(&head, ch.next, &ch) != ch.next);
39
40 if (a_cas(&target_tid, ch.tid, 0) == (ch.tid | 0x80000000))
41 __syscall(SYS_futex, &target_tid, FUTEX_UNLOCK_PI|FUTEX_PRIVATE);
42
43 sem_wait(&ch.target_sem);
44 callback(context);
45 sem_post(&ch.caller_sem);
46 sem_wait(&ch.target_sem);
47
48 errno = old_errno;
49}
50
51void __synccall(void (*func)(void *), void *ctx)
52{
53 sigset_t oldmask;
54 int cs, i, r, pid, self;;
55 DIR dir = {0};
56 struct dirent *de;
57 struct sigaction sa = { .sa_flags = SA_RESTART, .sa_handler = handler };
58 struct chain *cp, *next;
59 struct timespec ts;
60
61 /* Blocking signals in two steps, first only app-level signals
62 * before taking the lock, then all signals after taking the lock,
63 * is necessary to achieve AS-safety. Blocking them all first would
64 * deadlock if multiple threads called __synccall. Waiting to block
65 * any until after the lock would allow re-entry in the same thread
66 * with the lock already held. */
67 __block_app_sigs(&oldmask);
68 LOCK(synccall_lock);
69 __block_all_sigs(0);
70 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
71
72 head = 0;
73
74 if (!libc.threaded) goto single_threaded;
75
76 callback = func;
77 context = ctx;
78
79 /* This atomic store ensures that any signaled threads will see the
80 * above stores, and prevents more than a bounded number of threads,
81 * those already in pthread_create, from creating new threads until
82 * the value is cleared to zero again. */
83 a_store(&__block_new_threads, 1);
84
85 /* Block even implementation-internal signals, so that nothing
86 * interrupts the SIGSYNCCALL handlers. The main possible source
87 * of trouble is asynchronous cancellation. */
88 memset(&sa.sa_mask, -1, sizeof sa.sa_mask);
89 __libc_sigaction(SIGSYNCCALL, &sa, 0);
90
91 pid = __syscall(SYS_getpid);
92 self = __syscall(SYS_gettid);
93
94 /* Since opendir is not AS-safe, the DIR needs to be setup manually
95 * in automatic storage. Thankfully this is easy. */
96 dir.fd = open("/proc/self/task", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
97 if (dir.fd < 0) goto out;
98
99 /* Initially send one signal per counted thread. But since we can't
100 * synchronize with thread creation/exit here, there could be too
101 * few signals. This initial signaling is just an optimization, not
102 * part of the logic. */
103 for (i=libc.threads_minus_1; i; i--)
104 __syscall(SYS_kill, pid, SIGSYNCCALL);
105
106 /* Loop scanning the kernel-provided thread list until it shows no
107 * threads that have not already replied to the signal. */
108 for (;;) {
109 int miss_cnt = 0;
110 while ((de = readdir(&dir))) {
111 if (!isdigit(de->d_name[0])) continue;
112 int tid = atoi(de->d_name);
113 if (tid == self || !tid) continue;
114
115 /* Set the target thread as the PI futex owner before
116 * checking if it's in the list of caught threads. If it
117 * adds itself to the list after we check for it, then
118 * it will see its own tid in the PI futex and perform
119 * the unlock operation. */
120 a_store(&target_tid, tid);
121
122 /* Thread-already-caught is a success condition. */
123 for (cp = head; cp && cp->tid != tid; cp=cp->next);
124 if (cp) continue;
125
126 r = -__syscall(SYS_tgkill, pid, tid, SIGSYNCCALL);
127
128 /* Target thread exit is a success condition. */
129 if (r == ESRCH) continue;
130
131 /* The FUTEX_LOCK_PI operation is used to loan priority
132 * to the target thread, which otherwise may be unable
133 * to run. Timeout is necessary because there is a race
134 * condition where the tid may be reused by a different
135 * process. */
136 clock_gettime(CLOCK_REALTIME, &ts);
137 ts.tv_nsec += 10000000;
138 if (ts.tv_nsec >= 1000000000) {
139 ts.tv_sec++;
140 ts.tv_nsec -= 1000000000;
141 }
142 r = -__syscall(SYS_futex, &target_tid,
143 FUTEX_LOCK_PI|FUTEX_PRIVATE, 0, &ts);
144
145 /* Obtaining the lock means the thread responded. ESRCH
146 * means the target thread exited, which is okay too. */
147 if (!r || r == ESRCH) continue;
148
149 miss_cnt++;
150 }
151 if (!miss_cnt) break;
152 rewinddir(&dir);
153 }
154 close(dir.fd);
155
156 /* Serialize execution of callback in caught threads. */
157 for (cp=head; cp; cp=cp->next) {
158 sem_post(&cp->target_sem);
159 sem_wait(&cp->caller_sem);
160 }
161
162 sa.sa_handler = SIG_IGN;
163 __libc_sigaction(SIGSYNCCALL, &sa, 0);
164
165single_threaded:
166 func(ctx);
167
168 /* Only release the caught threads once all threads, including the
169 * caller, have returned from the callback function. */
170 for (cp=head; cp; cp=next) {
171 next = cp->next;
172 sem_post(&cp->target_sem);
173 }
174
175out:
176 a_store(&__block_new_threads, 0);
177 __wake(&__block_new_threads, -1, 1);
178
179 pthread_setcancelstate(cs, 0);
180 UNLOCK(synccall_lock);
181 __restore_sigs(&oldmask);
182}
Note: See TracBrowser for help on using the repository browser.