|
1 |
|
2 #include <stdlib.h> |
|
3 #include <stdio.h> |
|
4 #include <signal.h> |
|
5 #include <sys/types.h> |
|
6 #include <sys/wait.h> |
|
7 #include <sys/prctl.h> |
|
8 #include <ulocks.h> |
|
9 #include <errno.h> |
|
10 |
|
11 #define HDR_SIZE 2680 /* sizeof(ushdr_t) */ |
|
12 #define MAXPROC 100 /* max # of threads that can be started */ |
|
13 |
|
14 static usptr_t *shared_arena; |
|
15 static ulock_t count_lock; /* protection for some variables */ |
|
16 static ulock_t wait_lock; /* lock used to wait for other threads */ |
|
17 static int waiting_for_threads; /* protected by count_lock */ |
|
18 static int nthreads; /* protected by count_lock */ |
|
19 static int exit_status; |
|
20 #ifndef NO_EXIT_PROG |
|
21 static int do_exit; /* indicates that the program is to exit */ |
|
22 #endif |
|
23 static int exiting; /* we're already exiting (for maybe_exit) */ |
|
24 static pid_t my_pid; /* PID of main thread */ |
|
25 static struct pidlist { |
|
26 pid_t parent; |
|
27 pid_t child; |
|
28 } pidlist[MAXPROC]; /* PIDs of other threads; protected by count_lock */ |
|
29 static int maxpidindex; /* # of PIDs in pidlist */ |
|
30 |
|
31 #ifndef NO_EXIT_PROG |
|
32 /* |
|
33 * This routine is called as a signal handler when another thread |
|
34 * exits. When that happens, we must see whether we have to exit as |
|
35 * well (because of an PyThread_exit_prog()) or whether we should continue on. |
|
36 */ |
|
37 static void exit_sig(void) |
|
38 { |
|
39 d2printf(("exit_sig called\n")); |
|
40 if (exiting && getpid() == my_pid) { |
|
41 d2printf(("already exiting\n")); |
|
42 return; |
|
43 } |
|
44 if (do_exit) { |
|
45 d2printf(("exiting in exit_sig\n")); |
|
46 #ifdef Py_DEBUG |
|
47 if ((thread_debug & 8) == 0) |
|
48 thread_debug &= ~1; /* don't produce debug messages */ |
|
49 #endif |
|
50 PyThread_exit_thread(); |
|
51 } |
|
52 } |
|
53 |
|
54 /* |
|
55 * This routine is called when a process calls exit(). If that wasn't |
|
56 * done from the library, we do as if an PyThread_exit_prog() was intended. |
|
57 */ |
|
58 static void maybe_exit(void) |
|
59 { |
|
60 dprintf(("maybe_exit called\n")); |
|
61 if (exiting) { |
|
62 dprintf(("already exiting\n")); |
|
63 return; |
|
64 } |
|
65 PyThread_exit_prog(0); |
|
66 } |
|
67 #endif /* NO_EXIT_PROG */ |
|
68 |
|
69 /* |
|
70 * Initialization. |
|
71 */ |
|
72 static void PyThread__init_thread(void) |
|
73 { |
|
74 #ifndef NO_EXIT_PROG |
|
75 struct sigaction s; |
|
76 #endif /* NO_EXIT_PROG */ |
|
77 #ifdef USE_DL |
|
78 long addr, size; |
|
79 #endif /* USE_DL */ |
|
80 |
|
81 |
|
82 #ifdef USE_DL |
|
83 if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0) |
|
84 perror("usconfig - CONF_INITSIZE (check)"); |
|
85 if (usconfig(CONF_INITSIZE, size) < 0) |
|
86 perror("usconfig - CONF_INITSIZE (reset)"); |
|
87 addr = (long) dl_getrange(size + HDR_SIZE); |
|
88 dprintf(("trying to use addr %p-%p for shared arena\n", addr, addr+size)); |
|
89 errno = 0; |
|
90 if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 && errno != 0) |
|
91 perror("usconfig - CONF_ATTACHADDR (set)"); |
|
92 #endif /* USE_DL */ |
|
93 if (usconfig(CONF_INITUSERS, 16) < 0) |
|
94 perror("usconfig - CONF_INITUSERS"); |
|
95 my_pid = getpid(); /* so that we know which is the main thread */ |
|
96 #ifndef NO_EXIT_PROG |
|
97 atexit(maybe_exit); |
|
98 s.sa_handler = exit_sig; |
|
99 sigemptyset(&s.sa_mask); |
|
100 /*sigaddset(&s.sa_mask, SIGUSR1);*/ |
|
101 s.sa_flags = 0; |
|
102 sigaction(SIGUSR1, &s, 0); |
|
103 if (prctl(PR_SETEXITSIG, SIGUSR1) < 0) |
|
104 perror("prctl - PR_SETEXITSIG"); |
|
105 #endif /* NO_EXIT_PROG */ |
|
106 if (usconfig(CONF_ARENATYPE, US_SHAREDONLY) < 0) |
|
107 perror("usconfig - CONF_ARENATYPE"); |
|
108 usconfig(CONF_LOCKTYPE, US_DEBUG); /* XXX */ |
|
109 #ifdef Py_DEBUG |
|
110 if (thread_debug & 4) |
|
111 usconfig(CONF_LOCKTYPE, US_DEBUGPLUS); |
|
112 else if (thread_debug & 2) |
|
113 usconfig(CONF_LOCKTYPE, US_DEBUG); |
|
114 #endif /* Py_DEBUG */ |
|
115 if ((shared_arena = usinit(tmpnam(0))) == 0) |
|
116 perror("usinit"); |
|
117 #ifdef USE_DL |
|
118 if (usconfig(CONF_ATTACHADDR, addr) < 0) /* reset address */ |
|
119 perror("usconfig - CONF_ATTACHADDR (reset)"); |
|
120 #endif /* USE_DL */ |
|
121 if ((count_lock = usnewlock(shared_arena)) == NULL) |
|
122 perror("usnewlock (count_lock)"); |
|
123 (void) usinitlock(count_lock); |
|
124 if ((wait_lock = usnewlock(shared_arena)) == NULL) |
|
125 perror("usnewlock (wait_lock)"); |
|
126 dprintf(("arena start: %p, arena size: %ld\n", shared_arena, (long) usconfig(CONF_GETSIZE, shared_arena))); |
|
127 } |
|
128 |
|
129 /* |
|
130 * Thread support. |
|
131 */ |
|
132 |
|
133 static void clean_threads(void) |
|
134 { |
|
135 int i, j; |
|
136 pid_t mypid, pid; |
|
137 |
|
138 /* clean up any exited threads */ |
|
139 mypid = getpid(); |
|
140 i = 0; |
|
141 while (i < maxpidindex) { |
|
142 if (pidlist[i].parent == mypid && (pid = pidlist[i].child) > 0) { |
|
143 pid = waitpid(pid, 0, WNOHANG); |
|
144 if (pid > 0) { |
|
145 /* a thread has exited */ |
|
146 pidlist[i] = pidlist[--maxpidindex]; |
|
147 /* remove references to children of dead proc */ |
|
148 for (j = 0; j < maxpidindex; j++) |
|
149 if (pidlist[j].parent == pid) |
|
150 pidlist[j].child = -1; |
|
151 continue; /* don't increment i */ |
|
152 } |
|
153 } |
|
154 i++; |
|
155 } |
|
156 /* clean up the list */ |
|
157 i = 0; |
|
158 while (i < maxpidindex) { |
|
159 if (pidlist[i].child == -1) { |
|
160 pidlist[i] = pidlist[--maxpidindex]; |
|
161 continue; /* don't increment i */ |
|
162 } |
|
163 i++; |
|
164 } |
|
165 } |
|
166 |
|
167 long PyThread_start_new_thread(void (*func)(void *), void *arg) |
|
168 { |
|
169 #ifdef USE_DL |
|
170 long addr, size; |
|
171 static int local_initialized = 0; |
|
172 #endif /* USE_DL */ |
|
173 int success = 0; /* init not needed when SOLARIS_THREADS and */ |
|
174 /* C_THREADS implemented properly */ |
|
175 |
|
176 dprintf(("PyThread_start_new_thread called\n")); |
|
177 if (!initialized) |
|
178 PyThread_init_thread(); |
|
179 switch (ussetlock(count_lock)) { |
|
180 case 0: return 0; |
|
181 case -1: perror("ussetlock (count_lock)"); |
|
182 } |
|
183 if (maxpidindex >= MAXPROC) |
|
184 success = -1; |
|
185 else { |
|
186 #ifdef USE_DL |
|
187 if (!local_initialized) { |
|
188 if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0) |
|
189 perror("usconfig - CONF_INITSIZE (check)"); |
|
190 if (usconfig(CONF_INITSIZE, size) < 0) |
|
191 perror("usconfig - CONF_INITSIZE (reset)"); |
|
192 addr = (long) dl_getrange(size + HDR_SIZE); |
|
193 dprintf(("trying to use addr %p-%p for sproc\n", |
|
194 addr, addr+size)); |
|
195 errno = 0; |
|
196 if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 && |
|
197 errno != 0) |
|
198 perror("usconfig - CONF_ATTACHADDR (set)"); |
|
199 } |
|
200 #endif /* USE_DL */ |
|
201 clean_threads(); |
|
202 if ((success = sproc(func, PR_SALL, arg)) < 0) |
|
203 perror("sproc"); |
|
204 #ifdef USE_DL |
|
205 if (!local_initialized) { |
|
206 if (usconfig(CONF_ATTACHADDR, addr) < 0) |
|
207 /* reset address */ |
|
208 perror("usconfig - CONF_ATTACHADDR (reset)"); |
|
209 local_initialized = 1; |
|
210 } |
|
211 #endif /* USE_DL */ |
|
212 if (success >= 0) { |
|
213 nthreads++; |
|
214 pidlist[maxpidindex].parent = getpid(); |
|
215 pidlist[maxpidindex++].child = success; |
|
216 dprintf(("pidlist[%d] = %d\n", |
|
217 maxpidindex-1, success)); |
|
218 } |
|
219 } |
|
220 if (usunsetlock(count_lock) < 0) |
|
221 perror("usunsetlock (count_lock)"); |
|
222 return success; |
|
223 } |
|
224 |
|
225 long PyThread_get_thread_ident(void) |
|
226 { |
|
227 return getpid(); |
|
228 } |
|
229 |
|
230 static void do_PyThread_exit_thread(int no_cleanup) |
|
231 { |
|
232 dprintf(("PyThread_exit_thread called\n")); |
|
233 if (!initialized) |
|
234 if (no_cleanup) |
|
235 _exit(0); |
|
236 else |
|
237 exit(0); |
|
238 if (ussetlock(count_lock) < 0) |
|
239 perror("ussetlock (count_lock)"); |
|
240 nthreads--; |
|
241 if (getpid() == my_pid) { |
|
242 /* main thread; wait for other threads to exit */ |
|
243 exiting = 1; |
|
244 #ifndef NO_EXIT_PROG |
|
245 if (do_exit) { |
|
246 int i; |
|
247 |
|
248 /* notify other threads */ |
|
249 clean_threads(); |
|
250 if (nthreads >= 0) { |
|
251 dprintf(("kill other threads\n")); |
|
252 for (i = 0; i < maxpidindex; i++) |
|
253 if (pidlist[i].child > 0) |
|
254 (void) kill(pidlist[i].child, |
|
255 SIGKILL); |
|
256 _exit(exit_status); |
|
257 } |
|
258 } |
|
259 #endif /* NO_EXIT_PROG */ |
|
260 waiting_for_threads = 1; |
|
261 if (ussetlock(wait_lock) < 0) |
|
262 perror("ussetlock (wait_lock)"); |
|
263 for (;;) { |
|
264 if (nthreads < 0) { |
|
265 dprintf(("really exit (%d)\n", exit_status)); |
|
266 if (no_cleanup) |
|
267 _exit(exit_status); |
|
268 else |
|
269 exit(exit_status); |
|
270 } |
|
271 if (usunsetlock(count_lock) < 0) |
|
272 perror("usunsetlock (count_lock)"); |
|
273 dprintf(("waiting for other threads (%d)\n", nthreads)); |
|
274 if (ussetlock(wait_lock) < 0) |
|
275 perror("ussetlock (wait_lock)"); |
|
276 if (ussetlock(count_lock) < 0) |
|
277 perror("ussetlock (count_lock)"); |
|
278 } |
|
279 } |
|
280 /* not the main thread */ |
|
281 if (waiting_for_threads) { |
|
282 dprintf(("main thread is waiting\n")); |
|
283 if (usunsetlock(wait_lock) < 0) |
|
284 perror("usunsetlock (wait_lock)"); |
|
285 } |
|
286 #ifndef NO_EXIT_PROG |
|
287 else if (do_exit) |
|
288 (void) kill(my_pid, SIGUSR1); |
|
289 #endif /* NO_EXIT_PROG */ |
|
290 if (usunsetlock(count_lock) < 0) |
|
291 perror("usunsetlock (count_lock)"); |
|
292 _exit(0); |
|
293 } |
|
294 |
|
295 void PyThread_exit_thread(void) |
|
296 { |
|
297 do_PyThread_exit_thread(0); |
|
298 } |
|
299 |
|
300 void PyThread__exit_thread(void) |
|
301 { |
|
302 do_PyThread_exit_thread(1); |
|
303 } |
|
304 |
|
305 #ifndef NO_EXIT_PROG |
|
306 static void do_PyThread_exit_prog(int status, int no_cleanup) |
|
307 { |
|
308 dprintf(("PyThread_exit_prog(%d) called\n", status)); |
|
309 if (!initialized) |
|
310 if (no_cleanup) |
|
311 _exit(status); |
|
312 else |
|
313 exit(status); |
|
314 do_exit = 1; |
|
315 exit_status = status; |
|
316 do_PyThread_exit_thread(no_cleanup); |
|
317 } |
|
318 |
|
319 void PyThread_exit_prog(int status) |
|
320 { |
|
321 do_PyThread_exit_prog(status, 0); |
|
322 } |
|
323 |
|
324 void PyThread__exit_prog(int status) |
|
325 { |
|
326 do_PyThread_exit_prog(status, 1); |
|
327 } |
|
328 #endif /* NO_EXIT_PROG */ |
|
329 |
|
330 /* |
|
331 * Lock support. |
|
332 */ |
|
333 PyThread_type_lock PyThread_allocate_lock(void) |
|
334 { |
|
335 ulock_t lock; |
|
336 |
|
337 dprintf(("PyThread_allocate_lock called\n")); |
|
338 if (!initialized) |
|
339 PyThread_init_thread(); |
|
340 |
|
341 if ((lock = usnewlock(shared_arena)) == NULL) |
|
342 perror("usnewlock"); |
|
343 (void) usinitlock(lock); |
|
344 dprintf(("PyThread_allocate_lock() -> %p\n", lock)); |
|
345 return (PyThread_type_lock) lock; |
|
346 } |
|
347 |
|
348 void PyThread_free_lock(PyThread_type_lock lock) |
|
349 { |
|
350 dprintf(("PyThread_free_lock(%p) called\n", lock)); |
|
351 usfreelock((ulock_t) lock, shared_arena); |
|
352 } |
|
353 |
|
354 int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag) |
|
355 { |
|
356 int success; |
|
357 |
|
358 dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag)); |
|
359 errno = 0; /* clear it just in case */ |
|
360 if (waitflag) |
|
361 success = ussetlock((ulock_t) lock); |
|
362 else |
|
363 success = uscsetlock((ulock_t) lock, 1); /* Try it once */ |
|
364 if (success < 0) |
|
365 perror(waitflag ? "ussetlock" : "uscsetlock"); |
|
366 dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success)); |
|
367 return success; |
|
368 } |
|
369 |
|
370 void PyThread_release_lock(PyThread_type_lock lock) |
|
371 { |
|
372 dprintf(("PyThread_release_lock(%p) called\n", lock)); |
|
373 if (usunsetlock((ulock_t) lock) < 0) |
|
374 perror("usunsetlock"); |
|
375 } |