blob: 6f6334e5349211950f2b407ab542517750135487 [file] [log] [blame]
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +00001/***********************************************************
2Copyright 1991, 1992, 1993, 1994 by Stichting Mathematisch Centrum,
3Amsterdam, The Netherlands.
4
5 All Rights Reserved
6
7Permission to use, copy, modify, and distribute this software and its
8documentation for any purpose and without fee is hereby granted,
9provided that the above copyright notice appear in all copies and that
10both that copyright notice and this permission notice appear in
11supporting documentation, and that the names of Stichting Mathematisch
12Centrum or CWI not be used in advertising or publicity pertaining to
13distribution of the software without specific, written prior permission.
14
15STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
16THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
18FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
23******************************************************************/
24
25#ifdef WITH_SGI_DL
26#define USE_DL
27#endif
28
29#include <stdlib.h>
30#include <stdio.h>
31#include <signal.h>
32#include <sys/types.h>
33#include <sys/wait.h>
34#include <sys/prctl.h>
35#include <ulocks.h>
36#include <errno.h>
37
38#define HDR_SIZE 2680 /* sizeof(ushdr_t) */
39#define MAXPROC 100 /* max # of threads that can be started */
40
41static usptr_t *shared_arena;
42static ulock_t count_lock; /* protection for some variables */
43static ulock_t wait_lock; /* lock used to wait for other threads */
44static int waiting_for_threads; /* protected by count_lock */
45static int nthreads; /* protected by count_lock */
46static int exit_status;
47#ifndef NO_EXIT_PROG
48static int do_exit; /* indicates that the program is to exit */
49#endif
50static int exiting; /* we're already exiting (for maybe_exit) */
51static pid_t my_pid; /* PID of main thread */
52static pid_t pidlist[MAXPROC]; /* PIDs of other threads */
53static int maxpidindex; /* # of PIDs in pidlist */
54
55#ifndef NO_EXIT_PROG
56/*
57 * This routine is called as a signal handler when another thread
58 * exits. When that happens, we must see whether we have to exit as
59 * well (because of an exit_prog()) or whether we should continue on.
60 */
61static void exit_sig _P0()
62{
63 d2printf(("exit_sig called\n"));
64 if (exiting && getpid() == my_pid) {
65 d2printf(("already exiting\n"));
66 return;
67 }
68 if (do_exit) {
69 d2printf(("exiting in exit_sig\n"));
70#ifdef DEBUG
71 if ((thread_debug & 8) == 0)
72 thread_debug &= ~1; /* don't produce debug messages */
73#endif
74 exit_thread();
75 }
76}
77
78/*
79 * This routine is called when a process calls exit(). If that wasn't
80 * done from the library, we do as if an exit_prog() was intended.
81 */
82static void maybe_exit _P0()
83{
84 dprintf(("maybe_exit called\n"));
85 if (exiting) {
86 dprintf(("already exiting\n"));
87 return;
88 }
89 exit_prog(0);
90}
91#endif /* NO_EXIT_PROG */
92
93/*
94 * Initialization.
95 */
96static void _init_thread _P0()
97{
98#ifndef NO_EXIT_PROG
99 struct sigaction s;
100#endif /* NO_EXIT_PROG */
101#ifdef USE_DL
102 long addr, size;
103#endif /* USE_DL */
104
105
106#ifdef USE_DL
107 if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0)
108 perror("usconfig - CONF_INITSIZE (check)");
109 if (usconfig(CONF_INITSIZE, size) < 0)
110 perror("usconfig - CONF_INITSIZE (reset)");
111 addr = (long) dl_getrange(size + HDR_SIZE);
112 dprintf(("trying to use addr %lx-%lx for shared arena\n", addr, addr+size));
113 errno = 0;
114 if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 && errno != 0)
115 perror("usconfig - CONF_ATTACHADDR (set)");
116#endif /* USE_DL */
117 if (usconfig(CONF_INITUSERS, 16) < 0)
118 perror("usconfig - CONF_INITUSERS");
119 my_pid = getpid(); /* so that we know which is the main thread */
120#ifndef NO_EXIT_PROG
121 atexit(maybe_exit);
122 s.sa_handler = exit_sig;
123 sigemptyset(&s.sa_mask);
124 /*sigaddset(&s.sa_mask, SIGUSR1);*/
125 s.sa_flags = 0;
126 sigaction(SIGUSR1, &s, 0);
127 if (prctl(PR_SETEXITSIG, SIGUSR1) < 0)
128 perror("prctl - PR_SETEXITSIG");
129#endif /* NO_EXIT_PROG */
130 if (usconfig(CONF_ARENATYPE, US_SHAREDONLY) < 0)
131 perror("usconfig - CONF_ARENATYPE");
132 usconfig(CONF_LOCKTYPE, US_DEBUG); /* XXX */
133#ifdef DEBUG
134 if (thread_debug & 4)
135 usconfig(CONF_LOCKTYPE, US_DEBUGPLUS);
136 else if (thread_debug & 2)
137 usconfig(CONF_LOCKTYPE, US_DEBUG);
138#endif /* DEBUG */
139 if ((shared_arena = usinit(tmpnam(0))) == 0)
140 perror("usinit");
141#ifdef USE_DL
142 if (usconfig(CONF_ATTACHADDR, addr) < 0) /* reset address */
143 perror("usconfig - CONF_ATTACHADDR (reset)");
144#endif /* USE_DL */
145 if ((count_lock = usnewlock(shared_arena)) == NULL)
146 perror("usnewlock (count_lock)");
147 (void) usinitlock(count_lock);
148 if ((wait_lock = usnewlock(shared_arena)) == NULL)
149 perror("usnewlock (wait_lock)");
150 dprintf(("arena start: %lx, arena size: %ld\n", (long) shared_arena, (long) usconfig(CONF_GETSIZE, shared_arena)));
151}
152
153/*
154 * Thread support.
155 */
156
157static void clean_threads _P0()
158{
159 int i;
160 pid_t pid;
161
162 /* clean up any exited threads */
163 i = 0;
164 while (i < maxpidindex) {
165 if ((pid = pidlist[i]) > 0) {
166 pid = waitpid(pid, 0, WNOHANG);
167 if (pid < 0)
168 return;
169 if (pid != 0) {
170 /* a thread has exited */
171 pidlist[i] = pidlist[--maxpidindex];
172 continue; /* don't increment i */
173 }
174 }
175 i++;
176 }
177}
178
179int start_new_thread _P2(func, void (*func) _P((void *)), arg, void *arg)
180{
181#ifdef USE_DL
182 long addr, size;
183 static int local_initialized = 0;
184#endif /* USE_DL */
185 int success = 0; /* init not needed when SOLARIS_THREADS and */
186 /* C_THREADS implemented properly */
187
188 dprintf(("start_new_thread called\n"));
189 if (!initialized)
190 init_thread();
191 switch (ussetlock(count_lock)) {
192 case 0: return 0;
193 case -1: perror("ussetlock (count_lock)");
194 }
195 if (maxpidindex >= MAXPROC)
196 success = -1;
197 else {
198#ifdef USE_DL
199 if (!local_initialized) {
200 if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0)
201 perror("usconfig - CONF_INITSIZE (check)");
202 if (usconfig(CONF_INITSIZE, size) < 0)
203 perror("usconfig - CONF_INITSIZE (reset)");
204 addr = (long) dl_getrange(size + HDR_SIZE);
205 dprintf(("trying to use addr %lx-%lx for sproc\n", addr, addr+size));
206 errno = 0;
207 if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 && errno != 0)
208 perror("usconfig - CONF_ATTACHADDR (set)");
209 }
210#endif /* USE_DL */
211 clean_threads();
212 if ((success = sproc(func, PR_SALL, arg)) < 0)
213 perror("sproc");
214#ifdef USE_DL
215 if (!local_initialized) {
216 if (usconfig(CONF_ATTACHADDR, addr) < 0) /* reset address */
217 perror("usconfig - CONF_ATTACHADDR (reset)");
218 local_initialized = 1;
219 }
220#endif /* USE_DL */
221 if (success >= 0) {
222 nthreads++;
223 pidlist[maxpidindex++] = success;
224 dprintf(("pidlist[%d] = %d\n", maxpidindex-1, success));
225 }
226 }
227 if (usunsetlock(count_lock) < 0)
228 perror("usunsetlock (count_lock)");
229 return success < 0 ? 0 : 1;
230}
231
232static void do_exit_thread _P1(no_cleanup, int no_cleanup)
233{
234 dprintf(("exit_thread called\n"));
235 if (!initialized)
236 if (no_cleanup)
237 _exit(0);
238 else
239 exit(0);
240 if (ussetlock(count_lock) < 0)
241 perror("ussetlock (count_lock)");
242 nthreads--;
243 if (getpid() == my_pid) {
244 /* main thread; wait for other threads to exit */
245 exiting = 1;
246#ifndef NO_EXIT_PROG
247 if (do_exit) {
248 int i;
249
250 /* notify other threads */
251 clean_threads();
252 if (nthreads >= 0) {
253 dprintf(("kill other threads\n"));
254 for (i = 0; i < maxpidindex; i++)
255 if (pidlist[i] > 0)
256 (void) kill(pidlist[i],
257 SIGKILL);
258 _exit(exit_status);
259 }
260 }
261#endif /* NO_EXIT_PROG */
262 waiting_for_threads = 1;
263 if (ussetlock(wait_lock) < 0)
264 perror("ussetlock (wait_lock)");
265 for (;;) {
266 if (nthreads < 0) {
267 dprintf(("really exit (%d)\n", exit_status));
268 if (no_cleanup)
269 _exit(exit_status);
270 else
271 exit(exit_status);
272 }
273 if (usunsetlock(count_lock) < 0)
274 perror("usunsetlock (count_lock)");
275 dprintf(("waiting for other threads (%d)\n", nthreads));
276 if (ussetlock(wait_lock) < 0)
277 perror("ussetlock (wait_lock)");
278 if (ussetlock(count_lock) < 0)
279 perror("ussetlock (count_lock)");
280 }
281 }
282 /* not the main thread */
283 if (waiting_for_threads) {
284 dprintf(("main thread is waiting\n"));
285 if (usunsetlock(wait_lock) < 0)
286 perror("usunsetlock (wait_lock)");
287 }
288#ifndef NO_EXIT_PROG
289 else if (do_exit)
290 (void) kill(my_pid, SIGUSR1);
291#endif /* NO_EXIT_PROG */
292 if (usunsetlock(count_lock) < 0)
293 perror("usunsetlock (count_lock)");
294 _exit(0);
295}
296
297void exit_thread _P0()
298{
299 do_exit_thread(0);
300}
301
302void _exit_thread _P0()
303{
304 do_exit_thread(1);
305}
306
307#ifndef NO_EXIT_PROG
308static void do_exit_prog _P2(status, int status, no_cleanup, int no_cleanup)
309{
310 dprintf(("exit_prog(%d) called\n", status));
311 if (!initialized)
312 if (no_cleanup)
313 _exit(status);
314 else
315 exit(status);
316 do_exit = 1;
317 exit_status = status;
318 do_exit_thread(no_cleanup);
319}
320
321void exit_prog _P1(status, int status)
322{
323 do_exit_prog(status, 0);
324}
325
326void _exit_prog _P1(status, int status)
327{
328 do_exit_prog(status, 1);
329}
330#endif /* NO_EXIT_PROG */
331
332/*
333 * Lock support.
334 */
335type_lock allocate_lock _P0()
336{
337 ulock_t lock;
338
339 dprintf(("allocate_lock called\n"));
340 if (!initialized)
341 init_thread();
342
343 if ((lock = usnewlock(shared_arena)) == NULL)
344 perror("usnewlock");
345 (void) usinitlock(lock);
346 dprintf(("allocate_lock() -> %lx\n", (long)lock));
347 return (type_lock) lock;
348}
349
350void free_lock _P1(lock, type_lock lock)
351{
352 dprintf(("free_lock(%lx) called\n", (long)lock));
353 usfreelock((ulock_t) lock, shared_arena);
354}
355
356int acquire_lock _P2(lock, type_lock lock, waitflag, int waitflag)
357{
358 int success;
359
360 dprintf(("acquire_lock(%lx, %d) called\n", (long)lock, waitflag));
361 errno = 0; /* clear it just in case */
362 if (waitflag)
363 success = ussetlock((ulock_t) lock);
364 else
365 success = uscsetlock((ulock_t) lock, 1); /* Try it once */
366 if (success < 0)
367 perror(waitflag ? "ussetlock" : "uscsetlock");
368 dprintf(("acquire_lock(%lx, %d) -> %d\n", (long)lock, waitflag, success));
369 return success;
370}
371
372void release_lock _P1(lock, type_lock lock)
373{
374 dprintf(("release_lock(%lx) called\n", (long)lock));
375 if (usunsetlock((ulock_t) lock) < 0)
376 perror("usunsetlock");
377}
378
379/*
380 * Semaphore support.
381 */
382type_sema allocate_sema _P1(value, int value)
383{
384 usema_t *sema;
385 dprintf(("allocate_sema called\n"));
386 if (!initialized)
387 init_thread();
388
389 if ((sema = usnewsema(shared_arena, value)) == NULL)
390 perror("usnewsema");
391 dprintf(("allocate_sema() -> %lx\n", (long) sema));
392 return (type_sema) sema;
393}
394
395void free_sema _P1(sema, type_sema sema)
396{
397 dprintf(("free_sema(%lx) called\n", (long) sema));
398 usfreesema((usema_t *) sema, shared_arena);
399}
400
401void down_sema _P1(sema, type_sema sema)
402{
403 dprintf(("down_sema(%lx) called\n", (long) sema));
404 if (uspsema((usema_t *) sema) < 0)
405 perror("uspsema");
406 dprintf(("down_sema(%lx) return\n", (long) sema));
407}
408
409void up_sema _P1(sema, type_sema sema)
410{
411 dprintf(("up_sema(%lx)\n", (long) sema));
412 if (usvsema((usema_t *) sema) < 0)
413 perror("usvsema");
414}