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