thread_sgi.h 11.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

#ifdef WITH_SGI_DL
#define USE_DL
#endif

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <ulocks.h>
#include <errno.h>

#define HDR_SIZE	2680	/* sizeof(ushdr_t) */
#define MAXPROC		100	/* max # of threads that can be started */

static usptr_t *shared_arena;
static ulock_t count_lock;	/* protection for some variables */
static ulock_t wait_lock;	/* lock used to wait for other threads */
static int waiting_for_threads;	/* protected by count_lock */
static int nthreads;		/* protected by count_lock */
static int exit_status;
#ifndef NO_EXIT_PROG
static int do_exit;		/* indicates that the program is to exit */
#endif
static int exiting;		/* we're already exiting (for maybe_exit) */
static pid_t my_pid;		/* PID of main thread */
29 30 31 32
static struct pidlist {
	pid_t parent;
	pid_t child;
} pidlist[MAXPROC];	/* PIDs of other threads; protected by count_lock */
33 34 35 36 37 38
static int maxpidindex;		/* # of PIDs in pidlist */

#ifndef NO_EXIT_PROG
/*
 * This routine is called as a signal handler when another thread
 * exits.  When that happens, we must see whether we have to exit as
39
 * well (because of an PyThread_exit_prog()) or whether we should continue on.
40
 */
41
static void exit_sig(void)
42 43 44 45 46 47 48 49
{
	d2printf(("exit_sig called\n"));
	if (exiting && getpid() == my_pid) {
		d2printf(("already exiting\n"));
		return;
	}
	if (do_exit) {
		d2printf(("exiting in exit_sig\n"));
50
#ifdef Py_DEBUG
51 52 53
		if ((thread_debug & 8) == 0)
			thread_debug &= ~1; /* don't produce debug messages */
#endif
54
		PyThread_exit_thread();
55 56 57 58 59
	}
}

/*
 * This routine is called when a process calls exit().  If that wasn't
60
 * done from the library, we do as if an PyThread_exit_prog() was intended.
61
 */
62
static void maybe_exit(void)
63 64 65 66 67 68
{
	dprintf(("maybe_exit called\n"));
	if (exiting) {
		dprintf(("already exiting\n"));
		return;
	}
69
	PyThread_exit_prog(0);
70 71 72 73 74 75
}
#endif /* NO_EXIT_PROG */

/*
 * Initialization.
 */
76
static void PyThread__init_thread(void)
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
{
#ifndef NO_EXIT_PROG
	struct sigaction s;
#endif /* NO_EXIT_PROG */
#ifdef USE_DL
	long addr, size;
#endif /* USE_DL */


#ifdef USE_DL
	if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0)
		perror("usconfig - CONF_INITSIZE (check)");
	if (usconfig(CONF_INITSIZE, size) < 0)
		perror("usconfig - CONF_INITSIZE (reset)");
	addr = (long) dl_getrange(size + HDR_SIZE);
92
	dprintf(("trying to use addr %p-%p for shared arena\n", addr, addr+size));
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
	errno = 0;
	if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 && errno != 0)
		perror("usconfig - CONF_ATTACHADDR (set)");
#endif /* USE_DL */
	if (usconfig(CONF_INITUSERS, 16) < 0)
		perror("usconfig - CONF_INITUSERS");
	my_pid = getpid();	/* so that we know which is the main thread */
#ifndef NO_EXIT_PROG
	atexit(maybe_exit);
	s.sa_handler = exit_sig;
	sigemptyset(&s.sa_mask);
	/*sigaddset(&s.sa_mask, SIGUSR1);*/
	s.sa_flags = 0;
	sigaction(SIGUSR1, &s, 0);
	if (prctl(PR_SETEXITSIG, SIGUSR1) < 0)
		perror("prctl - PR_SETEXITSIG");
#endif /* NO_EXIT_PROG */
	if (usconfig(CONF_ARENATYPE, US_SHAREDONLY) < 0)
		perror("usconfig - CONF_ARENATYPE");
	usconfig(CONF_LOCKTYPE, US_DEBUG); /* XXX */
113
#ifdef Py_DEBUG
114 115 116 117
	if (thread_debug & 4)
		usconfig(CONF_LOCKTYPE, US_DEBUGPLUS);
	else if (thread_debug & 2)
		usconfig(CONF_LOCKTYPE, US_DEBUG);
118
#endif /* Py_DEBUG */
119 120 121 122 123 124 125 126 127 128 129
	if ((shared_arena = usinit(tmpnam(0))) == 0)
		perror("usinit");
#ifdef USE_DL
	if (usconfig(CONF_ATTACHADDR, addr) < 0) /* reset address */
		perror("usconfig - CONF_ATTACHADDR (reset)");
#endif /* USE_DL */
	if ((count_lock = usnewlock(shared_arena)) == NULL)
		perror("usnewlock (count_lock)");
	(void) usinitlock(count_lock);
	if ((wait_lock = usnewlock(shared_arena)) == NULL)
		perror("usnewlock (wait_lock)");
130
	dprintf(("arena start: %p, arena size: %ld\n",  shared_arena, (long) usconfig(CONF_GETSIZE, shared_arena)));
131 132 133 134 135 136
}

/*
 * Thread support.
 */

137
static void clean_threads(void)
138
{
139 140
	int i, j;
	pid_t mypid, pid;
141 142

	/* clean up any exited threads */
143
	mypid = getpid();
144 145
	i = 0;
	while (i < maxpidindex) {
146
		if (pidlist[i].parent == mypid && (pid = pidlist[i].child) > 0) {
147
			pid = waitpid(pid, 0, WNOHANG);
148
			if (pid > 0) {
149 150
				/* a thread has exited */
				pidlist[i] = pidlist[--maxpidindex];
151 152 153 154
				/* remove references to children of dead proc */
				for (j = 0; j < maxpidindex; j++)
					if (pidlist[j].parent == pid)
						pidlist[j].child = -1;
155 156 157 158 159
				continue; /* don't increment i */
			}
		}
		i++;
	}
160 161 162 163 164 165 166 167 168
	/* clean up the list */
	i = 0;
	while (i < maxpidindex) {
		if (pidlist[i].child == -1) {
			pidlist[i] = pidlist[--maxpidindex];
			continue; /* don't increment i */
		}
		i++;
	}
169 170
}

171
int PyThread_start_new_thread(void (*func)(void *), void *arg)
172 173 174 175 176 177 178 179
{
#ifdef USE_DL
	long addr, size;
	static int local_initialized = 0;
#endif /* USE_DL */
	int success = 0;	/* init not needed when SOLARIS_THREADS and */
				/* C_THREADS implemented properly */

180
	dprintf(("PyThread_start_new_thread called\n"));
181
	if (!initialized)
182
		PyThread_init_thread();
183 184 185 186 187 188 189 190 191 192 193 194 195 196
	switch (ussetlock(count_lock)) {
	case 0: return 0;
	case -1: perror("ussetlock (count_lock)");
	}
	if (maxpidindex >= MAXPROC)
		success = -1;
	else {
#ifdef USE_DL
		if (!local_initialized) {
			if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0)
				perror("usconfig - CONF_INITSIZE (check)");
			if (usconfig(CONF_INITSIZE, size) < 0)
				perror("usconfig - CONF_INITSIZE (reset)");
			addr = (long) dl_getrange(size + HDR_SIZE);
197
			dprintf(("trying to use addr %p-%p for sproc\n",
198
				 addr, addr+size));
199
			errno = 0;
200 201
			if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 &&
			    errno != 0)
202 203 204 205 206 207 208 209
				perror("usconfig - CONF_ATTACHADDR (set)");
		}
#endif /* USE_DL */
		clean_threads();
		if ((success = sproc(func, PR_SALL, arg)) < 0)
			perror("sproc");
#ifdef USE_DL
		if (!local_initialized) {
210 211
			if (usconfig(CONF_ATTACHADDR, addr) < 0)
				/* reset address */
212 213 214 215 216 217
				perror("usconfig - CONF_ATTACHADDR (reset)");
			local_initialized = 1;
		}
#endif /* USE_DL */
		if (success >= 0) {
			nthreads++;
218 219 220 221
			pidlist[maxpidindex].parent = getpid();
			pidlist[maxpidindex++].child = success;
			dprintf(("pidlist[%d] = %d\n",
				 maxpidindex-1, success));
222 223 224 225 226 227 228
		}
	}
	if (usunsetlock(count_lock) < 0)
		perror("usunsetlock (count_lock)");
	return success < 0 ? 0 : 1;
}

229
long PyThread_get_thread_ident(void)
230 231 232 233
{
	return getpid();
}

234
static void do_PyThread_exit_thread(int no_cleanup)
235
{
236
	dprintf(("PyThread_exit_thread called\n"));
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
	if (!initialized)
		if (no_cleanup)
			_exit(0);
		else
			exit(0);
	if (ussetlock(count_lock) < 0)
		perror("ussetlock (count_lock)");
	nthreads--;
	if (getpid() == my_pid) {
		/* main thread; wait for other threads to exit */
		exiting = 1;
#ifndef NO_EXIT_PROG
		if (do_exit) {
			int i;

			/* notify other threads */
			clean_threads();
			if (nthreads >= 0) {
				dprintf(("kill other threads\n"));
				for (i = 0; i < maxpidindex; i++)
257 258
					if (pidlist[i].child > 0)
						(void) kill(pidlist[i].child,
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
							    SIGKILL);
				_exit(exit_status);
			}
		}
#endif /* NO_EXIT_PROG */
		waiting_for_threads = 1;
		if (ussetlock(wait_lock) < 0)
			perror("ussetlock (wait_lock)");
		for (;;) {
			if (nthreads < 0) {
				dprintf(("really exit (%d)\n", exit_status));
				if (no_cleanup)
					_exit(exit_status);
				else
					exit(exit_status);
			}
			if (usunsetlock(count_lock) < 0)
				perror("usunsetlock (count_lock)");
			dprintf(("waiting for other threads (%d)\n", nthreads));
			if (ussetlock(wait_lock) < 0)
				perror("ussetlock (wait_lock)");
			if (ussetlock(count_lock) < 0)
				perror("ussetlock (count_lock)");
		}
	}
	/* not the main thread */
	if (waiting_for_threads) {
		dprintf(("main thread is waiting\n"));
		if (usunsetlock(wait_lock) < 0)
			perror("usunsetlock (wait_lock)");
	}
#ifndef NO_EXIT_PROG
	else if (do_exit)
		(void) kill(my_pid, SIGUSR1);
#endif /* NO_EXIT_PROG */
	if (usunsetlock(count_lock) < 0)
		perror("usunsetlock (count_lock)");
	_exit(0);
}

299
void PyThread_exit_thread(void)
300
{
301
	do_PyThread_exit_thread(0);
302 303
}

304
void PyThread__exit_thread(void)
305
{
306
	do_PyThread_exit_thread(1);
307 308 309
}

#ifndef NO_EXIT_PROG
310
static void do_PyThread_exit_prog(int status, int no_cleanup)
311
{
312
	dprintf(("PyThread_exit_prog(%d) called\n", status));
313 314 315 316 317 318 319
	if (!initialized)
		if (no_cleanup)
			_exit(status);
		else
			exit(status);
	do_exit = 1;
	exit_status = status;
320
	do_PyThread_exit_thread(no_cleanup);
321 322
}

323
void PyThread_exit_prog(int status)
324
{
325
	do_PyThread_exit_prog(status, 0);
326 327
}

328
void PyThread__exit_prog(int status)
329
{
330
	do_PyThread_exit_prog(status, 1);
331 332 333 334 335 336
}
#endif /* NO_EXIT_PROG */

/*
 * Lock support.
 */
337
PyThread_type_lock PyThread_allocate_lock(void)
338 339 340
{
	ulock_t lock;

341
	dprintf(("PyThread_allocate_lock called\n"));
342
	if (!initialized)
343
		PyThread_init_thread();
344 345 346 347

	if ((lock = usnewlock(shared_arena)) == NULL)
		perror("usnewlock");
	(void) usinitlock(lock);
348
	dprintf(("PyThread_allocate_lock() -> %p\n", lock));
349
	return (PyThread_type_lock) lock;
350 351
}

352
void PyThread_free_lock(PyThread_type_lock lock)
353
{
354
	dprintf(("PyThread_free_lock(%p) called\n", lock));
355 356 357
	usfreelock((ulock_t) lock, shared_arena);
}

358
int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
359 360 361
{
	int success;

362
	dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag));
363 364 365 366 367 368 369
	errno = 0;		/* clear it just in case */
	if (waitflag)
		success = ussetlock((ulock_t) lock);
	else
		success = uscsetlock((ulock_t) lock, 1); /* Try it once */
	if (success < 0)
		perror(waitflag ? "ussetlock" : "uscsetlock");
370
	dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success));
371 372 373
	return success;
}

374
void PyThread_release_lock(PyThread_type_lock lock)
375
{
376
	dprintf(("PyThread_release_lock(%p) called\n", lock));
377 378 379 380 381 382 383
	if (usunsetlock((ulock_t) lock) < 0)
		perror("usunsetlock");
}

/*
 * Semaphore support.
 */
384
PyThread_type_sema PyThread_allocate_sema(int value)
385 386
{
	usema_t *sema;
387
	dprintf(("PyThread_allocate_sema called\n"));
388
	if (!initialized)
389
		PyThread_init_thread();
390 391 392

	if ((sema = usnewsema(shared_arena, value)) == NULL)
		perror("usnewsema");
393
	dprintf(("PyThread_allocate_sema() -> %p\n",  sema));
394
	return (PyThread_type_sema) sema;
395 396
}

397
void PyThread_free_sema(PyThread_type_sema sema)
398
{
399
	dprintf(("PyThread_free_sema(%p) called\n",  sema));
400 401 402
	usfreesema((usema_t *) sema, shared_arena);
}

403
int PyThread_down_sema(PyThread_type_sema sema, int waitflag)
404
{
405 406
	int success;

407
	dprintf(("PyThread_down_sema(%p) called\n",  sema));
408 409 410 411 412 413
	if (waitflag)
		success = uspsema((usema_t *) sema);
	else
		success = uscpsema((usema_t *) sema);
	if (success < 0)
		perror(waitflag ? "uspsema" : "uscpsema");
414
	dprintf(("PyThread_down_sema(%p) return\n",  sema));
415
	return success;
416 417
}

418
void PyThread_up_sema(PyThread_type_sema sema)
419
{
420
	dprintf(("PyThread_up_sema(%p)\n",  sema));
421 422 423
	if (usvsema((usema_t *) sema) < 0)
		perror("usvsema");
}
424 425 426 427 428 429 430 431 432 433 434 435 436 437

/*
 * Per-thread data ("key") support.
 */

struct key {
	struct key *next;
	long id;
	int key;
	void *value;
};

static struct key *keyhead = NULL;
static int nkeys = 0;
438
static PyThread_type_lock keymutex = NULL;
439

440
static struct key *find_key(int key, void *value)
441 442
{
	struct key *p;
443
	long id = PyThread_get_thread_ident();
444 445 446 447 448 449 450 451 452 453 454
	for (p = keyhead; p != NULL; p = p->next) {
		if (p->id == id && p->key == key)
			return p;
	}
	if (value == NULL)
		return NULL;
	p = (struct key *)malloc(sizeof(struct key));
	if (p != NULL) {
		p->id = id;
		p->key = key;
		p->value = value;
455
		PyThread_acquire_lock(keymutex, 1);
456 457
		p->next = keyhead;
		keyhead = p;
458
		PyThread_release_lock(keymutex);
459 460 461 462
	}
	return p;
}

463
int PyThread_create_key(void)
464 465
{
	if (keymutex == NULL)
466
		keymutex = PyThread_allocate_lock();
467 468 469
	return ++nkeys;
}

470
void PyThread_delete_key(int key)
471 472
{
	struct key *p, **q;
473
	PyThread_acquire_lock(keymutex, 1);
474 475 476 477 478 479 480 481 482 483
	q = &keyhead;
	while ((p = *q) != NULL) {
		if (p->key == key) {
			*q = p->next;
			free((void *)p);
			/* NB This does *not* free p->value! */
		}
		else
			q = &p->next;
	}
484
	PyThread_release_lock(keymutex);
485 486
}

487
int PyThread_set_key_value(int key, void *value)
488 489 490 491 492 493 494 495
{
	struct key *p = find_key(key, value);
	if (p == NULL)
		return -1;
	else
		return 0;
}

496
void *PyThread_get_key_value(int key)
497 498 499 500 501 502 503
{
	struct key *p = find_key(key, NULL);
	if (p == NULL)
		return NULL;
	else
		return p->value;
}