Annotation of OpenXM_contrib2/asir2000/gc/aix_irix_threads.c, Revision 1.1
1.1 ! noro 1: /*
! 2: * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
! 3: * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
! 4: * Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved.
! 5: *
! 6: * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
! 7: * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
! 8: *
! 9: * Permission is hereby granted to use or copy this program
! 10: * for any purpose, provided the above notices are retained on all copies.
! 11: * Permission to modify the code and to distribute modified code is granted,
! 12: * provided the above notices are retained, and a notice that the code was
! 13: * modified is included with the above copyright notice.
! 14: */
! 15: /*
! 16: * Support code for Irix (>=6.2) Pthreads and for AIX pthreads.
! 17: * This relies on properties
! 18: * not guaranteed by the Pthread standard. It may or may not be portable
! 19: * to other implementations.
! 20: *
! 21: * Note that there is a lot of code duplication between this file and
! 22: * (pthread_support.c, pthread_stop_world.c). They should be merged.
! 23: * Pthread_support.c should be directly usable.
! 24: *
! 25: * Please avoid adding new ports here; use the generic pthread support
! 26: * as a base instead.
! 27: */
! 28:
! 29: # if defined(GC_IRIX_THREADS) || defined(GC_AIX_THREADS)
! 30:
! 31: # include "private/gc_priv.h"
! 32: # include <pthread.h>
! 33: # include <assert.h>
! 34: # include <semaphore.h>
! 35: # include <time.h>
! 36: # include <errno.h>
! 37: # include <unistd.h>
! 38: # include <sys/mman.h>
! 39: # include <sys/time.h>
! 40:
! 41: #undef pthread_create
! 42: #undef pthread_sigmask
! 43: #undef pthread_join
! 44:
! 45: #if defined(GC_IRIX_THREADS) && !defined(MUTEX_RECURSIVE_NP)
! 46: #define MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
! 47: #endif
! 48:
! 49: void GC_thr_init();
! 50:
! 51: #if 0
! 52: void GC_print_sig_mask()
! 53: {
! 54: sigset_t blocked;
! 55: int i;
! 56:
! 57: if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
! 58: ABORT("pthread_sigmask");
! 59: GC_printf0("Blocked: ");
! 60: for (i = 1; i <= MAXSIG; i++) {
! 61: if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
! 62: }
! 63: GC_printf0("\n");
! 64: }
! 65: #endif
! 66:
! 67: /* We use the allocation lock to protect thread-related data structures. */
! 68:
! 69: /* The set of all known threads. We intercept thread creation and */
! 70: /* joins. We never actually create detached threads. We allocate all */
! 71: /* new thread stacks ourselves. These allow us to maintain this */
! 72: /* data structure. */
! 73: /* Protected by GC_thr_lock. */
! 74: /* Some of this should be declared volatile, but that's incosnsistent */
! 75: /* with some library routine declarations. */
! 76: typedef struct GC_Thread_Rep {
! 77: struct GC_Thread_Rep * next; /* More recently allocated threads */
! 78: /* with a given pthread id come */
! 79: /* first. (All but the first are */
! 80: /* guaranteed to be dead, but we may */
! 81: /* not yet have registered the join.) */
! 82: pthread_t id;
! 83: word stop;
! 84: # define NOT_STOPPED 0
! 85: # define PLEASE_STOP 1
! 86: # define STOPPED 2
! 87: word flags;
! 88: # define FINISHED 1 /* Thread has exited. */
! 89: # define DETACHED 2 /* Thread is intended to be detached. */
! 90: # define CLIENT_OWNS_STACK 4
! 91: /* Stack was supplied by client. */
! 92: ptr_t stack;
! 93: ptr_t stack_ptr; /* Valid only when stopped. */
! 94: /* But must be within stack region at */
! 95: /* all times. */
! 96: size_t stack_size; /* 0 for original thread. */
! 97: void * status; /* Used only to avoid premature */
! 98: /* reclamation of any data it might */
! 99: /* reference. */
! 100: } * GC_thread;
! 101:
! 102: GC_thread GC_lookup_thread(pthread_t id);
! 103:
! 104: /*
! 105: * The only way to suspend threads given the pthread interface is to send
! 106: * signals. Unfortunately, this means we have to reserve
! 107: * a signal, and intercept client calls to change the signal mask.
! 108: */
! 109: #if 0 /* DOB: 6.1 */
! 110: # if defined(GC_AIX_THREADS)
! 111: # define SIG_SUSPEND SIGUSR1
! 112: # else
! 113: # define SIG_SUSPEND (SIGRTMIN + 6)
! 114: # endif
! 115: #endif
! 116:
! 117: pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
! 118: /* Number of threads stopped so far */
! 119: pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER;
! 120: pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER;
! 121:
! 122: void GC_suspend_handler(int sig)
! 123: {
! 124: int dummy;
! 125: GC_thread me;
! 126: sigset_t all_sigs;
! 127: sigset_t old_sigs;
! 128: int i;
! 129:
! 130: if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
! 131: me = GC_lookup_thread(pthread_self());
! 132: /* The lookup here is safe, since I'm doing this on behalf */
! 133: /* of a thread which holds the allocation lock in order */
! 134: /* to stop the world. Thus concurrent modification of the */
! 135: /* data structure is impossible. */
! 136: if (PLEASE_STOP != me -> stop) {
! 137: /* Misdirected signal. */
! 138: pthread_mutex_unlock(&GC_suspend_lock);
! 139: return;
! 140: }
! 141: pthread_mutex_lock(&GC_suspend_lock);
! 142: me -> stack_ptr = (ptr_t)(&dummy);
! 143: me -> stop = STOPPED;
! 144: pthread_cond_signal(&GC_suspend_ack_cv);
! 145: pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock);
! 146: pthread_mutex_unlock(&GC_suspend_lock);
! 147: /* GC_printf1("Continuing 0x%x\n", pthread_self()); */
! 148: }
! 149:
! 150:
! 151: GC_bool GC_thr_initialized = FALSE;
! 152:
! 153: size_t GC_min_stack_sz;
! 154:
! 155: size_t GC_page_sz;
! 156:
! 157: # define N_FREE_LISTS 25
! 158: ptr_t GC_stack_free_lists[N_FREE_LISTS] = { 0 };
! 159: /* GC_stack_free_lists[i] is free list for stacks of */
! 160: /* size GC_min_stack_sz*2**i. */
! 161: /* Free lists are linked through first word. */
! 162:
! 163: /* Return a stack of size at least *stack_size. *stack_size is */
! 164: /* replaced by the actual stack size. */
! 165: /* Caller holds allocation lock. */
! 166: ptr_t GC_stack_alloc(size_t * stack_size)
! 167: {
! 168: register size_t requested_sz = *stack_size;
! 169: register size_t search_sz = GC_min_stack_sz;
! 170: register int index = 0; /* = log2(search_sz/GC_min_stack_sz) */
! 171: register ptr_t result;
! 172:
! 173: while (search_sz < requested_sz) {
! 174: search_sz *= 2;
! 175: index++;
! 176: }
! 177: if ((result = GC_stack_free_lists[index]) == 0
! 178: && (result = GC_stack_free_lists[index+1]) != 0) {
! 179: /* Try next size up. */
! 180: search_sz *= 2; index++;
! 181: }
! 182: if (result != 0) {
! 183: GC_stack_free_lists[index] = *(ptr_t *)result;
! 184: } else {
! 185: result = (ptr_t) GC_scratch_alloc(search_sz + 2*GC_page_sz);
! 186: result = (ptr_t)(((word)result + GC_page_sz) & ~(GC_page_sz - 1));
! 187: /* Protect hottest page to detect overflow. */
! 188: # ifdef STACK_GROWS_UP
! 189: /* mprotect(result + search_sz, GC_page_sz, PROT_NONE); */
! 190: # else
! 191: /* mprotect(result, GC_page_sz, PROT_NONE); */
! 192: result += GC_page_sz;
! 193: # endif
! 194: }
! 195: *stack_size = search_sz;
! 196: return(result);
! 197: }
! 198:
! 199: /* Caller holds allocation lock. */
! 200: void GC_stack_free(ptr_t stack, size_t size)
! 201: {
! 202: register int index = 0;
! 203: register size_t search_sz = GC_min_stack_sz;
! 204:
! 205: while (search_sz < size) {
! 206: search_sz *= 2;
! 207: index++;
! 208: }
! 209: if (search_sz != size) ABORT("Bad stack size");
! 210: *(ptr_t *)stack = GC_stack_free_lists[index];
! 211: GC_stack_free_lists[index] = stack;
! 212: }
! 213:
! 214:
! 215:
! 216: # define THREAD_TABLE_SZ 128 /* Must be power of 2 */
! 217: volatile GC_thread GC_threads[THREAD_TABLE_SZ];
! 218:
! 219: void GC_push_thread_structures GC_PROTO((void))
! 220: {
! 221: GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
! 222: }
! 223:
! 224: /* Add a thread to GC_threads. We assume it wasn't already there. */
! 225: /* Caller holds allocation lock. */
! 226: GC_thread GC_new_thread(pthread_t id)
! 227: {
! 228: int hv = ((word)id) % THREAD_TABLE_SZ;
! 229: GC_thread result;
! 230: static struct GC_Thread_Rep first_thread;
! 231: static GC_bool first_thread_used = FALSE;
! 232:
! 233: if (!first_thread_used) {
! 234: result = &first_thread;
! 235: first_thread_used = TRUE;
! 236: /* Dont acquire allocation lock, since we may already hold it. */
! 237: } else {
! 238: result = (struct GC_Thread_Rep *)
! 239: GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
! 240: }
! 241: if (result == 0) return(0);
! 242: result -> id = id;
! 243: result -> next = GC_threads[hv];
! 244: GC_threads[hv] = result;
! 245: /* result -> flags = 0; */
! 246: /* result -> stop = 0; */
! 247: return(result);
! 248: }
! 249:
! 250: /* Delete a thread from GC_threads. We assume it is there. */
! 251: /* (The code intentionally traps if it wasn't.) */
! 252: /* Caller holds allocation lock. */
! 253: void GC_delete_thread(pthread_t id)
! 254: {
! 255: int hv = ((word)id) % THREAD_TABLE_SZ;
! 256: register GC_thread p = GC_threads[hv];
! 257: register GC_thread prev = 0;
! 258:
! 259: while (!pthread_equal(p -> id, id)) {
! 260: prev = p;
! 261: p = p -> next;
! 262: }
! 263: if (prev == 0) {
! 264: GC_threads[hv] = p -> next;
! 265: } else {
! 266: prev -> next = p -> next;
! 267: }
! 268: }
! 269:
! 270: /* If a thread has been joined, but we have not yet */
! 271: /* been notified, then there may be more than one thread */
! 272: /* in the table with the same pthread id. */
! 273: /* This is OK, but we need a way to delete a specific one. */
! 274: void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
! 275: {
! 276: int hv = ((word)id) % THREAD_TABLE_SZ;
! 277: register GC_thread p = GC_threads[hv];
! 278: register GC_thread prev = 0;
! 279:
! 280: while (p != gc_id) {
! 281: prev = p;
! 282: p = p -> next;
! 283: }
! 284: if (prev == 0) {
! 285: GC_threads[hv] = p -> next;
! 286: } else {
! 287: prev -> next = p -> next;
! 288: }
! 289: }
! 290:
! 291: /* Return a GC_thread corresponding to a given thread_t. */
! 292: /* Returns 0 if it's not there. */
! 293: /* Caller holds allocation lock or otherwise inhibits */
! 294: /* updates. */
! 295: /* If there is more than one thread with the given id we */
! 296: /* return the most recent one. */
! 297: GC_thread GC_lookup_thread(pthread_t id)
! 298: {
! 299: int hv = ((word)id) % THREAD_TABLE_SZ;
! 300: register GC_thread p = GC_threads[hv];
! 301:
! 302: while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
! 303: return(p);
! 304: }
! 305:
! 306: #if defined(GC_AIX_THREADS)
! 307: void GC_stop_world()
! 308: {
! 309: pthread_t my_thread = pthread_self();
! 310: register int i;
! 311: register GC_thread p;
! 312: register int result;
! 313: struct timespec timeout;
! 314:
! 315: for (i = 0; i < THREAD_TABLE_SZ; i++) {
! 316: for (p = GC_threads[i]; p != 0; p = p -> next) {
! 317: if (p -> id != my_thread) {
! 318: pthread_suspend_np(p->id);
! 319: }
! 320: }
! 321: }
! 322: /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
! 323: }
! 324:
! 325: void GC_start_world()
! 326: {
! 327: GC_thread p;
! 328: unsigned i;
! 329: pthread_t my_thread = pthread_self();
! 330:
! 331: /* GC_printf0("World starting\n"); */
! 332: for (i = 0; i < THREAD_TABLE_SZ; i++) {
! 333: for (p = GC_threads[i]; p != 0; p = p -> next) {
! 334: if (p -> id != my_thread) {
! 335: pthread_continue_np(p->id);
! 336: }
! 337: }
! 338: }
! 339: }
! 340:
! 341: #else /* GC_AIX_THREADS */
! 342:
! 343: /* Caller holds allocation lock. */
! 344: void GC_stop_world()
! 345: {
! 346: pthread_t my_thread = pthread_self();
! 347: register int i;
! 348: register GC_thread p;
! 349: register int result;
! 350: struct timespec timeout;
! 351:
! 352: for (i = 0; i < THREAD_TABLE_SZ; i++) {
! 353: for (p = GC_threads[i]; p != 0; p = p -> next) {
! 354: if (p -> id != my_thread) {
! 355: if (p -> flags & FINISHED) {
! 356: p -> stop = STOPPED;
! 357: continue;
! 358: }
! 359: p -> stop = PLEASE_STOP;
! 360: result = pthread_kill(p -> id, SIG_SUSPEND);
! 361: /* GC_printf1("Sent signal to 0x%x\n", p -> id); */
! 362: switch(result) {
! 363: case ESRCH:
! 364: /* Not really there anymore. Possible? */
! 365: p -> stop = STOPPED;
! 366: break;
! 367: case 0:
! 368: break;
! 369: default:
! 370: ABORT("pthread_kill failed");
! 371: }
! 372: }
! 373: }
! 374: }
! 375: pthread_mutex_lock(&GC_suspend_lock);
! 376: for (i = 0; i < THREAD_TABLE_SZ; i++) {
! 377: for (p = GC_threads[i]; p != 0; p = p -> next) {
! 378: while (p -> id != my_thread && p -> stop != STOPPED) {
! 379: clock_gettime(CLOCK_REALTIME, &timeout);
! 380: timeout.tv_nsec += 50000000; /* 50 msecs */
! 381: if (timeout.tv_nsec >= 1000000000) {
! 382: timeout.tv_nsec -= 1000000000;
! 383: ++timeout.tv_sec;
! 384: }
! 385: result = pthread_cond_timedwait(&GC_suspend_ack_cv,
! 386: &GC_suspend_lock,
! 387: &timeout);
! 388: if (result == ETIMEDOUT) {
! 389: /* Signal was lost or misdirected. Try again. */
! 390: /* Duplicate signals should be benign. */
! 391: result = pthread_kill(p -> id, SIG_SUSPEND);
! 392: }
! 393: }
! 394: }
! 395: }
! 396: pthread_mutex_unlock(&GC_suspend_lock);
! 397: /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
! 398: }
! 399:
! 400: /* Caller holds allocation lock. */
! 401: void GC_start_world()
! 402: {
! 403: GC_thread p;
! 404: unsigned i;
! 405:
! 406: /* GC_printf0("World starting\n"); */
! 407: for (i = 0; i < THREAD_TABLE_SZ; i++) {
! 408: for (p = GC_threads[i]; p != 0; p = p -> next) {
! 409: p -> stop = NOT_STOPPED;
! 410: }
! 411: }
! 412: pthread_mutex_lock(&GC_suspend_lock);
! 413: /* All other threads are at pthread_cond_wait in signal handler. */
! 414: /* Otherwise we couldn't have acquired the lock. */
! 415: pthread_mutex_unlock(&GC_suspend_lock);
! 416: pthread_cond_broadcast(&GC_continue_cv);
! 417: }
! 418:
! 419: #endif /* GC_AIX_THREADS */
! 420:
! 421: # ifdef MMAP_STACKS
! 422: --> not really supported yet.
! 423: int GC_is_thread_stack(ptr_t addr)
! 424: {
! 425: register int i;
! 426: register GC_thread p;
! 427:
! 428: for (i = 0; i < THREAD_TABLE_SZ; i++) {
! 429: for (p = GC_threads[i]; p != 0; p = p -> next) {
! 430: if (p -> stack_size != 0) {
! 431: if (p -> stack <= addr &&
! 432: addr < p -> stack + p -> stack_size)
! 433: return 1;
! 434: }
! 435: }
! 436: }
! 437: return 0;
! 438: }
! 439: # endif
! 440:
! 441: /* We hold allocation lock. Should do exactly the right thing if the */
! 442: /* world is stopped. Should not fail if it isn't. */
! 443: void GC_push_all_stacks()
! 444: {
! 445: register int i;
! 446: register GC_thread p;
! 447: register ptr_t sp = GC_approx_sp();
! 448: register ptr_t hot, cold;
! 449: pthread_t me = pthread_self();
! 450:
! 451: if (!GC_thr_initialized) GC_thr_init();
! 452: /* GC_printf1("Pushing stacks from thread 0x%x\n", me); */
! 453: for (i = 0; i < THREAD_TABLE_SZ; i++) {
! 454: for (p = GC_threads[i]; p != 0; p = p -> next) {
! 455: if (p -> flags & FINISHED) continue;
! 456: if (pthread_equal(p -> id, me)) {
! 457: hot = GC_approx_sp();
! 458: } else {
! 459: #ifdef GC_AIX_THREADS
! 460: /* AIX doesn't use signals to suspend, so we need to get an accurate hot stack pointer */
! 461: pthread_t id = p -> id;
! 462: struct __pthrdsinfo pinfo;
! 463: int val = 255;
! 464: char regbuf[255];
! 465: int retval = pthread_getthrds_np(&id, PTHRDSINFO_QUERY_ALL, &pinfo, sizeof(pinfo), regbuf, &val);
! 466: if (retval != 0) { printf("ERROR: pthread_getthrds_np() failed in GC\n"); abort(); }
! 467: hot = (ptr_t)(unsigned long)pinfo.__pi_ustk;
! 468: if ((p -> stack_size != 0 &&
! 469: (pinfo.__pi_stackend != ((ptr_t)p -> stack) + p -> stack_size ||
! 470: p -> stack_ptr < p -> stack ||
! 471: p -> stack_ptr > ((ptr_t)p -> stack) + p -> stack_size))) {
! 472: printf("ERROR in GC_push_all_stacks() stack state:\n"
! 473: "p->stack: 0x%08x\n"
! 474: "p->stack_size: 0x%08x\n"
! 475: "p->stack_ptr: 0x%08x\n"
! 476: "(p->stack+p->stack_size): 0x%08x\n"
! 477: "pinfo.__pi_stackaddr: 0x%08x\n"
! 478: "pinfo.__pi_stacksize: 0x%08x\n"
! 479: "pinfo.__pi_stackend: 0x%08x\n"
! 480: "GC_stackbottom: 0x%08x\n"
! 481: ,
! 482: (uintptr_t)p->stack, (uintptr_t)p->stack_size,
! 483: (uintptr_t)p->stack_ptr, (uintptr_t)(((ptr_t)(p->stack))+p->stack_size),
! 484: (uintptr_t)pinfo.__pi_stackaddr, (uintptr_t)pinfo.__pi_stacksize, (uintptr_t)pinfo.__pi_stackend,
! 485: (uintptr_t)GC_stackbottom
! 486: );
! 487: }
! 488: /* push the registers too, because they won't be on stack */
! 489: GC_push_all_eager((ptr_t)&pinfo.__pi_context, (ptr_t)((&pinfo.__pi_context)+1));
! 490: GC_push_all_eager((ptr_t)regbuf, (ptr_t)®buf[val]);
! 491: #else
! 492: hot = p -> stack_ptr;
! 493: #endif
! 494: }
! 495: if (p -> stack_size != 0) {
! 496: # ifdef STACK_GROWS_UP
! 497: cold = p -> stack;
! 498: # else
! 499: cold = p -> stack + p -> stack_size;
! 500: # endif
! 501: } else {
! 502: /* The original stack. */
! 503: cold = GC_stackbottom;
! 504: }
! 505: # ifdef STACK_GROWS_UP
! 506: GC_push_all_stack(cold, hot);
! 507: # else
! 508: /* printf("thread 0x%x: hot=0x%08x cold=0x%08x\n", p -> id, hot, cold); */
! 509: GC_push_all_stack(hot, cold);
! 510: # endif
! 511: }
! 512: }
! 513: }
! 514:
! 515:
! 516: /* We hold the allocation lock. */
! 517: void GC_thr_init()
! 518: {
! 519: GC_thread t;
! 520: struct sigaction act;
! 521:
! 522: if (GC_thr_initialized) return;
! 523: GC_thr_initialized = TRUE;
! 524: GC_min_stack_sz = HBLKSIZE;
! 525: GC_page_sz = sysconf(_SC_PAGESIZE);
! 526: #ifndef GC_AIX_THREADS
! 527: (void) sigaction(SIG_SUSPEND, 0, &act);
! 528: if (act.sa_handler != SIG_DFL)
! 529: ABORT("Previously installed SIG_SUSPEND handler");
! 530: /* Install handler. */
! 531: act.sa_handler = GC_suspend_handler;
! 532: act.sa_flags = SA_RESTART;
! 533: (void) sigemptyset(&act.sa_mask);
! 534: if (0 != sigaction(SIG_SUSPEND, &act, 0))
! 535: ABORT("Failed to install SIG_SUSPEND handler");
! 536: #endif
! 537: /* Add the initial thread, so we can stop it. */
! 538: t = GC_new_thread(pthread_self());
! 539: t -> stack_size = 0;
! 540: t -> stack_ptr = (ptr_t)(&t);
! 541: t -> flags = DETACHED;
! 542: }
! 543:
! 544: int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
! 545: {
! 546: sigset_t fudged_set;
! 547:
! 548: #ifdef GC_AIX_THREADS
! 549: return(pthread_sigmask(how, set, oset));
! 550: #endif
! 551:
! 552: if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
! 553: fudged_set = *set;
! 554: sigdelset(&fudged_set, SIG_SUSPEND);
! 555: set = &fudged_set;
! 556: }
! 557: return(pthread_sigmask(how, set, oset));
! 558: }
! 559:
! 560: struct start_info {
! 561: void *(*start_routine)(void *);
! 562: void *arg;
! 563: word flags;
! 564: ptr_t stack;
! 565: size_t stack_size;
! 566: sem_t registered;
! 567: };
! 568:
! 569: void GC_thread_exit_proc(void *arg)
! 570: {
! 571: GC_thread me;
! 572:
! 573: LOCK();
! 574: me = GC_lookup_thread(pthread_self());
! 575: if (me -> flags & DETACHED) {
! 576: GC_delete_thread(pthread_self());
! 577: } else {
! 578: me -> flags |= FINISHED;
! 579: }
! 580: UNLOCK();
! 581: }
! 582:
! 583: int GC_pthread_join(pthread_t thread, void **retval)
! 584: {
! 585: int result;
! 586: GC_thread thread_gc_id;
! 587:
! 588: LOCK();
! 589: thread_gc_id = GC_lookup_thread(thread);
! 590: /* This is guaranteed to be the intended one, since the thread id */
! 591: /* cant have been recycled by pthreads. */
! 592: UNLOCK();
! 593: result = pthread_join(thread, retval);
! 594: /* Some versions of the Irix pthreads library can erroneously */
! 595: /* return EINTR when the call succeeds. */
! 596: if (EINTR == result) result = 0;
! 597: LOCK();
! 598: /* Here the pthread thread id may have been recycled. */
! 599: GC_delete_gc_thread(thread, thread_gc_id);
! 600: UNLOCK();
! 601: return result;
! 602: }
! 603:
! 604: void * GC_start_routine(void * arg)
! 605: {
! 606: struct start_info * si = arg;
! 607: void * result;
! 608: GC_thread me;
! 609: pthread_t my_pthread;
! 610: void *(*start)(void *);
! 611: void *start_arg;
! 612:
! 613: my_pthread = pthread_self();
! 614: /* If a GC occurs before the thread is registered, that GC will */
! 615: /* ignore this thread. That's fine, since it will block trying to */
! 616: /* acquire the allocation lock, and won't yet hold interesting */
! 617: /* pointers. */
! 618: LOCK();
! 619: /* We register the thread here instead of in the parent, so that */
! 620: /* we don't need to hold the allocation lock during pthread_create. */
! 621: /* Holding the allocation lock there would make REDIRECT_MALLOC */
! 622: /* impossible. It probably still doesn't work, but we're a little */
! 623: /* closer ... */
! 624: /* This unfortunately means that we have to be careful the parent */
! 625: /* doesn't try to do a pthread_join before we're registered. */
! 626: me = GC_new_thread(my_pthread);
! 627: me -> flags = si -> flags;
! 628: me -> stack = si -> stack;
! 629: me -> stack_size = si -> stack_size;
! 630: #ifdef STACK_GROWS_UP
! 631: me -> stack_ptr = (ptr_t)si -> stack + si -> stack_size - sizeof(word);
! 632: #else
! 633: /* stack_ptr needs to point to the hot part of the stack (or conservatively, past it) */
! 634: me -> stack_ptr = (ptr_t)si -> stack;
! 635: #endif
! 636: UNLOCK();
! 637: start = si -> start_routine;
! 638: start_arg = si -> arg;
! 639: sem_post(&(si -> registered));
! 640: pthread_cleanup_push(GC_thread_exit_proc, 0);
! 641: result = (*start)(start_arg);
! 642: me -> status = result;
! 643: me -> flags |= FINISHED;
! 644: pthread_cleanup_pop(1);
! 645: /* This involves acquiring the lock, ensuring that we can't exit */
! 646: /* while a collection that thinks we're alive is trying to stop */
! 647: /* us. */
! 648: return(result);
! 649: }
! 650:
! 651: # if defined(GC_AIX_THREADS)
! 652: /* pthread_attr_t is not a structure, thus a simple structure copy */
! 653: /* won't work. */
! 654: static void copy_attr(pthread_attr_t * pa_ptr,
! 655: const pthread_attr_t * source) {
! 656: int tmp;
! 657: size_t stmp;
! 658: void * vtmp;
! 659: struct sched_param sp_tmp;
! 660: #ifndef GC_AIX_THREADS
! 661: pthread_spu_t ps_tmp;
! 662: #endif
! 663: (void) pthread_attr_init(pa_ptr);
! 664: (void) pthread_attr_getdetachstate(source, &tmp);
! 665: (void) pthread_attr_setdetachstate(pa_ptr, tmp);
! 666: (void) pthread_attr_getinheritsched(source, &tmp);
! 667: (void) pthread_attr_setinheritsched(pa_ptr, tmp);
! 668: (void) pthread_attr_getschedpolicy(source, &tmp);
! 669: (void) pthread_attr_setschedpolicy(pa_ptr, tmp);
! 670: (void) pthread_attr_getstacksize(source, &stmp);
! 671: (void) pthread_attr_setstacksize(pa_ptr, stmp);
! 672: (void) pthread_attr_getguardsize(source, &stmp);
! 673: (void) pthread_attr_setguardsize(pa_ptr, stmp);
! 674: (void) pthread_attr_getstackaddr(source, &vtmp);
! 675: (void) pthread_attr_setstackaddr(pa_ptr, vtmp);
! 676: (void) pthread_attr_getscope(source, &tmp);
! 677: (void) pthread_attr_setscope(pa_ptr, tmp);
! 678: (void) pthread_attr_getschedparam(source, &sp_tmp);
! 679: (void) pthread_attr_setschedparam(pa_ptr, &sp_tmp);
! 680: #ifndef GC_AIX_THREADS
! 681: (void) pthread_attr_getprocessor_np(source, &ps_tmp, &tmp);
! 682: (void) pthread_attr_setprocessor_np(pa_ptr, ps_tmp, tmp);
! 683: #endif
! 684: }
! 685: # else
! 686: # define copy_attr(pa_ptr, source) *(pa_ptr) = *(source)
! 687: # endif
! 688:
! 689: int
! 690: GC_pthread_create(pthread_t *new_thread,
! 691: const pthread_attr_t *attr,
! 692: void *(*start_routine)(void *), void *arg)
! 693: {
! 694: int result;
! 695: GC_thread t;
! 696: void * stack;
! 697: size_t stacksize;
! 698: pthread_attr_t new_attr;
! 699: int detachstate;
! 700: word my_flags = 0;
! 701: struct start_info * si = GC_malloc(sizeof(struct start_info));
! 702:
! 703: /* This is otherwise saved only in an area mmapped by the thread */
! 704: /* library, which isn't visible to the collector. */
! 705:
! 706: if (0 == si) return(ENOMEM);
! 707: if (0 != sem_init(&(si -> registered), 0, 0)) {
! 708: ABORT("sem_init failed");
! 709: }
! 710: si -> start_routine = start_routine;
! 711: si -> arg = arg;
! 712: LOCK();
! 713: if (!GC_thr_initialized) GC_thr_init();
! 714:
! 715: if (NULL == attr) {
! 716: stack = 0;
! 717: (void) pthread_attr_init(&new_attr);
! 718: } else {
! 719: copy_attr(&new_attr, attr);
! 720: pthread_attr_getstackaddr(&new_attr, &stack);
! 721: }
! 722: pthread_attr_getstacksize(&new_attr, &stacksize);
! 723: pthread_attr_getdetachstate(&new_attr, &detachstate);
! 724: #ifdef GC_AIX_THREADS
! 725: GC_min_stack_sz = 5*1048576;
! 726: if (stacksize < GC_min_stack_sz) {
! 727: stacksize = GC_min_stack_sz;
! 728: }
! 729: { int alignment = 16*1024; /* size must be multiple of 16KB greater than 56KB */
! 730: int minval = 56*1024;
! 731: if ((stacksize - minval) % alignment != 0) {
! 732: stacksize = minval + alignment * ((stacksize-minval)/alignment + 1);
! 733: }
! 734: }
! 735: #endif
! 736: if (0 == stack) {
! 737: stack = (void *)GC_stack_alloc(&stacksize);
! 738: if (0 == stack) {
! 739: UNLOCK();
! 740: return(ENOMEM);
! 741: }
! 742: pthread_attr_setstacksize(&new_attr, stacksize);
! 743: #ifdef GC_AIX_THREADS
! 744: pthread_attr_setstackaddr(&new_attr, ((char *)stack)+stacksize);
! 745: #else
! 746: pthread_attr_setstackaddr(&new_attr, stack);
! 747: #endif
! 748: } else {
! 749: my_flags |= CLIENT_OWNS_STACK;
! 750: }
! 751:
! 752: if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
! 753: si -> flags = my_flags;
! 754: si -> stack = stack;
! 755: si -> stack_size = stacksize;
! 756: result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
! 757:
! 758: if (0 == new_thread && !(my_flags & CLIENT_OWNS_STACK)) {
! 759: GC_stack_free(stack, stacksize);
! 760: }
! 761: UNLOCK();
! 762: /* Wait until child has been added to the thread table. */
! 763: /* This also ensures that we hold onto si until the child is done */
! 764: /* with it. Thus it doesn't matter whether it is otherwise */
! 765: /* visible to the collector. */
! 766: while (0 != sem_wait(&(si -> registered))) {
! 767: if (errno != EINTR) {
! 768: GC_printf1("Sem_wait: errno = %ld\n", (unsigned long) errno);
! 769: ABORT("sem_wait failed");
! 770: }
! 771: }
! 772: sem_destroy(&(si -> registered));
! 773: pthread_attr_destroy(&new_attr);
! 774:
! 775: return(result);
! 776: }
! 777:
! 778: /* For now we use the pthreads locking primitives on HP/UX */
! 779:
! 780: VOLATILE GC_bool GC_collecting = 0; /* A hint that we're in the collector and */
! 781: /* holding the allocation lock for an */
! 782: /* extended period. */
! 783:
! 784: /* Reasonably fast spin locks. Basically the same implementation */
! 785: /* as STL alloc.h. */
! 786:
! 787: #define SLEEP_THRESHOLD 3
! 788:
! 789: volatile unsigned int GC_allocate_lock = 0;
! 790: #define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock)
! 791: #define GC_LOCK_TAKEN GC_allocate_lock
! 792:
! 793: void GC_lock()
! 794: {
! 795: # define low_spin_max 30 /* spin cycles if we suspect uniprocessor */
! 796: # define high_spin_max 1000 /* spin cycles for multiprocessor */
! 797: static unsigned spin_max = low_spin_max;
! 798: unsigned my_spin_max;
! 799: static unsigned last_spins = 0;
! 800: unsigned my_last_spins;
! 801: volatile unsigned junk;
! 802: # define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
! 803: int i;
! 804:
! 805: if (GC_TRY_LOCK()) {
! 806: return;
! 807: }
! 808: junk = 0;
! 809: my_spin_max = spin_max;
! 810: my_last_spins = last_spins;
! 811: for (i = 0; i < my_spin_max; i++) {
! 812: if (GC_collecting) goto yield;
! 813: if (i < my_last_spins/2 || GC_LOCK_TAKEN) {
! 814: PAUSE;
! 815: continue;
! 816: }
! 817: if (GC_TRY_LOCK()) {
! 818: /*
! 819: * got it!
! 820: * Spinning worked. Thus we're probably not being scheduled
! 821: * against the other process with which we were contending.
! 822: * Thus it makes sense to spin longer the next time.
! 823: */
! 824: last_spins = i;
! 825: spin_max = high_spin_max;
! 826: return;
! 827: }
! 828: }
! 829: /* We are probably being scheduled against the other process. Sleep. */
! 830: spin_max = low_spin_max;
! 831: yield:
! 832: for (i = 0;; ++i) {
! 833: if (GC_TRY_LOCK()) {
! 834: return;
! 835: }
! 836: if (i < SLEEP_THRESHOLD) {
! 837: sched_yield();
! 838: } else {
! 839: struct timespec ts;
! 840:
! 841: if (i > 26) i = 26;
! 842: /* Don't wait for more than about 60msecs, even */
! 843: /* under extreme contention. */
! 844: ts.tv_sec = 0;
! 845: ts.tv_nsec = 1 << i;
! 846: nanosleep(&ts, 0);
! 847: }
! 848: }
! 849: }
! 850:
! 851: # else /* !GC_IRIX_THREADS && !GC_AIX_THREADS */
! 852:
! 853: #ifndef LINT
! 854: int GC_no_Irix_threads;
! 855: #endif
! 856:
! 857: # endif /* IRIX_THREADS */
! 858:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>