Annotation of OpenXM_contrib2/asir2000/gc/pthread_support.c, Revision 1.1
1.1 ! noro 1: /*
! 2: * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
! 3: * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
! 4: * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
! 5: * Copyright (c) 2000-2001 by Hewlett-Packard Company. All rights reserved.
! 6: *
! 7: * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
! 8: * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
! 9: *
! 10: * Permission is hereby granted to use or copy this program
! 11: * for any purpose, provided the above notices are retained on all copies.
! 12: * Permission to modify the code and to distribute modified code is granted,
! 13: * provided the above notices are retained, and a notice that the code was
! 14: * modified is included with the above copyright notice.
! 15: */
! 16: /*
! 17: * Support code for LinuxThreads, the clone()-based kernel
! 18: * thread package for Linux which is included in libc6.
! 19: *
! 20: * This code relies on implementation details of LinuxThreads,
! 21: * (i.e. properties not guaranteed by the Pthread standard),
! 22: * though this version now does less of that than the other Pthreads
! 23: * support code.
! 24: *
! 25: * Note that there is a lot of code duplication between linux_threads.c
! 26: * and thread support for some of the other Posix platforms; any changes
! 27: * made here may need to be reflected there too.
! 28: */
! 29: /* DG/UX ix86 support <takis@xfree86.org> */
! 30: /*
! 31: * Linux_threads.c now also includes some code to support HPUX and
! 32: * OSF1 (Compaq Tru64 Unix, really). The OSF1 support is not yet
! 33: * functional. The OSF1 code is based on Eric Benson's
! 34: * patch, though that was originally against hpux_irix_threads. The code
! 35: * here is completely untested. With 0.0000001% probability, it might
! 36: * actually work.
! 37: *
! 38: * Eric also suggested an alternate basis for a lock implementation in
! 39: * his code:
! 40: * + #elif defined(OSF1)
! 41: * + unsigned long GC_allocate_lock = 0;
! 42: * + msemaphore GC_allocate_semaphore;
! 43: * + # define GC_TRY_LOCK() \
! 44: * + ((msem_lock(&GC_allocate_semaphore, MSEM_IF_NOWAIT) == 0) \
! 45: * + ? (GC_allocate_lock = 1) \
! 46: * + : 0)
! 47: * + # define GC_LOCK_TAKEN GC_allocate_lock
! 48: */
! 49:
! 50: /*#define DEBUG_THREADS 1*/
! 51: /*#define GC_ASSERTIONS*/
! 52:
! 53: # include "private/pthread_support.h"
! 54:
! 55: # if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
! 56: && !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS) \
! 57: && !defined(GC_AIX_THREADS)
! 58:
! 59: # if defined(GC_HPUX_THREADS) && !defined(USE_PTHREAD_SPECIFIC) \
! 60: && !defined(USE_HPUX_TLS)
! 61: # define USE_HPUX_TLS
! 62: # endif
! 63:
! 64: # if (defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) || \
! 65: defined(GC_DARWIN_THREADS)) && !defined(USE_PTHREAD_SPECIFIC)
! 66: # define USE_PTHREAD_SPECIFIC
! 67: # endif
! 68:
! 69: # if defined(GC_DGUX386_THREADS) && !defined(_POSIX4A_DRAFT10_SOURCE)
! 70: # define _POSIX4A_DRAFT10_SOURCE 1
! 71: # endif
! 72:
! 73: # if defined(GC_DGUX386_THREADS) && !defined(_USING_POSIX4A_DRAFT10)
! 74: # define _USING_POSIX4A_DRAFT10 1
! 75: # endif
! 76:
! 77: # ifdef THREAD_LOCAL_ALLOC
! 78: # if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_HPUX_TLS)
! 79: # include "private/specific.h"
! 80: # endif
! 81: # if defined(USE_PTHREAD_SPECIFIC)
! 82: # define GC_getspecific pthread_getspecific
! 83: # define GC_setspecific pthread_setspecific
! 84: # define GC_key_create pthread_key_create
! 85: typedef pthread_key_t GC_key_t;
! 86: # endif
! 87: # if defined(USE_HPUX_TLS)
! 88: # define GC_getspecific(x) (x)
! 89: # define GC_setspecific(key, v) ((key) = (v), 0)
! 90: # define GC_key_create(key, d) 0
! 91: typedef void * GC_key_t;
! 92: # endif
! 93: # endif
! 94: # include <stdlib.h>
! 95: # include <pthread.h>
! 96: # include <sched.h>
! 97: # include <time.h>
! 98: # include <errno.h>
! 99: # include <unistd.h>
! 100: # include <sys/mman.h>
! 101: # include <sys/time.h>
! 102: # include <sys/types.h>
! 103: # include <sys/stat.h>
! 104: # include <fcntl.h>
! 105:
! 106: #if defined(GC_DARWIN_THREADS)
! 107: # include "private/darwin_semaphore.h"
! 108: #else
! 109: # include <semaphore.h>
! 110: #endif /* !GC_DARWIN_THREADS */
! 111:
! 112: #if defined(GC_DARWIN_THREADS)
! 113: # include <sys/sysctl.h>
! 114: #endif /* GC_DARWIN_THREADS */
! 115:
! 116:
! 117:
! 118: #if defined(GC_DGUX386_THREADS)
! 119: # include <sys/dg_sys_info.h>
! 120: # include <sys/_int_psem.h>
! 121: /* sem_t is an uint in DG/UX */
! 122: typedef unsigned int sem_t;
! 123: #endif /* GC_DGUX386_THREADS */
! 124:
! 125: #ifndef __GNUC__
! 126: # define __inline__
! 127: #endif
! 128:
! 129: #ifdef GC_USE_LD_WRAP
! 130: # define WRAP_FUNC(f) __wrap_##f
! 131: # define REAL_FUNC(f) __real_##f
! 132: #else
! 133: # define WRAP_FUNC(f) GC_##f
! 134: # if !defined(GC_DGUX386_THREADS)
! 135: # define REAL_FUNC(f) f
! 136: # else /* GC_DGUX386_THREADS */
! 137: # define REAL_FUNC(f) __d10_##f
! 138: # endif /* GC_DGUX386_THREADS */
! 139: # undef pthread_create
! 140: # if !defined(GC_DARWIN_THREADS)
! 141: # undef pthread_sigmask
! 142: # endif
! 143: # undef pthread_join
! 144: # undef pthread_detach
! 145: #endif
! 146:
! 147: void GC_thr_init();
! 148:
! 149: static GC_bool parallel_initialized = FALSE;
! 150:
! 151: void GC_init_parallel();
! 152:
! 153: # if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
! 154:
! 155: /* We don't really support thread-local allocation with DBG_HDRS_ALL */
! 156:
! 157: #ifdef USE_HPUX_TLS
! 158: __thread
! 159: #endif
! 160: GC_key_t GC_thread_key;
! 161:
! 162: static GC_bool keys_initialized;
! 163:
! 164: /* Recover the contents of the freelist array fl into the global one gfl.*/
! 165: /* Note that the indexing scheme differs, in that gfl has finer size */
! 166: /* resolution, even if not all entries are used. */
! 167: /* We hold the allocator lock. */
! 168: static void return_freelists(ptr_t *fl, ptr_t *gfl)
! 169: {
! 170: int i;
! 171: ptr_t q, *qptr;
! 172: size_t nwords;
! 173:
! 174: for (i = 1; i < NFREELISTS; ++i) {
! 175: nwords = i * (GRANULARITY/sizeof(word));
! 176: qptr = fl + i;
! 177: q = *qptr;
! 178: if ((word)q >= HBLKSIZE) {
! 179: if (gfl[nwords] == 0) {
! 180: gfl[nwords] = q;
! 181: } else {
! 182: /* Concatenate: */
! 183: for (; (word)q >= HBLKSIZE; qptr = &(obj_link(q)), q = *qptr);
! 184: GC_ASSERT(0 == q);
! 185: *qptr = gfl[nwords];
! 186: gfl[nwords] = fl[i];
! 187: }
! 188: }
! 189: /* Clear fl[i], since the thread structure may hang around. */
! 190: /* Do it in a way that is likely to trap if we access it. */
! 191: fl[i] = (ptr_t)HBLKSIZE;
! 192: }
! 193: }
! 194:
! 195: /* We statically allocate a single "size 0" object. It is linked to */
! 196: /* itself, and is thus repeatedly reused for all size 0 allocation */
! 197: /* requests. (Size 0 gcj allocation requests are incorrect, and */
! 198: /* we arrange for those to fault asap.) */
! 199: static ptr_t size_zero_object = (ptr_t)(&size_zero_object);
! 200:
! 201: /* Each thread structure must be initialized. */
! 202: /* This call must be made from the new thread. */
! 203: /* Caller holds allocation lock. */
! 204: void GC_init_thread_local(GC_thread p)
! 205: {
! 206: int i;
! 207:
! 208: if (!keys_initialized) {
! 209: if (0 != GC_key_create(&GC_thread_key, 0)) {
! 210: ABORT("Failed to create key for local allocator");
! 211: }
! 212: keys_initialized = TRUE;
! 213: }
! 214: if (0 != GC_setspecific(GC_thread_key, p)) {
! 215: ABORT("Failed to set thread specific allocation pointers");
! 216: }
! 217: for (i = 1; i < NFREELISTS; ++i) {
! 218: p -> ptrfree_freelists[i] = (ptr_t)1;
! 219: p -> normal_freelists[i] = (ptr_t)1;
! 220: # ifdef GC_GCJ_SUPPORT
! 221: p -> gcj_freelists[i] = (ptr_t)1;
! 222: # endif
! 223: }
! 224: /* Set up the size 0 free lists. */
! 225: p -> ptrfree_freelists[0] = (ptr_t)(&size_zero_object);
! 226: p -> normal_freelists[0] = (ptr_t)(&size_zero_object);
! 227: # ifdef GC_GCJ_SUPPORT
! 228: p -> gcj_freelists[0] = (ptr_t)(-1);
! 229: # endif
! 230: }
! 231:
! 232: #ifdef GC_GCJ_SUPPORT
! 233: extern ptr_t * GC_gcjobjfreelist;
! 234: #endif
! 235:
! 236: /* We hold the allocator lock. */
! 237: void GC_destroy_thread_local(GC_thread p)
! 238: {
! 239: /* We currently only do this from the thread itself or from */
! 240: /* the fork handler for a child process. */
! 241: # ifndef HANDLE_FORK
! 242: GC_ASSERT(GC_getspecific(GC_thread_key) == (void *)p);
! 243: # endif
! 244: return_freelists(p -> ptrfree_freelists, GC_aobjfreelist);
! 245: return_freelists(p -> normal_freelists, GC_objfreelist);
! 246: # ifdef GC_GCJ_SUPPORT
! 247: return_freelists(p -> gcj_freelists, GC_gcjobjfreelist);
! 248: # endif
! 249: }
! 250:
! 251: extern GC_PTR GC_generic_malloc_many();
! 252:
! 253: GC_PTR GC_local_malloc(size_t bytes)
! 254: {
! 255: if (EXPECT(!SMALL_ENOUGH(bytes),0)) {
! 256: return(GC_malloc(bytes));
! 257: } else {
! 258: int index = INDEX_FROM_BYTES(bytes);
! 259: ptr_t * my_fl;
! 260: ptr_t my_entry;
! 261: # if defined(REDIRECT_MALLOC) && !defined(USE_PTHREAD_SPECIFIC)
! 262: GC_key_t k = GC_thread_key;
! 263: # endif
! 264: void * tsd;
! 265:
! 266: # if defined(REDIRECT_MALLOC) && !defined(USE_PTHREAD_SPECIFIC)
! 267: if (EXPECT(0 == k, 0)) {
! 268: /* This can happen if we get called when the world is */
! 269: /* being initialized. Whether we can actually complete */
! 270: /* the initialization then is unclear. */
! 271: GC_init_parallel();
! 272: k = GC_thread_key;
! 273: }
! 274: # endif
! 275: tsd = GC_getspecific(GC_thread_key);
! 276: # ifdef GC_ASSERTIONS
! 277: LOCK();
! 278: GC_ASSERT(tsd == (void *)GC_lookup_thread(pthread_self()));
! 279: UNLOCK();
! 280: # endif
! 281: my_fl = ((GC_thread)tsd) -> normal_freelists + index;
! 282: my_entry = *my_fl;
! 283: if (EXPECT((word)my_entry >= HBLKSIZE, 1)) {
! 284: ptr_t next = obj_link(my_entry);
! 285: GC_PTR result = (GC_PTR)my_entry;
! 286: *my_fl = next;
! 287: obj_link(my_entry) = 0;
! 288: PREFETCH_FOR_WRITE(next);
! 289: return result;
! 290: } else if ((word)my_entry - 1 < DIRECT_GRANULES) {
! 291: *my_fl = my_entry + index + 1;
! 292: return GC_malloc(bytes);
! 293: } else {
! 294: GC_generic_malloc_many(BYTES_FROM_INDEX(index), NORMAL, my_fl);
! 295: if (*my_fl == 0) return GC_oom_fn(bytes);
! 296: return GC_local_malloc(bytes);
! 297: }
! 298: }
! 299: }
! 300:
! 301: GC_PTR GC_local_malloc_atomic(size_t bytes)
! 302: {
! 303: if (EXPECT(!SMALL_ENOUGH(bytes), 0)) {
! 304: return(GC_malloc_atomic(bytes));
! 305: } else {
! 306: int index = INDEX_FROM_BYTES(bytes);
! 307: ptr_t * my_fl = ((GC_thread)GC_getspecific(GC_thread_key))
! 308: -> ptrfree_freelists + index;
! 309: ptr_t my_entry = *my_fl;
! 310:
! 311: if (EXPECT((word)my_entry >= HBLKSIZE, 1)) {
! 312: GC_PTR result = (GC_PTR)my_entry;
! 313: *my_fl = obj_link(my_entry);
! 314: return result;
! 315: } else if ((word)my_entry - 1 < DIRECT_GRANULES) {
! 316: *my_fl = my_entry + index + 1;
! 317: return GC_malloc_atomic(bytes);
! 318: } else {
! 319: GC_generic_malloc_many(BYTES_FROM_INDEX(index), PTRFREE, my_fl);
! 320: /* *my_fl is updated while the collector is excluded; */
! 321: /* the free list is always visible to the collector as */
! 322: /* such. */
! 323: if (*my_fl == 0) return GC_oom_fn(bytes);
! 324: return GC_local_malloc_atomic(bytes);
! 325: }
! 326: }
! 327: }
! 328:
! 329: #ifdef GC_GCJ_SUPPORT
! 330:
! 331: #include "include/gc_gcj.h"
! 332:
! 333: #ifdef GC_ASSERTIONS
! 334: extern GC_bool GC_gcj_malloc_initialized;
! 335: #endif
! 336:
! 337: extern int GC_gcj_kind;
! 338:
! 339: GC_PTR GC_local_gcj_malloc(size_t bytes,
! 340: void * ptr_to_struct_containing_descr)
! 341: {
! 342: GC_ASSERT(GC_gcj_malloc_initialized);
! 343: if (EXPECT(!SMALL_ENOUGH(bytes), 0)) {
! 344: return GC_gcj_malloc(bytes, ptr_to_struct_containing_descr);
! 345: } else {
! 346: int index = INDEX_FROM_BYTES(bytes);
! 347: ptr_t * my_fl = ((GC_thread)GC_getspecific(GC_thread_key))
! 348: -> gcj_freelists + index;
! 349: ptr_t my_entry = *my_fl;
! 350: if (EXPECT((word)my_entry >= HBLKSIZE, 1)) {
! 351: GC_PTR result = (GC_PTR)my_entry;
! 352: GC_ASSERT(!GC_incremental);
! 353: /* We assert that any concurrent marker will stop us. */
! 354: /* Thus it is impossible for a mark procedure to see the */
! 355: /* allocation of the next object, but to see this object */
! 356: /* still containing a free list pointer. Otherwise the */
! 357: /* marker might find a random "mark descriptor". */
! 358: *(volatile ptr_t *)my_fl = obj_link(my_entry);
! 359: /* We must update the freelist before we store the pointer. */
! 360: /* Otherwise a GC at this point would see a corrupted */
! 361: /* free list. */
! 362: /* A memory barrier is probably never needed, since the */
! 363: /* action of stopping this thread will cause prior writes */
! 364: /* to complete. */
! 365: GC_ASSERT(((void * volatile *)result)[1] == 0);
! 366: *(void * volatile *)result = ptr_to_struct_containing_descr;
! 367: return result;
! 368: } else if ((word)my_entry - 1 < DIRECT_GRANULES) {
! 369: if (!GC_incremental) *my_fl = my_entry + index + 1;
! 370: /* In the incremental case, we always have to take this */
! 371: /* path. Thus we leave the counter alone. */
! 372: return GC_gcj_malloc(bytes, ptr_to_struct_containing_descr);
! 373: } else {
! 374: GC_generic_malloc_many(BYTES_FROM_INDEX(index), GC_gcj_kind, my_fl);
! 375: if (*my_fl == 0) return GC_oom_fn(bytes);
! 376: return GC_local_gcj_malloc(bytes, ptr_to_struct_containing_descr);
! 377: }
! 378: }
! 379: }
! 380:
! 381: #endif /* GC_GCJ_SUPPORT */
! 382:
! 383: # else /* !THREAD_LOCAL_ALLOC && !DBG_HDRS_ALL */
! 384:
! 385: # define GC_destroy_thread_local(t)
! 386:
! 387: # endif /* !THREAD_LOCAL_ALLOC */
! 388:
! 389: #if 0
! 390: /*
! 391: To make sure that we're using LinuxThreads and not some other thread
! 392: package, we generate a dummy reference to `pthread_kill_other_threads_np'
! 393: (was `__pthread_initial_thread_bos' but that disappeared),
! 394: which is a symbol defined in LinuxThreads, but (hopefully) not in other
! 395: thread packages.
! 396:
! 397: We no longer do this, since this code is now portable enough that it might
! 398: actually work for something else.
! 399: */
! 400: void (*dummy_var_to_force_linux_threads)() = pthread_kill_other_threads_np;
! 401: #endif /* 0 */
! 402:
! 403: long GC_nprocs = 1; /* Number of processors. We may not have */
! 404: /* access to all of them, but this is as good */
! 405: /* a guess as any ... */
! 406:
! 407: #ifdef PARALLEL_MARK
! 408:
! 409: # ifndef MAX_MARKERS
! 410: # define MAX_MARKERS 16
! 411: # endif
! 412:
! 413: static ptr_t marker_sp[MAX_MARKERS] = {0};
! 414:
! 415: void * GC_mark_thread(void * id)
! 416: {
! 417: word my_mark_no = 0;
! 418:
! 419: marker_sp[(word)id] = GC_approx_sp();
! 420: for (;; ++my_mark_no) {
! 421: /* GC_mark_no is passed only to allow GC_help_marker to terminate */
! 422: /* promptly. This is important if it were called from the signal */
! 423: /* handler or from the GC lock acquisition code. Under Linux, it's */
! 424: /* not safe to call it from a signal handler, since it uses mutexes */
! 425: /* and condition variables. Since it is called only here, the */
! 426: /* argument is unnecessary. */
! 427: if (my_mark_no < GC_mark_no || my_mark_no > GC_mark_no + 2) {
! 428: /* resynchronize if we get far off, e.g. because GC_mark_no */
! 429: /* wrapped. */
! 430: my_mark_no = GC_mark_no;
! 431: }
! 432: # ifdef DEBUG_THREADS
! 433: GC_printf1("Starting mark helper for mark number %ld\n", my_mark_no);
! 434: # endif
! 435: GC_help_marker(my_mark_no);
! 436: }
! 437: }
! 438:
! 439: extern long GC_markers; /* Number of mark threads we would */
! 440: /* like to have. Includes the */
! 441: /* initiating thread. */
! 442:
! 443: pthread_t GC_mark_threads[MAX_MARKERS];
! 444:
! 445: #define PTHREAD_CREATE REAL_FUNC(pthread_create)
! 446:
! 447: static void start_mark_threads()
! 448: {
! 449: unsigned i;
! 450: pthread_attr_t attr;
! 451:
! 452: if (GC_markers > MAX_MARKERS) {
! 453: WARN("Limiting number of mark threads\n", 0);
! 454: GC_markers = MAX_MARKERS;
! 455: }
! 456: if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed");
! 457:
! 458: if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
! 459: ABORT("pthread_attr_setdetachstate failed");
! 460:
! 461: # if defined(HPUX) || defined(GC_DGUX386_THREADS)
! 462: /* Default stack size is usually too small: fix it. */
! 463: /* Otherwise marker threads or GC may run out of */
! 464: /* space. */
! 465: # define MIN_STACK_SIZE (8*HBLKSIZE*sizeof(word))
! 466: {
! 467: size_t old_size;
! 468: int code;
! 469:
! 470: if (pthread_attr_getstacksize(&attr, &old_size) != 0)
! 471: ABORT("pthread_attr_getstacksize failed\n");
! 472: if (old_size < MIN_STACK_SIZE) {
! 473: if (pthread_attr_setstacksize(&attr, MIN_STACK_SIZE) != 0)
! 474: ABORT("pthread_attr_setstacksize failed\n");
! 475: }
! 476: }
! 477: # endif /* HPUX || GC_DGUX386_THREADS */
! 478: # ifdef CONDPRINT
! 479: if (GC_print_stats) {
! 480: GC_printf1("Starting %ld marker threads\n", GC_markers - 1);
! 481: }
! 482: # endif
! 483: for (i = 0; i < GC_markers - 1; ++i) {
! 484: if (0 != PTHREAD_CREATE(GC_mark_threads + i, &attr,
! 485: GC_mark_thread, (void *)(word)i)) {
! 486: WARN("Marker thread creation failed, errno = %ld.\n", errno);
! 487: }
! 488: }
! 489: }
! 490:
! 491: #else /* !PARALLEL_MARK */
! 492:
! 493: static __inline__ void start_mark_threads()
! 494: {
! 495: }
! 496:
! 497: #endif /* !PARALLEL_MARK */
! 498:
! 499: /* Defining INSTALL_LOOPING_SEGV_HANDLER causes SIGSEGV and SIGBUS to */
! 500: /* result in an infinite loop in a signal handler. This can be very */
! 501: /* useful for debugging, since (as of RH7) gdb still seems to have */
! 502: /* serious problems with threads. */
! 503: #ifdef INSTALL_LOOPING_SEGV_HANDLER
! 504: void GC_looping_handler(int sig)
! 505: {
! 506: GC_printf3("Signal %ld in thread %lx, pid %ld\n",
! 507: sig, pthread_self(), getpid());
! 508: for (;;);
! 509: }
! 510: #endif
! 511:
! 512: GC_bool GC_thr_initialized = FALSE;
! 513:
! 514: volatile GC_thread GC_threads[THREAD_TABLE_SZ];
! 515:
! 516: void GC_push_thread_structures GC_PROTO((void))
! 517: {
! 518: GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
! 519: # if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
! 520: GC_push_all((ptr_t)(&GC_thread_key),
! 521: (ptr_t)(&GC_thread_key)+sizeof(&GC_thread_key));
! 522: # endif
! 523: }
! 524:
! 525: #ifdef THREAD_LOCAL_ALLOC
! 526: /* We must explicitly mark ptrfree and gcj free lists, since the free */
! 527: /* list links wouldn't otherwise be found. We also set them in the */
! 528: /* normal free lists, since that involves touching less memory than if */
! 529: /* we scanned them normally. */
! 530: void GC_mark_thread_local_free_lists(void)
! 531: {
! 532: int i, j;
! 533: GC_thread p;
! 534: ptr_t q;
! 535:
! 536: for (i = 0; i < THREAD_TABLE_SZ; ++i) {
! 537: for (p = GC_threads[i]; 0 != p; p = p -> next) {
! 538: for (j = 1; j < NFREELISTS; ++j) {
! 539: q = p -> ptrfree_freelists[j];
! 540: if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
! 541: q = p -> normal_freelists[j];
! 542: if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
! 543: # ifdef GC_GCJ_SUPPORT
! 544: q = p -> gcj_freelists[j];
! 545: if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
! 546: # endif /* GC_GCJ_SUPPORT */
! 547: }
! 548: }
! 549: }
! 550: }
! 551: #endif /* THREAD_LOCAL_ALLOC */
! 552:
! 553: static struct GC_Thread_Rep first_thread;
! 554:
! 555: /* Add a thread to GC_threads. We assume it wasn't already there. */
! 556: /* Caller holds allocation lock. */
! 557: GC_thread GC_new_thread(pthread_t id)
! 558: {
! 559: int hv = ((word)id) % THREAD_TABLE_SZ;
! 560: GC_thread result;
! 561: static GC_bool first_thread_used = FALSE;
! 562:
! 563: if (!first_thread_used) {
! 564: result = &first_thread;
! 565: first_thread_used = TRUE;
! 566: } else {
! 567: result = (struct GC_Thread_Rep *)
! 568: GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL);
! 569: }
! 570: if (result == 0) return(0);
! 571: result -> id = id;
! 572: result -> next = GC_threads[hv];
! 573: GC_threads[hv] = result;
! 574: GC_ASSERT(result -> flags == 0 && result -> thread_blocked == 0);
! 575: return(result);
! 576: }
! 577:
! 578: /* Delete a thread from GC_threads. We assume it is there. */
! 579: /* (The code intentionally traps if it wasn't.) */
! 580: /* Caller holds allocation lock. */
! 581: void GC_delete_thread(pthread_t id)
! 582: {
! 583: int hv = ((word)id) % THREAD_TABLE_SZ;
! 584: register GC_thread p = GC_threads[hv];
! 585: register GC_thread prev = 0;
! 586:
! 587: while (!pthread_equal(p -> id, id)) {
! 588: prev = p;
! 589: p = p -> next;
! 590: }
! 591: if (prev == 0) {
! 592: GC_threads[hv] = p -> next;
! 593: } else {
! 594: prev -> next = p -> next;
! 595: }
! 596: GC_INTERNAL_FREE(p);
! 597: }
! 598:
! 599: /* If a thread has been joined, but we have not yet */
! 600: /* been notified, then there may be more than one thread */
! 601: /* in the table with the same pthread id. */
! 602: /* This is OK, but we need a way to delete a specific one. */
! 603: void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
! 604: {
! 605: int hv = ((word)id) % THREAD_TABLE_SZ;
! 606: register GC_thread p = GC_threads[hv];
! 607: register GC_thread prev = 0;
! 608:
! 609: while (p != gc_id) {
! 610: prev = p;
! 611: p = p -> next;
! 612: }
! 613: if (prev == 0) {
! 614: GC_threads[hv] = p -> next;
! 615: } else {
! 616: prev -> next = p -> next;
! 617: }
! 618: GC_INTERNAL_FREE(p);
! 619: }
! 620:
! 621: /* Return a GC_thread corresponding to a given thread_t. */
! 622: /* Returns 0 if it's not there. */
! 623: /* Caller holds allocation lock or otherwise inhibits */
! 624: /* updates. */
! 625: /* If there is more than one thread with the given id we */
! 626: /* return the most recent one. */
! 627: GC_thread GC_lookup_thread(pthread_t id)
! 628: {
! 629: int hv = ((word)id) % THREAD_TABLE_SZ;
! 630: register GC_thread p = GC_threads[hv];
! 631:
! 632: while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
! 633: return(p);
! 634: }
! 635:
! 636: #ifdef HANDLE_FORK
! 637: /* Remove all entries from the GC_threads table, except the */
! 638: /* one for the current thread. We need to do this in the child */
! 639: /* process after a fork(), since only the current thread */
! 640: /* survives in the child. */
! 641: void GC_remove_all_threads_but_me(void)
! 642: {
! 643: pthread_t self = pthread_self();
! 644: int hv;
! 645: GC_thread p, next, me;
! 646:
! 647: for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) {
! 648: me = 0;
! 649: for (p = GC_threads[hv]; 0 != p; p = next) {
! 650: next = p -> next;
! 651: if (p -> id == self) {
! 652: me = p;
! 653: p -> next = 0;
! 654: } else {
! 655: # ifdef THREAD_LOCAL_ALLOC
! 656: if (!(p -> flags & FINISHED)) {
! 657: GC_destroy_thread_local(p);
! 658: }
! 659: # endif /* THREAD_LOCAL_ALLOC */
! 660: if (p != &first_thread) GC_INTERNAL_FREE(p);
! 661: }
! 662: }
! 663: GC_threads[hv] = me;
! 664: }
! 665: }
! 666: #endif /* HANDLE_FORK */
! 667:
! 668: #ifdef USE_PROC_FOR_LIBRARIES
! 669: int GC_segment_is_thread_stack(ptr_t lo, ptr_t hi)
! 670: {
! 671: int i;
! 672: GC_thread p;
! 673:
! 674: # ifdef PARALLEL_MARK
! 675: for (i = 0; i < GC_markers; ++i) {
! 676: if (marker_sp[i] > lo & marker_sp[i] < hi) return 1;
! 677: }
! 678: # endif
! 679: for (i = 0; i < THREAD_TABLE_SZ; i++) {
! 680: for (p = GC_threads[i]; p != 0; p = p -> next) {
! 681: if (0 != p -> stack_end) {
! 682: # ifdef STACK_GROWS_UP
! 683: if (p -> stack_end >= lo && p -> stack_end < hi) return 1;
! 684: # else /* STACK_GROWS_DOWN */
! 685: if (p -> stack_end > lo && p -> stack_end <= hi) return 1;
! 686: # endif
! 687: }
! 688: }
! 689: }
! 690: return 0;
! 691: }
! 692: #endif /* USE_PROC_FOR_LIBRARIES */
! 693:
! 694: #ifdef GC_LINUX_THREADS
! 695: /* Return the number of processors, or i<= 0 if it can't be determined. */
! 696: int GC_get_nprocs()
! 697: {
! 698: /* Should be "return sysconf(_SC_NPROCESSORS_ONLN);" but that */
! 699: /* appears to be buggy in many cases. */
! 700: /* We look for lines "cpu<n>" in /proc/stat. */
! 701: # define STAT_BUF_SIZE 4096
! 702: # define STAT_READ read
! 703: /* If read is wrapped, this may need to be redefined to call */
! 704: /* the real one. */
! 705: char stat_buf[STAT_BUF_SIZE];
! 706: int f;
! 707: word result = 1;
! 708: /* Some old kernels only have a single "cpu nnnn ..." */
! 709: /* entry in /proc/stat. We identify those as */
! 710: /* uniprocessors. */
! 711: size_t i, len = 0;
! 712:
! 713: f = open("/proc/stat", O_RDONLY);
! 714: if (f < 0 || (len = STAT_READ(f, stat_buf, STAT_BUF_SIZE)) < 100) {
! 715: WARN("Couldn't read /proc/stat\n", 0);
! 716: return -1;
! 717: }
! 718: for (i = 0; i < len - 100; ++i) {
! 719: if (stat_buf[i] == '\n' && stat_buf[i+1] == 'c'
! 720: && stat_buf[i+2] == 'p' && stat_buf[i+3] == 'u') {
! 721: int cpu_no = atoi(stat_buf + i + 4);
! 722: if (cpu_no >= result) result = cpu_no + 1;
! 723: }
! 724: }
! 725: close(f);
! 726: return result;
! 727: }
! 728: #endif /* GC_LINUX_THREADS */
! 729:
! 730: /* We hold the GC lock. Wait until an in-progress GC has finished. */
! 731: /* Repeatedly RELEASES GC LOCK in order to wait. */
! 732: /* If wait_for_all is true, then we exit with the GC lock held and no */
! 733: /* collection in progress; otherwise we just wait for the current GC */
! 734: /* to finish. */
! 735: extern GC_bool GC_collection_in_progress();
! 736: void GC_wait_for_gc_completion(GC_bool wait_for_all)
! 737: {
! 738: if (GC_incremental && GC_collection_in_progress()) {
! 739: int old_gc_no = GC_gc_no;
! 740:
! 741: /* Make sure that no part of our stack is still on the mark stack, */
! 742: /* since it's about to be unmapped. */
! 743: while (GC_incremental && GC_collection_in_progress()
! 744: && (wait_for_all || old_gc_no == GC_gc_no)) {
! 745: ENTER_GC();
! 746: GC_collect_a_little_inner(1);
! 747: EXIT_GC();
! 748: UNLOCK();
! 749: sched_yield();
! 750: LOCK();
! 751: }
! 752: }
! 753: }
! 754:
! 755: #ifdef HANDLE_FORK
! 756: /* Procedures called before and after a fork. The goal here is to make */
! 757: /* it safe to call GC_malloc() in a forked child. It's unclear that is */
! 758: /* attainable, since the single UNIX spec seems to imply that one */
! 759: /* should only call async-signal-safe functions, and we probably can't */
! 760: /* quite guarantee that. But we give it our best shot. (That same */
! 761: /* spec also implies that it's not safe to call the system malloc */
! 762: /* between fork() and exec(). Thus we're doing no worse than it. */
! 763:
! 764: /* Called before a fork() */
! 765: void GC_fork_prepare_proc(void)
! 766: {
! 767: /* Acquire all relevant locks, so that after releasing the locks */
! 768: /* the child will see a consistent state in which monitor */
! 769: /* invariants hold. Unfortunately, we can't acquire libc locks */
! 770: /* we might need, and there seems to be no guarantee that libc */
! 771: /* must install a suitable fork handler. */
! 772: /* Wait for an ongoing GC to finish, since we can't finish it in */
! 773: /* the (one remaining thread in) the child. */
! 774: LOCK();
! 775: # if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
! 776: GC_wait_for_reclaim();
! 777: # endif
! 778: GC_wait_for_gc_completion(TRUE);
! 779: # if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
! 780: GC_acquire_mark_lock();
! 781: # endif
! 782: }
! 783:
! 784: /* Called in parent after a fork() */
! 785: void GC_fork_parent_proc(void)
! 786: {
! 787: # if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
! 788: GC_release_mark_lock();
! 789: # endif
! 790: UNLOCK();
! 791: }
! 792:
! 793: /* Called in child after a fork() */
! 794: void GC_fork_child_proc(void)
! 795: {
! 796: /* Clean up the thread table, so that just our thread is left. */
! 797: # if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
! 798: GC_release_mark_lock();
! 799: # endif
! 800: GC_remove_all_threads_but_me();
! 801: # ifdef PARALLEL_MARK
! 802: /* Turn off parallel marking in the child, since we are probably */
! 803: /* just going to exec, and we would have to restart mark threads. */
! 804: GC_markers = 1;
! 805: GC_parallel = FALSE;
! 806: # endif /* PARALLEL_MARK */
! 807: UNLOCK();
! 808: }
! 809: #endif /* HANDLE_FORK */
! 810:
! 811: #if defined(GC_DGUX386_THREADS)
! 812: /* Return the number of processors, or i<= 0 if it can't be determined. */
! 813: int GC_get_nprocs()
! 814: {
! 815: /* <takis@XFree86.Org> */
! 816: int numCpus;
! 817: struct dg_sys_info_pm_info pm_sysinfo;
! 818: int status =0;
! 819:
! 820: status = dg_sys_info((long int *) &pm_sysinfo,
! 821: DG_SYS_INFO_PM_INFO_TYPE, DG_SYS_INFO_PM_CURRENT_VERSION);
! 822: if (status < 0)
! 823: /* set -1 for error */
! 824: numCpus = -1;
! 825: else
! 826: /* Active CPUs */
! 827: numCpus = pm_sysinfo.idle_vp_count;
! 828:
! 829: # ifdef DEBUG_THREADS
! 830: GC_printf1("Number of active CPUs in this system: %d\n", numCpus);
! 831: # endif
! 832: return(numCpus);
! 833: }
! 834: #endif /* GC_DGUX386_THREADS */
! 835:
! 836: /* We hold the allocation lock. */
! 837: void GC_thr_init()
! 838: {
! 839: # ifndef GC_DARWIN_THREADS
! 840: int dummy;
! 841: # endif
! 842: GC_thread t;
! 843:
! 844: if (GC_thr_initialized) return;
! 845: GC_thr_initialized = TRUE;
! 846:
! 847: # ifdef HANDLE_FORK
! 848: /* Prepare for a possible fork. */
! 849: pthread_atfork(GC_fork_prepare_proc, GC_fork_parent_proc,
! 850: GC_fork_child_proc);
! 851: # endif /* HANDLE_FORK */
! 852: /* Add the initial thread, so we can stop it. */
! 853: t = GC_new_thread(pthread_self());
! 854: # ifdef GC_DARWIN_THREADS
! 855: t -> stop_info.mach_thread = mach_thread_self();
! 856: # else
! 857: t -> stop_info.stack_ptr = (ptr_t)(&dummy);
! 858: # endif
! 859: t -> flags = DETACHED | MAIN_THREAD;
! 860:
! 861: GC_stop_init();
! 862:
! 863: /* Set GC_nprocs. */
! 864: {
! 865: char * nprocs_string = GETENV("GC_NPROCS");
! 866: GC_nprocs = -1;
! 867: if (nprocs_string != NULL) GC_nprocs = atoi(nprocs_string);
! 868: }
! 869: if (GC_nprocs <= 0) {
! 870: # if defined(GC_HPUX_THREADS)
! 871: GC_nprocs = pthread_num_processors_np();
! 872: # endif
! 873: # if defined(GC_OSF1_THREADS)
! 874: GC_nprocs = sysconf(_SC_NPROCESSORS_ONLN);
! 875: if (GC_nprocs <= 0) GC_nprocs = 1;
! 876: # endif
! 877: # if defined(GC_FREEBSD_THREADS)
! 878: GC_nprocs = 1;
! 879: # endif
! 880: # if defined(GC_DARWIN_THREADS)
! 881: int ncpus = 1;
! 882: size_t len = sizeof(ncpus);
! 883: sysctl((int[2]) {CTL_HW, HW_NCPU}, 2, &ncpus, &len, NULL, 0);
! 884: GC_nprocs = ncpus;
! 885: # endif
! 886: # if defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)
! 887: GC_nprocs = GC_get_nprocs();
! 888: # endif
! 889: }
! 890: if (GC_nprocs <= 0) {
! 891: WARN("GC_get_nprocs() returned %ld\n", GC_nprocs);
! 892: GC_nprocs = 2;
! 893: # ifdef PARALLEL_MARK
! 894: GC_markers = 1;
! 895: # endif
! 896: } else {
! 897: # ifdef PARALLEL_MARK
! 898: {
! 899: char * markers_string = GETENV("GC_MARKERS");
! 900: if (markers_string != NULL) {
! 901: GC_markers = atoi(markers_string);
! 902: } else {
! 903: GC_markers = GC_nprocs;
! 904: }
! 905: }
! 906: # endif
! 907: }
! 908: # ifdef PARALLEL_MARK
! 909: # ifdef CONDPRINT
! 910: if (GC_print_stats) {
! 911: GC_printf2("Number of processors = %ld, "
! 912: "number of marker threads = %ld\n", GC_nprocs, GC_markers);
! 913: }
! 914: # endif
! 915: if (GC_markers == 1) {
! 916: GC_parallel = FALSE;
! 917: # ifdef CONDPRINT
! 918: if (GC_print_stats) {
! 919: GC_printf0("Single marker thread, turning off parallel marking\n");
! 920: }
! 921: # endif
! 922: } else {
! 923: GC_parallel = TRUE;
! 924: /* Disable true incremental collection, but generational is OK. */
! 925: GC_time_limit = GC_TIME_UNLIMITED;
! 926: }
! 927: # endif
! 928: }
! 929:
! 930:
! 931: /* Perform all initializations, including those that */
! 932: /* may require allocation. */
! 933: /* Called without allocation lock. */
! 934: /* Must be called before a second thread is created. */
! 935: /* Called without allocation lock. */
! 936: void GC_init_parallel()
! 937: {
! 938: if (parallel_initialized) return;
! 939: parallel_initialized = TRUE;
! 940:
! 941: /* GC_init() calls us back, so set flag first. */
! 942: if (!GC_is_initialized) GC_init();
! 943: /* If we are using a parallel marker, start the helper threads. */
! 944: # ifdef PARALLEL_MARK
! 945: if (GC_parallel) start_mark_threads();
! 946: # endif
! 947: /* Initialize thread local free lists if used. */
! 948: # if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
! 949: LOCK();
! 950: GC_init_thread_local(GC_lookup_thread(pthread_self()));
! 951: UNLOCK();
! 952: # endif
! 953: }
! 954:
! 955:
! 956: #if !defined(GC_DARWIN_THREADS)
! 957: int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, sigset_t *oset)
! 958: {
! 959: sigset_t fudged_set;
! 960:
! 961: if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
! 962: fudged_set = *set;
! 963: sigdelset(&fudged_set, SIG_SUSPEND);
! 964: set = &fudged_set;
! 965: }
! 966: return(REAL_FUNC(pthread_sigmask)(how, set, oset));
! 967: }
! 968: #endif /* !GC_DARWIN_THREADS */
! 969:
! 970: /* Wrappers for functions that are likely to block for an appreciable */
! 971: /* length of time. Must be called in pairs, if at all. */
! 972: /* Nothing much beyond the system call itself should be executed */
! 973: /* between these. */
! 974:
! 975: void GC_start_blocking(void) {
! 976: # define SP_SLOP 128
! 977: GC_thread me;
! 978: LOCK();
! 979: me = GC_lookup_thread(pthread_self());
! 980: GC_ASSERT(!(me -> thread_blocked));
! 981: # ifdef SPARC
! 982: me -> stop_info.stack_ptr = (ptr_t)GC_save_regs_in_stack();
! 983: # else
! 984: # ifndef GC_DARWIN_THREADS
! 985: me -> stop_info.stack_ptr = (ptr_t)GC_approx_sp();
! 986: # endif
! 987: # endif
! 988: # ifdef IA64
! 989: me -> backing_store_ptr = (ptr_t)GC_save_regs_in_stack() + SP_SLOP;
! 990: # endif
! 991: /* Add some slop to the stack pointer, since the wrapped call may */
! 992: /* end up pushing more callee-save registers. */
! 993: # ifndef GC_DARWIN_THREADS
! 994: # ifdef STACK_GROWS_UP
! 995: me -> stop_info.stack_ptr += SP_SLOP;
! 996: # else
! 997: me -> stop_info.stack_ptr -= SP_SLOP;
! 998: # endif
! 999: # endif
! 1000: me -> thread_blocked = TRUE;
! 1001: UNLOCK();
! 1002: }
! 1003:
! 1004: void GC_end_blocking(void) {
! 1005: GC_thread me;
! 1006: LOCK(); /* This will block if the world is stopped. */
! 1007: me = GC_lookup_thread(pthread_self());
! 1008: GC_ASSERT(me -> thread_blocked);
! 1009: me -> thread_blocked = FALSE;
! 1010: UNLOCK();
! 1011: }
! 1012:
! 1013: #if defined(GC_DGUX386_THREADS)
! 1014: #define __d10_sleep sleep
! 1015: #endif /* GC_DGUX386_THREADS */
! 1016:
! 1017: /* A wrapper for the standard C sleep function */
! 1018: int WRAP_FUNC(sleep) (unsigned int seconds)
! 1019: {
! 1020: int result;
! 1021:
! 1022: GC_start_blocking();
! 1023: result = REAL_FUNC(sleep)(seconds);
! 1024: GC_end_blocking();
! 1025: return result;
! 1026: }
! 1027:
! 1028: struct start_info {
! 1029: void *(*start_routine)(void *);
! 1030: void *arg;
! 1031: word flags;
! 1032: sem_t registered; /* 1 ==> in our thread table, but */
! 1033: /* parent hasn't yet noticed. */
! 1034: };
! 1035:
! 1036: /* Called at thread exit. */
! 1037: /* Never called for main thread. That's OK, since it */
! 1038: /* results in at most a tiny one-time leak. And */
! 1039: /* linuxthreads doesn't reclaim the main threads */
! 1040: /* resources or id anyway. */
! 1041: void GC_thread_exit_proc(void *arg)
! 1042: {
! 1043: GC_thread me;
! 1044:
! 1045: LOCK();
! 1046: me = GC_lookup_thread(pthread_self());
! 1047: GC_destroy_thread_local(me);
! 1048: if (me -> flags & DETACHED) {
! 1049: GC_delete_thread(pthread_self());
! 1050: } else {
! 1051: me -> flags |= FINISHED;
! 1052: }
! 1053: # if defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_SPECIFIC) \
! 1054: && !defined(USE_HPUX_TLS) && !defined(DBG_HDRS_ALL)
! 1055: GC_remove_specific(GC_thread_key);
! 1056: # endif
! 1057: GC_wait_for_gc_completion(FALSE);
! 1058: UNLOCK();
! 1059: }
! 1060:
! 1061: int WRAP_FUNC(pthread_join)(pthread_t thread, void **retval)
! 1062: {
! 1063: int result;
! 1064: GC_thread thread_gc_id;
! 1065:
! 1066: LOCK();
! 1067: thread_gc_id = GC_lookup_thread(thread);
! 1068: /* This is guaranteed to be the intended one, since the thread id */
! 1069: /* cant have been recycled by pthreads. */
! 1070: UNLOCK();
! 1071: result = REAL_FUNC(pthread_join)(thread, retval);
! 1072: # if defined (GC_FREEBSD_THREADS)
! 1073: /* On FreeBSD, the wrapped pthread_join() sometimes returns (what
! 1074: appears to be) a spurious EINTR which caused the test and real code
! 1075: to gratuitously fail. Having looked at system pthread library source
! 1076: code, I see how this return code may be generated. In one path of
! 1077: code, pthread_join() just returns the errno setting of the thread
! 1078: being joined. This does not match the POSIX specification or the
! 1079: local man pages thus I have taken the liberty to catch this one
! 1080: spurious return value properly conditionalized on GC_FREEBSD_THREADS. */
! 1081: if (result == EINTR) result = 0;
! 1082: # endif
! 1083: if (result == 0) {
! 1084: LOCK();
! 1085: /* Here the pthread thread id may have been recycled. */
! 1086: GC_delete_gc_thread(thread, thread_gc_id);
! 1087: UNLOCK();
! 1088: }
! 1089: return result;
! 1090: }
! 1091:
! 1092: int
! 1093: WRAP_FUNC(pthread_detach)(pthread_t thread)
! 1094: {
! 1095: int result;
! 1096: GC_thread thread_gc_id;
! 1097:
! 1098: LOCK();
! 1099: thread_gc_id = GC_lookup_thread(thread);
! 1100: UNLOCK();
! 1101: result = REAL_FUNC(pthread_detach)(thread);
! 1102: if (result == 0) {
! 1103: LOCK();
! 1104: thread_gc_id -> flags |= DETACHED;
! 1105: /* Here the pthread thread id may have been recycled. */
! 1106: if (thread_gc_id -> flags & FINISHED) {
! 1107: GC_delete_gc_thread(thread, thread_gc_id);
! 1108: }
! 1109: UNLOCK();
! 1110: }
! 1111: return result;
! 1112: }
! 1113:
! 1114: void * GC_start_routine(void * arg)
! 1115: {
! 1116: int dummy;
! 1117: struct start_info * si = arg;
! 1118: void * result;
! 1119: GC_thread me;
! 1120: pthread_t my_pthread;
! 1121: void *(*start)(void *);
! 1122: void *start_arg;
! 1123:
! 1124: my_pthread = pthread_self();
! 1125: # ifdef DEBUG_THREADS
! 1126: GC_printf1("Starting thread 0x%lx\n", my_pthread);
! 1127: GC_printf1("pid = %ld\n", (long) getpid());
! 1128: GC_printf1("sp = 0x%lx\n", (long) &arg);
! 1129: # endif
! 1130: LOCK();
! 1131: me = GC_new_thread(my_pthread);
! 1132: #ifdef GC_DARWIN_THREADS
! 1133: me -> stop_info.mach_thread = mach_thread_self();
! 1134: #else
! 1135: me -> stop_info.stack_ptr = 0;
! 1136: #endif
! 1137: me -> flags = si -> flags;
! 1138: /* me -> stack_end = GC_linux_stack_base(); -- currently (11/99) */
! 1139: /* doesn't work because the stack base in /proc/self/stat is the */
! 1140: /* one for the main thread. There is a strong argument that that's */
! 1141: /* a kernel bug, but a pervasive one. */
! 1142: # ifdef STACK_GROWS_DOWN
! 1143: me -> stack_end = (ptr_t)(((word)(&dummy) + (GC_page_size - 1))
! 1144: & ~(GC_page_size - 1));
! 1145: # ifndef GC_DARWIN_THREADS
! 1146: me -> stop_info.stack_ptr = me -> stack_end - 0x10;
! 1147: # endif
! 1148: /* Needs to be plausible, since an asynchronous stack mark */
! 1149: /* should not crash. */
! 1150: # else
! 1151: me -> stack_end = (ptr_t)((word)(&dummy) & ~(GC_page_size - 1));
! 1152: me -> stop_info.stack_ptr = me -> stack_end + 0x10;
! 1153: # endif
! 1154: /* This is dubious, since we may be more than a page into the stack, */
! 1155: /* and hence skip some of it, though it's not clear that matters. */
! 1156: # ifdef IA64
! 1157: me -> backing_store_end = (ptr_t)
! 1158: (GC_save_regs_in_stack() & ~(GC_page_size - 1));
! 1159: /* This is also < 100% convincing. We should also read this */
! 1160: /* from /proc, but the hook to do so isn't there yet. */
! 1161: # endif /* IA64 */
! 1162: UNLOCK();
! 1163: start = si -> start_routine;
! 1164: # ifdef DEBUG_THREADS
! 1165: GC_printf1("start_routine = 0x%lx\n", start);
! 1166: # endif
! 1167: start_arg = si -> arg;
! 1168: sem_post(&(si -> registered)); /* Last action on si. */
! 1169: /* OK to deallocate. */
! 1170: pthread_cleanup_push(GC_thread_exit_proc, 0);
! 1171: # if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
! 1172: LOCK();
! 1173: GC_init_thread_local(me);
! 1174: UNLOCK();
! 1175: # endif
! 1176: result = (*start)(start_arg);
! 1177: #if DEBUG_THREADS
! 1178: GC_printf1("Finishing thread 0x%x\n", pthread_self());
! 1179: #endif
! 1180: me -> status = result;
! 1181: me -> flags |= FINISHED;
! 1182: pthread_cleanup_pop(1);
! 1183: /* Cleanup acquires lock, ensuring that we can't exit */
! 1184: /* while a collection that thinks we're alive is trying to stop */
! 1185: /* us. */
! 1186: return(result);
! 1187: }
! 1188:
! 1189: int
! 1190: WRAP_FUNC(pthread_create)(pthread_t *new_thread,
! 1191: const pthread_attr_t *attr,
! 1192: void *(*start_routine)(void *), void *arg)
! 1193: {
! 1194: int result;
! 1195: int detachstate;
! 1196: word my_flags = 0;
! 1197: struct start_info * si;
! 1198: /* This is otherwise saved only in an area mmapped by the thread */
! 1199: /* library, which isn't visible to the collector. */
! 1200:
! 1201: /* We resist the temptation to muck with the stack size here, */
! 1202: /* even if the default is unreasonably small. That's the client's */
! 1203: /* responsibility. */
! 1204:
! 1205: LOCK();
! 1206: si = (struct start_info *)GC_INTERNAL_MALLOC(sizeof(struct start_info),
! 1207: NORMAL);
! 1208: UNLOCK();
! 1209: if (!parallel_initialized) GC_init_parallel();
! 1210: if (0 == si) return(ENOMEM);
! 1211: sem_init(&(si -> registered), 0, 0);
! 1212: si -> start_routine = start_routine;
! 1213: si -> arg = arg;
! 1214: LOCK();
! 1215: if (!GC_thr_initialized) GC_thr_init();
! 1216: # ifdef GC_ASSERTIONS
! 1217: {
! 1218: int stack_size;
! 1219: if (NULL == attr) {
! 1220: pthread_attr_t my_attr;
! 1221: pthread_attr_init(&my_attr);
! 1222: pthread_attr_getstacksize(&my_attr, &stack_size);
! 1223: } else {
! 1224: pthread_attr_getstacksize(attr, &stack_size);
! 1225: }
! 1226: GC_ASSERT(stack_size >= (8*HBLKSIZE*sizeof(word)));
! 1227: /* Our threads may need to do some work for the GC. */
! 1228: /* Ridiculously small threads won't work, and they */
! 1229: /* probably wouldn't work anyway. */
! 1230: }
! 1231: # endif
! 1232: if (NULL == attr) {
! 1233: detachstate = PTHREAD_CREATE_JOINABLE;
! 1234: } else {
! 1235: pthread_attr_getdetachstate(attr, &detachstate);
! 1236: }
! 1237: if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
! 1238: si -> flags = my_flags;
! 1239: UNLOCK();
! 1240: # ifdef DEBUG_THREADS
! 1241: GC_printf1("About to start new thread from thread 0x%X\n",
! 1242: pthread_self());
! 1243: # endif
! 1244:
! 1245: result = REAL_FUNC(pthread_create)(new_thread, attr, GC_start_routine, si);
! 1246:
! 1247: # ifdef DEBUG_THREADS
! 1248: GC_printf1("Started thread 0x%X\n", *new_thread);
! 1249: # endif
! 1250: /* Wait until child has been added to the thread table. */
! 1251: /* This also ensures that we hold onto si until the child is done */
! 1252: /* with it. Thus it doesn't matter whether it is otherwise */
! 1253: /* visible to the collector. */
! 1254: while (0 != sem_wait(&(si -> registered))) {
! 1255: if (EINTR != errno) ABORT("sem_wait failed");
! 1256: }
! 1257: sem_destroy(&(si -> registered));
! 1258: LOCK();
! 1259: GC_INTERNAL_FREE(si);
! 1260: UNLOCK();
! 1261:
! 1262: return(result);
! 1263: }
! 1264:
! 1265: #ifdef GENERIC_COMPARE_AND_SWAP
! 1266: pthread_mutex_t GC_compare_and_swap_lock = PTHREAD_MUTEX_INITIALIZER;
! 1267:
! 1268: GC_bool GC_compare_and_exchange(volatile GC_word *addr,
! 1269: GC_word old, GC_word new_val)
! 1270: {
! 1271: GC_bool result;
! 1272: pthread_mutex_lock(&GC_compare_and_swap_lock);
! 1273: if (*addr == old) {
! 1274: *addr = new_val;
! 1275: result = TRUE;
! 1276: } else {
! 1277: result = FALSE;
! 1278: }
! 1279: pthread_mutex_unlock(&GC_compare_and_swap_lock);
! 1280: return result;
! 1281: }
! 1282:
! 1283: GC_word GC_atomic_add(volatile GC_word *addr, GC_word how_much)
! 1284: {
! 1285: GC_word old;
! 1286: pthread_mutex_lock(&GC_compare_and_swap_lock);
! 1287: old = *addr;
! 1288: *addr = old + how_much;
! 1289: pthread_mutex_unlock(&GC_compare_and_swap_lock);
! 1290: return old;
! 1291: }
! 1292:
! 1293: #endif /* GENERIC_COMPARE_AND_SWAP */
! 1294: /* Spend a few cycles in a way that can't introduce contention with */
! 1295: /* othre threads. */
! 1296: void GC_pause()
! 1297: {
! 1298: int i;
! 1299: # ifndef __GNUC__
! 1300: volatile word dummy = 0;
! 1301: # endif
! 1302:
! 1303: for (i = 0; i < 10; ++i) {
! 1304: # ifdef __GNUC__
! 1305: __asm__ __volatile__ (" " : : : "memory");
! 1306: # else
! 1307: /* Something that's unlikely to be optimized away. */
! 1308: GC_noop(++dummy);
! 1309: # endif
! 1310: }
! 1311: }
! 1312:
! 1313: #define SPIN_MAX 1024 /* Maximum number of calls to GC_pause before */
! 1314: /* give up. */
! 1315:
! 1316: VOLATILE GC_bool GC_collecting = 0;
! 1317: /* A hint that we're in the collector and */
! 1318: /* holding the allocation lock for an */
! 1319: /* extended period. */
! 1320:
! 1321: #if !defined(USE_SPIN_LOCK) || defined(PARALLEL_MARK)
! 1322: /* If we don't want to use the below spinlock implementation, either */
! 1323: /* because we don't have a GC_test_and_set implementation, or because */
! 1324: /* we don't want to risk sleeping, we can still try spinning on */
! 1325: /* pthread_mutex_trylock for a while. This appears to be very */
! 1326: /* beneficial in many cases. */
! 1327: /* I suspect that under high contention this is nearly always better */
! 1328: /* than the spin lock. But it's a bit slower on a uniprocessor. */
! 1329: /* Hence we still default to the spin lock. */
! 1330: /* This is also used to acquire the mark lock for the parallel */
! 1331: /* marker. */
! 1332:
! 1333: /* Here we use a strict exponential backoff scheme. I don't know */
! 1334: /* whether that's better or worse than the above. We eventually */
! 1335: /* yield by calling pthread_mutex_lock(); it never makes sense to */
! 1336: /* explicitly sleep. */
! 1337:
! 1338: void GC_generic_lock(pthread_mutex_t * lock)
! 1339: {
! 1340: #ifndef NO_PTHREAD_TRYLOCK
! 1341: unsigned pause_length = 1;
! 1342: unsigned i;
! 1343:
! 1344: if (0 == pthread_mutex_trylock(lock)) return;
! 1345: for (; pause_length <= SPIN_MAX; pause_length <<= 1) {
! 1346: for (i = 0; i < pause_length; ++i) {
! 1347: GC_pause();
! 1348: }
! 1349: switch(pthread_mutex_trylock(lock)) {
! 1350: case 0:
! 1351: return;
! 1352: case EBUSY:
! 1353: break;
! 1354: default:
! 1355: ABORT("Unexpected error from pthread_mutex_trylock");
! 1356: }
! 1357: }
! 1358: #endif /* !NO_PTHREAD_TRYLOCK */
! 1359: pthread_mutex_lock(lock);
! 1360: }
! 1361:
! 1362: #endif /* !USE_SPIN_LOCK || PARALLEL_MARK */
! 1363:
! 1364: #if defined(USE_SPIN_LOCK)
! 1365:
! 1366: /* Reasonably fast spin locks. Basically the same implementation */
! 1367: /* as STL alloc.h. This isn't really the right way to do this. */
! 1368: /* but until the POSIX scheduling mess gets straightened out ... */
! 1369:
! 1370: volatile unsigned int GC_allocate_lock = 0;
! 1371:
! 1372:
! 1373: void GC_lock()
! 1374: {
! 1375: # define low_spin_max 30 /* spin cycles if we suspect uniprocessor */
! 1376: # define high_spin_max SPIN_MAX /* spin cycles for multiprocessor */
! 1377: static unsigned spin_max = low_spin_max;
! 1378: unsigned my_spin_max;
! 1379: static unsigned last_spins = 0;
! 1380: unsigned my_last_spins;
! 1381: int i;
! 1382:
! 1383: if (!GC_test_and_set(&GC_allocate_lock)) {
! 1384: return;
! 1385: }
! 1386: my_spin_max = spin_max;
! 1387: my_last_spins = last_spins;
! 1388: for (i = 0; i < my_spin_max; i++) {
! 1389: if (GC_collecting || GC_nprocs == 1) goto yield;
! 1390: if (i < my_last_spins/2 || GC_allocate_lock) {
! 1391: GC_pause();
! 1392: continue;
! 1393: }
! 1394: if (!GC_test_and_set(&GC_allocate_lock)) {
! 1395: /*
! 1396: * got it!
! 1397: * Spinning worked. Thus we're probably not being scheduled
! 1398: * against the other process with which we were contending.
! 1399: * Thus it makes sense to spin longer the next time.
! 1400: */
! 1401: last_spins = i;
! 1402: spin_max = high_spin_max;
! 1403: return;
! 1404: }
! 1405: }
! 1406: /* We are probably being scheduled against the other process. Sleep. */
! 1407: spin_max = low_spin_max;
! 1408: yield:
! 1409: for (i = 0;; ++i) {
! 1410: if (!GC_test_and_set(&GC_allocate_lock)) {
! 1411: return;
! 1412: }
! 1413: # define SLEEP_THRESHOLD 12
! 1414: /* Under Linux very short sleeps tend to wait until */
! 1415: /* the current time quantum expires. On old Linux */
! 1416: /* kernels nanosleep(<= 2ms) just spins under Linux. */
! 1417: /* (Under 2.4, this happens only for real-time */
! 1418: /* processes.) We want to minimize both behaviors */
! 1419: /* here. */
! 1420: if (i < SLEEP_THRESHOLD) {
! 1421: sched_yield();
! 1422: } else {
! 1423: struct timespec ts;
! 1424:
! 1425: if (i > 24) i = 24;
! 1426: /* Don't wait for more than about 15msecs, even */
! 1427: /* under extreme contention. */
! 1428: ts.tv_sec = 0;
! 1429: ts.tv_nsec = 1 << i;
! 1430: nanosleep(&ts, 0);
! 1431: }
! 1432: }
! 1433: }
! 1434:
! 1435: #else /* !USE_SPINLOCK */
! 1436: void GC_lock()
! 1437: {
! 1438: #ifndef NO_PTHREAD_TRYLOCK
! 1439: if (1 == GC_nprocs || GC_collecting) {
! 1440: pthread_mutex_lock(&GC_allocate_ml);
! 1441: } else {
! 1442: GC_generic_lock(&GC_allocate_ml);
! 1443: }
! 1444: #else /* !NO_PTHREAD_TRYLOCK */
! 1445: pthread_mutex_lock(&GC_allocate_ml);
! 1446: #endif /* !NO_PTHREAD_TRYLOCK */
! 1447: }
! 1448:
! 1449: #endif /* !USE_SPINLOCK */
! 1450:
! 1451: #if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
! 1452:
! 1453: #ifdef GC_ASSERTIONS
! 1454: pthread_t GC_mark_lock_holder = NO_THREAD;
! 1455: #endif
! 1456:
! 1457: #if 0
! 1458: /* Ugly workaround for a linux threads bug in the final versions */
! 1459: /* of glibc2.1. Pthread_mutex_trylock sets the mutex owner */
! 1460: /* field even when it fails to acquire the mutex. This causes */
! 1461: /* pthread_cond_wait to die. Remove for glibc2.2. */
! 1462: /* According to the man page, we should use */
! 1463: /* PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, but that isn't actually */
! 1464: /* defined. */
! 1465: static pthread_mutex_t mark_mutex =
! 1466: {0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, {0, 0}};
! 1467: #else
! 1468: static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER;
! 1469: #endif
! 1470:
! 1471: static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER;
! 1472:
! 1473: void GC_acquire_mark_lock()
! 1474: {
! 1475: /*
! 1476: if (pthread_mutex_lock(&mark_mutex) != 0) {
! 1477: ABORT("pthread_mutex_lock failed");
! 1478: }
! 1479: */
! 1480: GC_generic_lock(&mark_mutex);
! 1481: # ifdef GC_ASSERTIONS
! 1482: GC_mark_lock_holder = pthread_self();
! 1483: # endif
! 1484: }
! 1485:
! 1486: void GC_release_mark_lock()
! 1487: {
! 1488: GC_ASSERT(GC_mark_lock_holder == pthread_self());
! 1489: # ifdef GC_ASSERTIONS
! 1490: GC_mark_lock_holder = NO_THREAD;
! 1491: # endif
! 1492: if (pthread_mutex_unlock(&mark_mutex) != 0) {
! 1493: ABORT("pthread_mutex_unlock failed");
! 1494: }
! 1495: }
! 1496:
! 1497: /* Collector must wait for a freelist builders for 2 reasons: */
! 1498: /* 1) Mark bits may still be getting examined without lock. */
! 1499: /* 2) Partial free lists referenced only by locals may not be scanned */
! 1500: /* correctly, e.g. if they contain "pointer-free" objects, since the */
! 1501: /* free-list link may be ignored. */
! 1502: void GC_wait_builder()
! 1503: {
! 1504: GC_ASSERT(GC_mark_lock_holder == pthread_self());
! 1505: # ifdef GC_ASSERTIONS
! 1506: GC_mark_lock_holder = NO_THREAD;
! 1507: # endif
! 1508: if (pthread_cond_wait(&builder_cv, &mark_mutex) != 0) {
! 1509: ABORT("pthread_cond_wait failed");
! 1510: }
! 1511: GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
! 1512: # ifdef GC_ASSERTIONS
! 1513: GC_mark_lock_holder = pthread_self();
! 1514: # endif
! 1515: }
! 1516:
! 1517: void GC_wait_for_reclaim()
! 1518: {
! 1519: GC_acquire_mark_lock();
! 1520: while (GC_fl_builder_count > 0) {
! 1521: GC_wait_builder();
! 1522: }
! 1523: GC_release_mark_lock();
! 1524: }
! 1525:
! 1526: void GC_notify_all_builder()
! 1527: {
! 1528: GC_ASSERT(GC_mark_lock_holder == pthread_self());
! 1529: if (pthread_cond_broadcast(&builder_cv) != 0) {
! 1530: ABORT("pthread_cond_broadcast failed");
! 1531: }
! 1532: }
! 1533:
! 1534: #endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */
! 1535:
! 1536: #ifdef PARALLEL_MARK
! 1537:
! 1538: static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER;
! 1539:
! 1540: void GC_wait_marker()
! 1541: {
! 1542: GC_ASSERT(GC_mark_lock_holder == pthread_self());
! 1543: # ifdef GC_ASSERTIONS
! 1544: GC_mark_lock_holder = NO_THREAD;
! 1545: # endif
! 1546: if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0) {
! 1547: ABORT("pthread_cond_wait failed");
! 1548: }
! 1549: GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
! 1550: # ifdef GC_ASSERTIONS
! 1551: GC_mark_lock_holder = pthread_self();
! 1552: # endif
! 1553: }
! 1554:
! 1555: void GC_notify_all_marker()
! 1556: {
! 1557: if (pthread_cond_broadcast(&mark_cv) != 0) {
! 1558: ABORT("pthread_cond_broadcast failed");
! 1559: }
! 1560: }
! 1561:
! 1562: #endif /* PARALLEL_MARK */
! 1563:
! 1564: # endif /* GC_LINUX_THREADS and friends */
! 1565:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>