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