[BACK]Return to linux_threads.c CVS log [TXT][DIR] Up to [local] / OpenXM_contrib / gc

Annotation of OpenXM_contrib/gc/linux_threads.c, Revision 1.1

1.1     ! maekawa     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:  *
        !             6:  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
        !             7:  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
        !             8:  *
        !             9:  * Permission is hereby granted to use or copy this program
        !            10:  * for any purpose,  provided the above notices are retained on all copies.
        !            11:  * Permission to modify the code and to distribute modified code is granted,
        !            12:  * provided the above notices are retained, and a notice that the code was
        !            13:  * modified is included with the above copyright notice.
        !            14:  */
        !            15: /*
        !            16:  * Support code for LinuxThreads, the clone()-based kernel
        !            17:  * thread package for Linux which is included in libc6.
        !            18:  *
        !            19:  * This code relies on implementation details of LinuxThreads,
        !            20:  * (i.e. properties not guaranteed by the Pthread standard):
        !            21:  *
        !            22:  *     - the function GC_linux_thread_top_of_stack(void)
        !            23:  *       relies on the way LinuxThreads lays out thread stacks
        !            24:  *       in the address space.
        !            25:  *
        !            26:  * Note that there is a lot of code duplication between linux_threads.c
        !            27:  * and irix_threads.c; any changes made here may need to be reflected
        !            28:  * there too.
        !            29:  */
        !            30:
        !            31: /* #define DEBUG_THREADS 1 */
        !            32:
        !            33: /* ANSI C requires that a compilation unit contains something */
        !            34: # include "gc_priv.h"
        !            35:
        !            36: # if defined(LINUX_THREADS)
        !            37:
        !            38: # include <pthread.h>
        !            39: # include <time.h>
        !            40: # include <errno.h>
        !            41: # include <unistd.h>
        !            42: # include <sys/mman.h>
        !            43: # include <sys/time.h>
        !            44: # include <semaphore.h>
        !            45:
        !            46: #undef pthread_create
        !            47: #undef pthread_sigmask
        !            48: #undef pthread_join
        !            49:
        !            50: void GC_thr_init();
        !            51:
        !            52: #if 0
        !            53: void GC_print_sig_mask()
        !            54: {
        !            55:     sigset_t blocked;
        !            56:     int i;
        !            57:
        !            58:     if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
        !            59:        ABORT("pthread_sigmask");
        !            60:     GC_printf0("Blocked: ");
        !            61:     for (i = 1; i <= MAXSIG; i++) {
        !            62:         if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
        !            63:     }
        !            64:     GC_printf0("\n");
        !            65: }
        !            66: #endif
        !            67:
        !            68: /* We use the allocation lock to protect thread-related data structures. */
        !            69:
        !            70: /* The set of all known threads.  We intercept thread creation and     */
        !            71: /* joins.  We never actually create detached threads.  We allocate all         */
        !            72: /* new thread stacks ourselves.  These allow us to maintain this       */
        !            73: /* data structure.                                                     */
        !            74: /* Protected by GC_thr_lock.                                           */
        !            75: /* Some of this should be declared volatile, but that's incosnsistent  */
        !            76: /* with some library routine declarations.                             */
        !            77: typedef struct GC_Thread_Rep {
        !            78:     struct GC_Thread_Rep * next;  /* More recently allocated threads   */
        !            79:                                  /* with a given pthread id come       */
        !            80:                                  /* first.  (All but the first are     */
        !            81:                                  /* guaranteed to be dead, but we may  */
        !            82:                                  /* not yet have registered the join.) */
        !            83:     pthread_t id;
        !            84:     word flags;
        !            85: #      define FINISHED 1       /* Thread has exited.   */
        !            86: #      define DETACHED 2       /* Thread is intended to be detached.   */
        !            87: #      define MAIN_THREAD 4    /* True for the original thread only.   */
        !            88:
        !            89:     ptr_t stack_end;
        !            90:     ptr_t stack_ptr;           /* Valid only when stopped. */
        !            91:     int        signal;
        !            92:     void * status;             /* The value returned from the thread.  */
        !            93:                                /* Used only to avoid premature         */
        !            94:                                /* reclamation of any data it might     */
        !            95:                                /* reference.                           */
        !            96: } * GC_thread;
        !            97:
        !            98: GC_thread GC_lookup_thread(pthread_t id);
        !            99:
        !           100: /*
        !           101:  * The only way to suspend threads given the pthread interface is to send
        !           102:  * signals.  We can't use SIGSTOP directly, because we need to get the
        !           103:  * thread to save its stack pointer in the GC thread table before
        !           104:  * suspending.  So we have to reserve a signal of our own for this.
        !           105:  * This means we have to intercept client calls to change the signal mask.
        !           106:  * The linuxthreads package already uses SIGUSR1 and SIGUSR2,
        !           107:  * so we need to reuse something else.  I chose SIGPWR.
        !           108:  * (Perhaps SIGUNUSED would be a better choice.)
        !           109:  */
        !           110: #define SIG_SUSPEND SIGPWR
        !           111:
        !           112: #define SIG_RESTART SIGXCPU
        !           113:
        !           114: sem_t GC_suspend_ack_sem;
        !           115:
        !           116: /*
        !           117: GC_linux_thread_top_of_stack() relies on implementation details of
        !           118: LinuxThreads, namely that thread stacks are allocated on 2M boundaries
        !           119: and grow to no more than 2M.
        !           120: To make sure that we're using LinuxThreads and not some other thread
        !           121: package, we generate a dummy reference to `__pthread_initial_thread_bos',
        !           122: which is a symbol defined in LinuxThreads, but (hopefully) not in other
        !           123: thread packages.
        !           124: */
        !           125: extern char * __pthread_initial_thread_bos;
        !           126: char **dummy_var_to_force_linux_threads = &__pthread_initial_thread_bos;
        !           127:
        !           128: #define LINUX_THREADS_STACK_SIZE  (2 * 1024 * 1024)
        !           129:
        !           130: static inline ptr_t GC_linux_thread_top_of_stack(void)
        !           131: {
        !           132:   char *sp = GC_approx_sp();
        !           133:   ptr_t tos = (ptr_t) (((unsigned long)sp | (LINUX_THREADS_STACK_SIZE - 1)) + 1);
        !           134: #if DEBUG_THREADS
        !           135:   GC_printf1("SP = %lx\n", (unsigned long)sp);
        !           136:   GC_printf1("TOS = %lx\n", (unsigned long)tos);
        !           137: #endif
        !           138:   return tos;
        !           139: }
        !           140:
        !           141: void GC_suspend_handler(int sig)
        !           142: {
        !           143:     int dummy;
        !           144:     pthread_t my_thread = pthread_self();
        !           145:     GC_thread me;
        !           146:     sigset_t all_sigs;
        !           147:     sigset_t old_sigs;
        !           148:     int i;
        !           149:     sigset_t mask;
        !           150:
        !           151:     if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
        !           152:
        !           153: #if DEBUG_THREADS
        !           154:     GC_printf1("Suspending 0x%x\n", my_thread);
        !           155: #endif
        !           156:
        !           157:     me = GC_lookup_thread(my_thread);
        !           158:     /* The lookup here is safe, since I'm doing this on behalf  */
        !           159:     /* of a thread which holds the allocation lock in order    */
        !           160:     /* to stop the world.  Thus concurrent modification of the */
        !           161:     /* data structure is impossible.                           */
        !           162:     me -> stack_ptr = (ptr_t)(&dummy);
        !           163:     me -> stack_end = GC_linux_thread_top_of_stack();
        !           164:
        !           165:     /* Tell the thread that wants to stop the world that this   */
        !           166:     /* thread has been stopped.  Note that sem_post() is       */
        !           167:     /* the only async-signal-safe primitive in LinuxThreads.    */
        !           168:     sem_post(&GC_suspend_ack_sem);
        !           169:
        !           170:     /* Wait until that thread tells us to restart by sending    */
        !           171:     /* this thread a SIG_RESTART signal.                       */
        !           172:     /* SIG_RESTART should be masked at this point.  Thus there */
        !           173:     /* is no race.                                             */
        !           174:     if (sigfillset(&mask) != 0) ABORT("sigfillset() failed");
        !           175:     if (sigdelset(&mask, SIG_RESTART) != 0) ABORT("sigdelset() failed");
        !           176:     do {
        !           177:            me->signal = 0;
        !           178:            sigsuspend(&mask);             /* Wait for signal */
        !           179:     } while (me->signal != SIG_RESTART);
        !           180:
        !           181: #if DEBUG_THREADS
        !           182:     GC_printf1("Continuing 0x%x\n", my_thread);
        !           183: #endif
        !           184: }
        !           185:
        !           186: void GC_restart_handler(int sig)
        !           187: {
        !           188:     GC_thread me;
        !           189:
        !           190:     if (sig != SIG_RESTART) ABORT("Bad signal in suspend_handler");
        !           191:
        !           192:     /* Let the GC_suspend_handler() know that we got a SIG_RESTART. */
        !           193:     /* The lookup here is safe, since I'm doing this on behalf  */
        !           194:     /* of a thread which holds the allocation lock in order    */
        !           195:     /* to stop the world.  Thus concurrent modification of the */
        !           196:     /* data structure is impossible.                           */
        !           197:     me = GC_lookup_thread(pthread_self());
        !           198:     me->signal = SIG_RESTART;
        !           199:
        !           200:     /*
        !           201:     ** Note: even if we didn't do anything useful here,
        !           202:     ** it would still be necessary to have a signal handler,
        !           203:     ** rather than ignoring the signals, otherwise
        !           204:     ** the signals will not be delivered at all, and
        !           205:     ** will thus not interrupt the sigsuspend() above.
        !           206:     */
        !           207:
        !           208: #if DEBUG_THREADS
        !           209:     GC_printf1("In GC_restart_handler for 0x%x\n", pthread_self());
        !           210: #endif
        !           211: }
        !           212:
        !           213: GC_bool GC_thr_initialized = FALSE;
        !           214:
        !           215: # define THREAD_TABLE_SZ 128   /* Must be power of 2   */
        !           216: volatile GC_thread GC_threads[THREAD_TABLE_SZ];
        !           217:
        !           218: /* Add a thread to GC_threads.  We assume it wasn't already there.     */
        !           219: /* Caller holds allocation lock.                                       */
        !           220: GC_thread GC_new_thread(pthread_t id)
        !           221: {
        !           222:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           223:     GC_thread result;
        !           224:     static struct GC_Thread_Rep first_thread;
        !           225:     static GC_bool first_thread_used = FALSE;
        !           226:
        !           227:     if (!first_thread_used) {
        !           228:        result = &first_thread;
        !           229:        first_thread_used = TRUE;
        !           230:        /* Dont acquire allocation lock, since we may already hold it. */
        !           231:     } else {
        !           232:         result = (struct GC_Thread_Rep *)
        !           233:                 GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
        !           234:     }
        !           235:     if (result == 0) return(0);
        !           236:     result -> id = id;
        !           237:     result -> next = GC_threads[hv];
        !           238:     GC_threads[hv] = result;
        !           239:     /* result -> flags = 0; */
        !           240:     return(result);
        !           241: }
        !           242:
        !           243: /* Delete a thread from GC_threads.  We assume it is there.    */
        !           244: /* (The code intentionally traps if it wasn't.)                        */
        !           245: /* Caller holds allocation lock.                               */
        !           246: void GC_delete_thread(pthread_t id)
        !           247: {
        !           248:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           249:     register GC_thread p = GC_threads[hv];
        !           250:     register GC_thread prev = 0;
        !           251:
        !           252:     while (!pthread_equal(p -> id, id)) {
        !           253:         prev = p;
        !           254:         p = p -> next;
        !           255:     }
        !           256:     if (prev == 0) {
        !           257:         GC_threads[hv] = p -> next;
        !           258:     } else {
        !           259:         prev -> next = p -> next;
        !           260:     }
        !           261: }
        !           262:
        !           263: /* If a thread has been joined, but we have not yet            */
        !           264: /* been notified, then there may be more than one thread       */
        !           265: /* in the table with the same pthread id.                      */
        !           266: /* This is OK, but we need a way to delete a specific one.     */
        !           267: void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
        !           268: {
        !           269:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           270:     register GC_thread p = GC_threads[hv];
        !           271:     register GC_thread prev = 0;
        !           272:
        !           273:     while (p != gc_id) {
        !           274:         prev = p;
        !           275:         p = p -> next;
        !           276:     }
        !           277:     if (prev == 0) {
        !           278:         GC_threads[hv] = p -> next;
        !           279:     } else {
        !           280:         prev -> next = p -> next;
        !           281:     }
        !           282: }
        !           283:
        !           284: /* Return a GC_thread corresponding to a given thread_t.       */
        !           285: /* Returns 0 if it's not there.                                        */
        !           286: /* Caller holds  allocation lock or otherwise inhibits                 */
        !           287: /* updates.                                                    */
        !           288: /* If there is more than one thread with the given id we       */
        !           289: /* return the most recent one.                                 */
        !           290: GC_thread GC_lookup_thread(pthread_t id)
        !           291: {
        !           292:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           293:     register GC_thread p = GC_threads[hv];
        !           294:
        !           295:     while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
        !           296:     return(p);
        !           297: }
        !           298:
        !           299: /* Caller holds allocation lock.       */
        !           300: void GC_stop_world()
        !           301: {
        !           302:     pthread_t my_thread = pthread_self();
        !           303:     register int i;
        !           304:     register GC_thread p;
        !           305:     register int n_live_threads = 0;
        !           306:     register int result;
        !           307:
        !           308:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
        !           309:       for (p = GC_threads[i]; p != 0; p = p -> next) {
        !           310:         if (p -> id != my_thread) {
        !           311:             if (p -> flags & FINISHED) continue;
        !           312:             n_live_threads++;
        !           313:            #if DEBUG_THREADS
        !           314:              GC_printf1("Sending suspend signal to 0x%x\n", p -> id);
        !           315:            #endif
        !           316:             result = pthread_kill(p -> id, SIG_SUSPEND);
        !           317:            switch(result) {
        !           318:                 case ESRCH:
        !           319:                     /* Not really there anymore.  Possible? */
        !           320:                     n_live_threads--;
        !           321:                     break;
        !           322:                 case 0:
        !           323:                     break;
        !           324:                 default:
        !           325:                     ABORT("pthread_kill failed");
        !           326:             }
        !           327:         }
        !           328:       }
        !           329:     }
        !           330:     for (i = 0; i < n_live_threads; i++) {
        !           331:        sem_wait(&GC_suspend_ack_sem);
        !           332:     }
        !           333:     #if DEBUG_THREADS
        !           334:     GC_printf1("World stopped 0x%x\n", pthread_self());
        !           335:     #endif
        !           336: }
        !           337:
        !           338: /* Caller holds allocation lock.       */
        !           339: void GC_start_world()
        !           340: {
        !           341:     pthread_t my_thread = pthread_self();
        !           342:     register int i;
        !           343:     register GC_thread p;
        !           344:     register int n_live_threads = 0;
        !           345:     register int result;
        !           346:
        !           347: #   if DEBUG_THREADS
        !           348:       GC_printf0("World starting\n");
        !           349: #   endif
        !           350:
        !           351:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
        !           352:       for (p = GC_threads[i]; p != 0; p = p -> next) {
        !           353:         if (p -> id != my_thread) {
        !           354:             if (p -> flags & FINISHED) continue;
        !           355:             n_live_threads++;
        !           356:            #if DEBUG_THREADS
        !           357:              GC_printf1("Sending restart signal to 0x%x\n", p -> id);
        !           358:            #endif
        !           359:             result = pthread_kill(p -> id, SIG_RESTART);
        !           360:            switch(result) {
        !           361:                 case ESRCH:
        !           362:                     /* Not really there anymore.  Possible? */
        !           363:                     n_live_threads--;
        !           364:                     break;
        !           365:                 case 0:
        !           366:                     break;
        !           367:                 default:
        !           368:                     ABORT("pthread_kill failed");
        !           369:             }
        !           370:         }
        !           371:       }
        !           372:     }
        !           373:     #if DEBUG_THREADS
        !           374:       GC_printf0("World started\n");
        !           375:     #endif
        !           376: }
        !           377:
        !           378: /* We hold allocation lock.  We assume the world is stopped.   */
        !           379: void GC_push_all_stacks()
        !           380: {
        !           381:     register int i;
        !           382:     register GC_thread p;
        !           383:     register ptr_t sp = GC_approx_sp();
        !           384:     register ptr_t lo, hi;
        !           385:     pthread_t me = pthread_self();
        !           386:
        !           387:     if (!GC_thr_initialized) GC_thr_init();
        !           388:     #if DEBUG_THREADS
        !           389:         GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me);
        !           390:     #endif
        !           391:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
        !           392:       for (p = GC_threads[i]; p != 0; p = p -> next) {
        !           393:         if (p -> flags & FINISHED) continue;
        !           394:         if (pthread_equal(p -> id, me)) {
        !           395:            lo = GC_approx_sp();
        !           396:        } else {
        !           397:            lo = p -> stack_ptr;
        !           398:        }
        !           399:         if ((p -> flags & MAIN_THREAD) == 0) {
        !           400:            if (pthread_equal(p -> id, me)) {
        !           401:                hi = GC_linux_thread_top_of_stack();
        !           402:            } else {
        !           403:                hi = p -> stack_end;
        !           404:            }
        !           405:         } else {
        !           406:             /* The original stack. */
        !           407:             hi = GC_stackbottom;
        !           408:         }
        !           409:         #if DEBUG_THREADS
        !           410:             GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n",
        !           411:                (unsigned long) p -> id,
        !           412:                (unsigned long) lo, (unsigned long) hi);
        !           413:         #endif
        !           414:         GC_push_all_stack(lo, hi);
        !           415:       }
        !           416:     }
        !           417: }
        !           418:
        !           419:
        !           420: /* We hold the allocation lock.        */
        !           421: void GC_thr_init()
        !           422: {
        !           423:     GC_thread t;
        !           424:     struct sigaction act;
        !           425:
        !           426:     if (GC_thr_initialized) return;
        !           427:     GC_thr_initialized = TRUE;
        !           428:
        !           429:     if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
        !           430:        ABORT("sem_init failed");
        !           431:
        !           432:     act.sa_flags = SA_RESTART;
        !           433:     if (sigfillset(&act.sa_mask) != 0) {
        !           434:        ABORT("sigfillset() failed");
        !           435:     }
        !           436:     /* SIG_RESTART is unmasked by the handler when necessary.  */
        !           437:     act.sa_handler = GC_suspend_handler;
        !           438:     if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
        !           439:        ABORT("Cannot set SIG_SUSPEND handler");
        !           440:     }
        !           441:
        !           442:     act.sa_handler = GC_restart_handler;
        !           443:     if (sigaction(SIG_RESTART, &act, NULL) != 0) {
        !           444:        ABORT("Cannot set SIG_SUSPEND handler");
        !           445:     }
        !           446:
        !           447:     /* Add the initial thread, so we can stop it.      */
        !           448:       t = GC_new_thread(pthread_self());
        !           449:       t -> stack_ptr = 0;
        !           450:       t -> flags = DETACHED | MAIN_THREAD;
        !           451: }
        !           452:
        !           453: int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
        !           454: {
        !           455:     sigset_t fudged_set;
        !           456:
        !           457:     if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
        !           458:         fudged_set = *set;
        !           459:         sigdelset(&fudged_set, SIG_SUSPEND);
        !           460:         set = &fudged_set;
        !           461:     }
        !           462:     return(pthread_sigmask(how, set, oset));
        !           463: }
        !           464:
        !           465: struct start_info {
        !           466:     void *(*start_routine)(void *);
        !           467:     void *arg;
        !           468:     word flags;
        !           469:     sem_t registered;          /* 1 ==> in our thread table, but       */
        !           470:                                /* parent hasn't yet noticed.           */
        !           471: };
        !           472:
        !           473:
        !           474: void GC_thread_exit_proc(void *arg)
        !           475: {
        !           476:     GC_thread me;
        !           477:     struct start_info * si = arg;
        !           478:
        !           479:     LOCK();
        !           480:     me = GC_lookup_thread(pthread_self());
        !           481:     if (me -> flags & DETACHED) {
        !           482:        GC_delete_thread(pthread_self());
        !           483:     } else {
        !           484:        me -> flags |= FINISHED;
        !           485:     }
        !           486:     UNLOCK();
        !           487: }
        !           488:
        !           489: int GC_pthread_join(pthread_t thread, void **retval)
        !           490: {
        !           491:     int result;
        !           492:     GC_thread thread_gc_id;
        !           493:
        !           494:     LOCK();
        !           495:     thread_gc_id = GC_lookup_thread(thread);
        !           496:     /* This is guaranteed to be the intended one, since the thread id  */
        !           497:     /* cant have been recycled by pthreads.                            */
        !           498:     UNLOCK();
        !           499:     result = pthread_join(thread, retval);
        !           500:     LOCK();
        !           501:     /* Here the pthread thread id may have been recycled. */
        !           502:     GC_delete_gc_thread(thread, thread_gc_id);
        !           503:     UNLOCK();
        !           504:     return result;
        !           505: }
        !           506:
        !           507: void * GC_start_routine(void * arg)
        !           508: {
        !           509:     struct start_info * si = arg;
        !           510:     void * result;
        !           511:     GC_thread me;
        !           512:     pthread_t my_pthread;
        !           513:     void *(*start)(void *);
        !           514:     void *start_arg;
        !           515:
        !           516:     my_pthread = pthread_self();
        !           517:     LOCK();
        !           518:     me = GC_new_thread(my_pthread);
        !           519:     me -> flags = si -> flags;
        !           520:     me -> stack_ptr = 0;
        !           521:     me -> stack_end = 0;
        !           522:     UNLOCK();
        !           523:     start = si -> start_routine;
        !           524:     start_arg = si -> arg;
        !           525:     sem_post(&(si -> registered));
        !           526:     pthread_cleanup_push(GC_thread_exit_proc, si);
        !           527: #   ifdef DEBUG_THREADS
        !           528:         GC_printf1("Starting thread 0x%lx\n", pthread_self());
        !           529:         GC_printf1("pid = %ld\n", (long) getpid());
        !           530:         GC_printf1("sp = 0x%lx\n", (long) &arg);
        !           531:        GC_printf1("start_routine = 0x%lx\n", start);
        !           532: #   endif
        !           533:     result = (*start)(start_arg);
        !           534: #if DEBUG_THREADS
        !           535:         GC_printf1("Finishing thread 0x%x\n", pthread_self());
        !           536: #endif
        !           537:     me -> status = result;
        !           538:     me -> flags |= FINISHED;
        !           539:     pthread_cleanup_pop(1);
        !           540:     /* Cleanup acquires lock, ensuring that we can't exit              */
        !           541:     /* while a collection that thinks we're alive is trying to stop     */
        !           542:     /* us.                                                             */
        !           543:     return(result);
        !           544: }
        !           545:
        !           546: int
        !           547: GC_pthread_create(pthread_t *new_thread,
        !           548:                  const pthread_attr_t *attr,
        !           549:                   void *(*start_routine)(void *), void *arg)
        !           550: {
        !           551:     int result;
        !           552:     GC_thread t;
        !           553:     pthread_t my_new_thread;
        !           554:     void * stack;
        !           555:     size_t stacksize;
        !           556:     pthread_attr_t new_attr;
        !           557:     int detachstate;
        !           558:     word my_flags = 0;
        !           559:     struct start_info * si = GC_malloc(sizeof(struct start_info));
        !           560:        /* This is otherwise saved only in an area mmapped by the thread */
        !           561:        /* library, which isn't visible to the collector.                */
        !           562:
        !           563:     if (0 == si) return(ENOMEM);
        !           564:     sem_init(&(si -> registered), 0, 0);
        !           565:     si -> start_routine = start_routine;
        !           566:     si -> arg = arg;
        !           567:     LOCK();
        !           568:     if (!GC_thr_initialized) GC_thr_init();
        !           569:     if (NULL == attr) {
        !           570:         stack = 0;
        !           571:        (void) pthread_attr_init(&new_attr);
        !           572:     } else {
        !           573:         new_attr = *attr;
        !           574:     }
        !           575:     pthread_attr_getdetachstate(&new_attr, &detachstate);
        !           576:     if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
        !           577:     si -> flags = my_flags;
        !           578:     UNLOCK();
        !           579:     result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
        !           580:     /* Wait until child has been added to the thread table.            */
        !           581:     /* This also ensures that we hold onto si until the child is done  */
        !           582:     /* with it.  Thus it doesn't matter whether it is otherwise                */
        !           583:     /* visible to the collector.                                       */
        !           584:         if (0 != sem_wait(&(si -> registered))) ABORT("sem_wait failed");
        !           585:         sem_destroy(&(si -> registered));
        !           586:     /* pthread_attr_destroy(&new_attr); */
        !           587:     /* pthread_attr_destroy(&new_attr); */
        !           588:     return(result);
        !           589: }
        !           590:
        !           591: GC_bool GC_collecting = 0;
        !           592:                        /* A hint that we're in the collector and       */
        !           593:                         /* holding the allocation lock for an           */
        !           594:                         /* extended period.                             */
        !           595:
        !           596: /* Reasonably fast spin locks.  Basically the same implementation */
        !           597: /* as STL alloc.h.  This isn't really the right way to do this.   */
        !           598: /* but until the POSIX scheduling mess gets straightened out ...  */
        !           599:
        !           600: volatile unsigned int GC_allocate_lock = 0;
        !           601:
        !           602:
        !           603: void GC_lock()
        !           604: {
        !           605: #   define low_spin_max 30  /* spin cycles if we suspect uniprocessor */
        !           606: #   define high_spin_max 1000 /* spin cycles for multiprocessor */
        !           607:     static unsigned spin_max = low_spin_max;
        !           608:     unsigned my_spin_max;
        !           609:     static unsigned last_spins = 0;
        !           610:     unsigned my_last_spins;
        !           611:     volatile unsigned junk;
        !           612: #   define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
        !           613:     int i;
        !           614:
        !           615:     if (!GC_test_and_set(&GC_allocate_lock)) {
        !           616:         return;
        !           617:     }
        !           618:     junk = 0;
        !           619:     my_spin_max = spin_max;
        !           620:     my_last_spins = last_spins;
        !           621:     for (i = 0; i < my_spin_max; i++) {
        !           622:         if (GC_collecting) goto yield;
        !           623:         if (i < my_last_spins/2 || GC_allocate_lock) {
        !           624:             PAUSE;
        !           625:             continue;
        !           626:         }
        !           627:         if (!GC_test_and_set(&GC_allocate_lock)) {
        !           628:            /*
        !           629:              * got it!
        !           630:              * Spinning worked.  Thus we're probably not being scheduled
        !           631:              * against the other process with which we were contending.
        !           632:              * Thus it makes sense to spin longer the next time.
        !           633:             */
        !           634:             last_spins = i;
        !           635:             spin_max = high_spin_max;
        !           636:             return;
        !           637:         }
        !           638:     }
        !           639:     /* We are probably being scheduled against the other process.  Sleep. */
        !           640:     spin_max = low_spin_max;
        !           641: yield:
        !           642:     for (i = 0;; ++i) {
        !           643:         if (!GC_test_and_set(&GC_allocate_lock)) {
        !           644:             return;
        !           645:         }
        !           646: #       define SLEEP_THRESHOLD 12
        !           647:                /* nanosleep(<= 2ms) just spins under Linux.  We        */
        !           648:                /* want to be careful to avoid that behavior.           */
        !           649:         if (i < SLEEP_THRESHOLD) {
        !           650:             sched_yield();
        !           651:        } else {
        !           652:            struct timespec ts;
        !           653:
        !           654:            if (i > 26) i = 26;
        !           655:                        /* Don't wait for more than about 60msecs, even */
        !           656:                        /* under extreme contention.                    */
        !           657:            ts.tv_sec = 0;
        !           658:            ts.tv_nsec = 1 << i;
        !           659:            nanosleep(&ts, 0);
        !           660:        }
        !           661:     }
        !           662: }
        !           663:
        !           664: # endif /* LINUX_THREADS */
        !           665:

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>