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

Annotation of OpenXM_contrib2/asir2000/gc/linux_threads.c, Revision 1.1

1.1     ! noro        1: /*
        !             2:  * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
        !             3:  * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
        !             4:  * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
        !             5:  *
        !             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_kill_other_threads_np'
        !           122: (was `__pthread_initial_thread_bos' but that disappeared),
        !           123: which is a symbol defined in LinuxThreads, but (hopefully) not in other
        !           124: thread packages.
        !           125: */
        !           126: extern char * __pthread_initial_thread_bos;
        !           127: char **dummy_var_to_force_linux_threads = &__pthread_kill_other_threads_np;
        !           128:
        !           129: #define LINUX_THREADS_STACK_SIZE  (2 * 1024 * 1024)
        !           130:
        !           131: static inline ptr_t GC_linux_thread_top_of_stack(void)
        !           132: {
        !           133:   char *sp = GC_approx_sp();
        !           134:   ptr_t tos = (ptr_t) (((unsigned long)sp | (LINUX_THREADS_STACK_SIZE - 1)) + 1);
        !           135: #if DEBUG_THREADS
        !           136:   GC_printf1("SP = %lx\n", (unsigned long)sp);
        !           137:   GC_printf1("TOS = %lx\n", (unsigned long)tos);
        !           138: #endif
        !           139:   return tos;
        !           140: }
        !           141:
        !           142: void GC_suspend_handler(int sig)
        !           143: {
        !           144:     int dummy;
        !           145:     pthread_t my_thread = pthread_self();
        !           146:     GC_thread me;
        !           147:     sigset_t all_sigs;
        !           148:     sigset_t old_sigs;
        !           149:     int i;
        !           150:     sigset_t mask;
        !           151:
        !           152:     if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
        !           153:
        !           154: #if DEBUG_THREADS
        !           155:     GC_printf1("Suspending 0x%x\n", my_thread);
        !           156: #endif
        !           157:
        !           158:     me = GC_lookup_thread(my_thread);
        !           159:     /* The lookup here is safe, since I'm doing this on behalf  */
        !           160:     /* of a thread which holds the allocation lock in order    */
        !           161:     /* to stop the world.  Thus concurrent modification of the */
        !           162:     /* data structure is impossible.                           */
        !           163:     me -> stack_ptr = (ptr_t)(&dummy);
        !           164:     me -> stack_end = GC_linux_thread_top_of_stack();
        !           165:
        !           166:     /* Tell the thread that wants to stop the world that this   */
        !           167:     /* thread has been stopped.  Note that sem_post() is       */
        !           168:     /* the only async-signal-safe primitive in LinuxThreads.    */
        !           169:     sem_post(&GC_suspend_ack_sem);
        !           170:
        !           171:     /* Wait until that thread tells us to restart by sending    */
        !           172:     /* this thread a SIG_RESTART signal.                       */
        !           173:     /* SIG_RESTART should be masked at this point.  Thus there */
        !           174:     /* is no race.                                             */
        !           175:     if (sigfillset(&mask) != 0) ABORT("sigfillset() failed");
        !           176:     if (sigdelset(&mask, SIG_RESTART) != 0) ABORT("sigdelset() failed");
        !           177:     do {
        !           178:            me->signal = 0;
        !           179:            sigsuspend(&mask);             /* Wait for signal */
        !           180:     } while (me->signal != SIG_RESTART);
        !           181:
        !           182: #if DEBUG_THREADS
        !           183:     GC_printf1("Continuing 0x%x\n", my_thread);
        !           184: #endif
        !           185: }
        !           186:
        !           187: void GC_restart_handler(int sig)
        !           188: {
        !           189:     GC_thread me;
        !           190:
        !           191:     if (sig != SIG_RESTART) ABORT("Bad signal in suspend_handler");
        !           192:
        !           193:     /* Let the GC_suspend_handler() know that we got a SIG_RESTART. */
        !           194:     /* The lookup here is safe, since I'm doing this on behalf  */
        !           195:     /* of a thread which holds the allocation lock in order    */
        !           196:     /* to stop the world.  Thus concurrent modification of the */
        !           197:     /* data structure is impossible.                           */
        !           198:     me = GC_lookup_thread(pthread_self());
        !           199:     me->signal = SIG_RESTART;
        !           200:
        !           201:     /*
        !           202:     ** Note: even if we didn't do anything useful here,
        !           203:     ** it would still be necessary to have a signal handler,
        !           204:     ** rather than ignoring the signals, otherwise
        !           205:     ** the signals will not be delivered at all, and
        !           206:     ** will thus not interrupt the sigsuspend() above.
        !           207:     */
        !           208:
        !           209: #if DEBUG_THREADS
        !           210:     GC_printf1("In GC_restart_handler for 0x%x\n", pthread_self());
        !           211: #endif
        !           212: }
        !           213:
        !           214: GC_bool GC_thr_initialized = FALSE;
        !           215:
        !           216: # define THREAD_TABLE_SZ 128   /* Must be power of 2   */
        !           217: volatile GC_thread GC_threads[THREAD_TABLE_SZ];
        !           218:
        !           219: /* Add a thread to GC_threads.  We assume it wasn't already there.     */
        !           220: /* Caller holds allocation lock.                                       */
        !           221: GC_thread GC_new_thread(pthread_t id)
        !           222: {
        !           223:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           224:     GC_thread result;
        !           225:     static struct GC_Thread_Rep first_thread;
        !           226:     static GC_bool first_thread_used = FALSE;
        !           227:
        !           228:     if (!first_thread_used) {
        !           229:        result = &first_thread;
        !           230:        first_thread_used = TRUE;
        !           231:        /* Dont acquire allocation lock, since we may already hold it. */
        !           232:     } else {
        !           233:         result = (struct GC_Thread_Rep *)
        !           234:                 GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
        !           235:     }
        !           236:     if (result == 0) return(0);
        !           237:     result -> id = id;
        !           238:     result -> next = GC_threads[hv];
        !           239:     GC_threads[hv] = result;
        !           240:     /* result -> flags = 0; */
        !           241:     return(result);
        !           242: }
        !           243:
        !           244: /* Delete a thread from GC_threads.  We assume it is there.    */
        !           245: /* (The code intentionally traps if it wasn't.)                        */
        !           246: /* Caller holds allocation lock.                               */
        !           247: void GC_delete_thread(pthread_t id)
        !           248: {
        !           249:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           250:     register GC_thread p = GC_threads[hv];
        !           251:     register GC_thread prev = 0;
        !           252:
        !           253:     while (!pthread_equal(p -> id, id)) {
        !           254:         prev = p;
        !           255:         p = p -> next;
        !           256:     }
        !           257:     if (prev == 0) {
        !           258:         GC_threads[hv] = p -> next;
        !           259:     } else {
        !           260:         prev -> next = p -> next;
        !           261:     }
        !           262: }
        !           263:
        !           264: /* If a thread has been joined, but we have not yet            */
        !           265: /* been notified, then there may be more than one thread       */
        !           266: /* in the table with the same pthread id.                      */
        !           267: /* This is OK, but we need a way to delete a specific one.     */
        !           268: void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
        !           269: {
        !           270:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           271:     register GC_thread p = GC_threads[hv];
        !           272:     register GC_thread prev = 0;
        !           273:
        !           274:     while (p != gc_id) {
        !           275:         prev = p;
        !           276:         p = p -> next;
        !           277:     }
        !           278:     if (prev == 0) {
        !           279:         GC_threads[hv] = p -> next;
        !           280:     } else {
        !           281:         prev -> next = p -> next;
        !           282:     }
        !           283: }
        !           284:
        !           285: /* Return a GC_thread corresponding to a given thread_t.       */
        !           286: /* Returns 0 if it's not there.                                        */
        !           287: /* Caller holds  allocation lock or otherwise inhibits                 */
        !           288: /* updates.                                                    */
        !           289: /* If there is more than one thread with the given id we       */
        !           290: /* return the most recent one.                                 */
        !           291: GC_thread GC_lookup_thread(pthread_t id)
        !           292: {
        !           293:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           294:     register GC_thread p = GC_threads[hv];
        !           295:
        !           296:     while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
        !           297:     return(p);
        !           298: }
        !           299:
        !           300: /* Caller holds allocation lock.       */
        !           301: void GC_stop_world()
        !           302: {
        !           303:     pthread_t my_thread = pthread_self();
        !           304:     register int i;
        !           305:     register GC_thread p;
        !           306:     register int n_live_threads = 0;
        !           307:     register int result;
        !           308:
        !           309:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
        !           310:       for (p = GC_threads[i]; p != 0; p = p -> next) {
        !           311:         if (p -> id != my_thread) {
        !           312:             if (p -> flags & FINISHED) continue;
        !           313:             n_live_threads++;
        !           314:            #if DEBUG_THREADS
        !           315:              GC_printf1("Sending suspend signal to 0x%x\n", p -> id);
        !           316:            #endif
        !           317:             result = pthread_kill(p -> id, SIG_SUSPEND);
        !           318:            switch(result) {
        !           319:                 case ESRCH:
        !           320:                     /* Not really there anymore.  Possible? */
        !           321:                     n_live_threads--;
        !           322:                     break;
        !           323:                 case 0:
        !           324:                     break;
        !           325:                 default:
        !           326:                     ABORT("pthread_kill failed");
        !           327:             }
        !           328:         }
        !           329:       }
        !           330:     }
        !           331:     for (i = 0; i < n_live_threads; i++) {
        !           332:        sem_wait(&GC_suspend_ack_sem);
        !           333:     }
        !           334:     #if DEBUG_THREADS
        !           335:     GC_printf1("World stopped 0x%x\n", pthread_self());
        !           336:     #endif
        !           337: }
        !           338:
        !           339: /* Caller holds allocation lock.       */
        !           340: void GC_start_world()
        !           341: {
        !           342:     pthread_t my_thread = pthread_self();
        !           343:     register int i;
        !           344:     register GC_thread p;
        !           345:     register int n_live_threads = 0;
        !           346:     register int result;
        !           347:
        !           348: #   if DEBUG_THREADS
        !           349:       GC_printf0("World starting\n");
        !           350: #   endif
        !           351:
        !           352:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
        !           353:       for (p = GC_threads[i]; p != 0; p = p -> next) {
        !           354:         if (p -> id != my_thread) {
        !           355:             if (p -> flags & FINISHED) continue;
        !           356:             n_live_threads++;
        !           357:            #if DEBUG_THREADS
        !           358:              GC_printf1("Sending restart signal to 0x%x\n", p -> id);
        !           359:            #endif
        !           360:             result = pthread_kill(p -> id, SIG_RESTART);
        !           361:            switch(result) {
        !           362:                 case ESRCH:
        !           363:                     /* Not really there anymore.  Possible? */
        !           364:                     n_live_threads--;
        !           365:                     break;
        !           366:                 case 0:
        !           367:                     break;
        !           368:                 default:
        !           369:                     ABORT("pthread_kill failed");
        !           370:             }
        !           371:         }
        !           372:       }
        !           373:     }
        !           374:     #if DEBUG_THREADS
        !           375:       GC_printf0("World started\n");
        !           376:     #endif
        !           377: }
        !           378:
        !           379: /* We hold allocation lock.  We assume the world is stopped.   */
        !           380: void GC_push_all_stacks()
        !           381: {
        !           382:     register int i;
        !           383:     register GC_thread p;
        !           384:     register ptr_t sp = GC_approx_sp();
        !           385:     register ptr_t lo, hi;
        !           386:     pthread_t me = pthread_self();
        !           387:
        !           388:     if (!GC_thr_initialized) GC_thr_init();
        !           389:     #if DEBUG_THREADS
        !           390:         GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me);
        !           391:     #endif
        !           392:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
        !           393:       for (p = GC_threads[i]; p != 0; p = p -> next) {
        !           394:         if (p -> flags & FINISHED) continue;
        !           395:         if (pthread_equal(p -> id, me)) {
        !           396:            lo = GC_approx_sp();
        !           397:        } else {
        !           398:            lo = p -> stack_ptr;
        !           399:        }
        !           400:         if ((p -> flags & MAIN_THREAD) == 0) {
        !           401:            if (pthread_equal(p -> id, me)) {
        !           402:                hi = GC_linux_thread_top_of_stack();
        !           403:            } else {
        !           404:                hi = p -> stack_end;
        !           405:            }
        !           406:         } else {
        !           407:             /* The original stack. */
        !           408:             hi = GC_stackbottom;
        !           409:         }
        !           410:         #if DEBUG_THREADS
        !           411:             GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n",
        !           412:                (unsigned long) p -> id,
        !           413:                (unsigned long) lo, (unsigned long) hi);
        !           414:         #endif
        !           415:         GC_push_all_stack(lo, hi);
        !           416:       }
        !           417:     }
        !           418: }
        !           419:
        !           420:
        !           421: /* We hold the allocation lock.        */
        !           422: void GC_thr_init()
        !           423: {
        !           424:     GC_thread t;
        !           425:     struct sigaction act;
        !           426:
        !           427:     if (GC_thr_initialized) return;
        !           428:     GC_thr_initialized = TRUE;
        !           429:
        !           430:     if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
        !           431:        ABORT("sem_init failed");
        !           432:
        !           433:     act.sa_flags = SA_RESTART;
        !           434:     if (sigfillset(&act.sa_mask) != 0) {
        !           435:        ABORT("sigfillset() failed");
        !           436:     }
        !           437:     /* SIG_RESTART is unmasked by the handler when necessary.  */
        !           438:     act.sa_handler = GC_suspend_handler;
        !           439:     if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
        !           440:        ABORT("Cannot set SIG_SUSPEND handler");
        !           441:     }
        !           442:
        !           443:     act.sa_handler = GC_restart_handler;
        !           444:     if (sigaction(SIG_RESTART, &act, NULL) != 0) {
        !           445:        ABORT("Cannot set SIG_SUSPEND handler");
        !           446:     }
        !           447:
        !           448:     /* Add the initial thread, so we can stop it.      */
        !           449:       t = GC_new_thread(pthread_self());
        !           450:       t -> stack_ptr = 0;
        !           451:       t -> flags = DETACHED | MAIN_THREAD;
        !           452: }
        !           453:
        !           454: int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
        !           455: {
        !           456:     sigset_t fudged_set;
        !           457:
        !           458:     if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
        !           459:         fudged_set = *set;
        !           460:         sigdelset(&fudged_set, SIG_SUSPEND);
        !           461:         set = &fudged_set;
        !           462:     }
        !           463:     return(pthread_sigmask(how, set, oset));
        !           464: }
        !           465:
        !           466: struct start_info {
        !           467:     void *(*start_routine)(void *);
        !           468:     void *arg;
        !           469:     word flags;
        !           470:     sem_t registered;          /* 1 ==> in our thread table, but       */
        !           471:                                /* parent hasn't yet noticed.           */
        !           472: };
        !           473:
        !           474:
        !           475: void GC_thread_exit_proc(void *arg)
        !           476: {
        !           477:     GC_thread me;
        !           478:     struct start_info * si = arg;
        !           479:
        !           480:     LOCK();
        !           481:     me = GC_lookup_thread(pthread_self());
        !           482:     if (me -> flags & DETACHED) {
        !           483:        GC_delete_thread(pthread_self());
        !           484:     } else {
        !           485:        me -> flags |= FINISHED;
        !           486:     }
        !           487:     UNLOCK();
        !           488: }
        !           489:
        !           490: int GC_pthread_join(pthread_t thread, void **retval)
        !           491: {
        !           492:     int result;
        !           493:     GC_thread thread_gc_id;
        !           494:
        !           495:     LOCK();
        !           496:     thread_gc_id = GC_lookup_thread(thread);
        !           497:     /* This is guaranteed to be the intended one, since the thread id  */
        !           498:     /* cant have been recycled by pthreads.                            */
        !           499:     UNLOCK();
        !           500:     result = pthread_join(thread, retval);
        !           501:     LOCK();
        !           502:     /* Here the pthread thread id may have been recycled. */
        !           503:     GC_delete_gc_thread(thread, thread_gc_id);
        !           504:     UNLOCK();
        !           505:     return result;
        !           506: }
        !           507:
        !           508: void * GC_start_routine(void * arg)
        !           509: {
        !           510:     struct start_info * si = arg;
        !           511:     void * result;
        !           512:     GC_thread me;
        !           513:     pthread_t my_pthread;
        !           514:     void *(*start)(void *);
        !           515:     void *start_arg;
        !           516:
        !           517:     my_pthread = pthread_self();
        !           518:     LOCK();
        !           519:     me = GC_new_thread(my_pthread);
        !           520:     me -> flags = si -> flags;
        !           521:     me -> stack_ptr = 0;
        !           522:     me -> stack_end = 0;
        !           523:     UNLOCK();
        !           524:     start = si -> start_routine;
        !           525:     start_arg = si -> arg;
        !           526:     sem_post(&(si -> registered));
        !           527:     pthread_cleanup_push(GC_thread_exit_proc, si);
        !           528: #   ifdef DEBUG_THREADS
        !           529:         GC_printf1("Starting thread 0x%lx\n", pthread_self());
        !           530:         GC_printf1("pid = %ld\n", (long) getpid());
        !           531:         GC_printf1("sp = 0x%lx\n", (long) &arg);
        !           532:        GC_printf1("start_routine = 0x%lx\n", start);
        !           533: #   endif
        !           534:     result = (*start)(start_arg);
        !           535: #if DEBUG_THREADS
        !           536:         GC_printf1("Finishing thread 0x%x\n", pthread_self());
        !           537: #endif
        !           538:     me -> status = result;
        !           539:     me -> flags |= FINISHED;
        !           540:     pthread_cleanup_pop(1);
        !           541:     /* Cleanup acquires lock, ensuring that we can't exit              */
        !           542:     /* while a collection that thinks we're alive is trying to stop     */
        !           543:     /* us.                                                             */
        !           544:     return(result);
        !           545: }
        !           546:
        !           547: int
        !           548: GC_pthread_create(pthread_t *new_thread,
        !           549:                  const pthread_attr_t *attr,
        !           550:                   void *(*start_routine)(void *), void *arg)
        !           551: {
        !           552:     int result;
        !           553:     GC_thread t;
        !           554:     pthread_t my_new_thread;
        !           555:     void * stack;
        !           556:     size_t stacksize;
        !           557:     pthread_attr_t new_attr;
        !           558:     int detachstate;
        !           559:     word my_flags = 0;
        !           560:     struct start_info * si = GC_malloc(sizeof(struct start_info));
        !           561:        /* This is otherwise saved only in an area mmapped by the thread */
        !           562:        /* library, which isn't visible to the collector.                */
        !           563:
        !           564:     if (0 == si) return(ENOMEM);
        !           565:     sem_init(&(si -> registered), 0, 0);
        !           566:     si -> start_routine = start_routine;
        !           567:     si -> arg = arg;
        !           568:     LOCK();
        !           569:     if (!GC_thr_initialized) GC_thr_init();
        !           570:     if (NULL == attr) {
        !           571:         stack = 0;
        !           572:        (void) pthread_attr_init(&new_attr);
        !           573:     } else {
        !           574:         new_attr = *attr;
        !           575:     }
        !           576:     pthread_attr_getdetachstate(&new_attr, &detachstate);
        !           577:     if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
        !           578:     si -> flags = my_flags;
        !           579:     UNLOCK();
        !           580:     result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
        !           581:     /* Wait until child has been added to the thread table.            */
        !           582:     /* This also ensures that we hold onto si until the child is done  */
        !           583:     /* with it.  Thus it doesn't matter whether it is otherwise                */
        !           584:     /* visible to the collector.                                       */
        !           585:         if (0 != sem_wait(&(si -> registered))) ABORT("sem_wait failed");
        !           586:         sem_destroy(&(si -> registered));
        !           587:     /* pthread_attr_destroy(&new_attr); */
        !           588:     /* pthread_attr_destroy(&new_attr); */
        !           589:     return(result);
        !           590: }
        !           591:
        !           592: GC_bool GC_collecting = 0;
        !           593:                        /* A hint that we're in the collector and       */
        !           594:                         /* holding the allocation lock for an           */
        !           595:                         /* extended period.                             */
        !           596:
        !           597: /* Reasonably fast spin locks.  Basically the same implementation */
        !           598: /* as STL alloc.h.  This isn't really the right way to do this.   */
        !           599: /* but until the POSIX scheduling mess gets straightened out ...  */
        !           600:
        !           601: volatile unsigned int GC_allocate_lock = 0;
        !           602:
        !           603:
        !           604: void GC_lock()
        !           605: {
        !           606: #   define low_spin_max 30  /* spin cycles if we suspect uniprocessor */
        !           607: #   define high_spin_max 1000 /* spin cycles for multiprocessor */
        !           608:     static unsigned spin_max = low_spin_max;
        !           609:     unsigned my_spin_max;
        !           610:     static unsigned last_spins = 0;
        !           611:     unsigned my_last_spins;
        !           612:     volatile unsigned junk;
        !           613: #   define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
        !           614:     int i;
        !           615:
        !           616:     if (!GC_test_and_set(&GC_allocate_lock)) {
        !           617:         return;
        !           618:     }
        !           619:     junk = 0;
        !           620:     my_spin_max = spin_max;
        !           621:     my_last_spins = last_spins;
        !           622:     for (i = 0; i < my_spin_max; i++) {
        !           623:         if (GC_collecting) goto yield;
        !           624:         if (i < my_last_spins/2 || GC_allocate_lock) {
        !           625:             PAUSE;
        !           626:             continue;
        !           627:         }
        !           628:         if (!GC_test_and_set(&GC_allocate_lock)) {
        !           629:            /*
        !           630:              * got it!
        !           631:              * Spinning worked.  Thus we're probably not being scheduled
        !           632:              * against the other process with which we were contending.
        !           633:              * Thus it makes sense to spin longer the next time.
        !           634:             */
        !           635:             last_spins = i;
        !           636:             spin_max = high_spin_max;
        !           637:             return;
        !           638:         }
        !           639:     }
        !           640:     /* We are probably being scheduled against the other process.  Sleep. */
        !           641:     spin_max = low_spin_max;
        !           642: yield:
        !           643:     for (i = 0;; ++i) {
        !           644:         if (!GC_test_and_set(&GC_allocate_lock)) {
        !           645:             return;
        !           646:         }
        !           647: #       define SLEEP_THRESHOLD 12
        !           648:                /* nanosleep(<= 2ms) just spins under Linux.  We        */
        !           649:                /* want to be careful to avoid that behavior.           */
        !           650:         if (i < SLEEP_THRESHOLD) {
        !           651:             sched_yield();
        !           652:        } else {
        !           653:            struct timespec ts;
        !           654:
        !           655:            if (i > 26) i = 26;
        !           656:                        /* Don't wait for more than about 60msecs, even */
        !           657:                        /* under extreme contention.                    */
        !           658:            ts.tv_sec = 0;
        !           659:            ts.tv_nsec = 1 << i;
        !           660:            nanosleep(&ts, 0);
        !           661:        }
        !           662:     }
        !           663: }
        !           664:
        !           665: # endif /* LINUX_THREADS */
        !           666:

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