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

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

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