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

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

1.1     ! maekawa     1: /*
        !             2:  * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
        !             3:  * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
        !             4:  * Copyright (c) 1999 by Hewlett-Packard Company. 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 Irix (>=6.2) Pthreads.  This relies on properties
        !            17:  * not guaranteed by the Pthread standard.  It may or may not be portable
        !            18:  * to other implementations.
        !            19:  *
        !            20:  * This now also includes an initial attempt at thread support for
        !            21:  * HP/UX 11.
        !            22:  *
        !            23:  * Note that there is a lot of code duplication between linux_threads.c
        !            24:  * and hpux_irix_threads.c; any changes made here may need to be reflected
        !            25:  * there too.
        !            26:  */
        !            27:
        !            28: # if defined(IRIX_THREADS) || defined(HPUX_THREADS)
        !            29:
        !            30: # if defined(HPUX_THREADS)
        !            31: #   include <sys/semaphore.h>
        !            32: # endif
        !            33:
        !            34: # include "gc_priv.h"
        !            35: # include <pthread.h>
        !            36: # include <semaphore.h>
        !            37: # include <time.h>
        !            38: # include <errno.h>
        !            39: # include <unistd.h>
        !            40: # include <sys/mman.h>
        !            41: # include <sys/time.h>
        !            42:
        !            43: #undef pthread_create
        !            44: #undef pthread_sigmask
        !            45: #undef pthread_join
        !            46:
        !            47: void GC_thr_init();
        !            48:
        !            49: #if 0
        !            50: void GC_print_sig_mask()
        !            51: {
        !            52:     sigset_t blocked;
        !            53:     int i;
        !            54:
        !            55:     if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
        !            56:        ABORT("pthread_sigmask");
        !            57:     GC_printf0("Blocked: ");
        !            58:     for (i = 1; i <= MAXSIG; i++) {
        !            59:         if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
        !            60:     }
        !            61:     GC_printf0("\n");
        !            62: }
        !            63: #endif
        !            64:
        !            65: /* We use the allocation lock to protect thread-related data structures. */
        !            66:
        !            67: /* The set of all known threads.  We intercept thread creation and     */
        !            68: /* joins.  We never actually create detached threads.  We allocate all         */
        !            69: /* new thread stacks ourselves.  These allow us to maintain this       */
        !            70: /* data structure.                                                     */
        !            71: /* Protected by GC_thr_lock.                                           */
        !            72: /* Some of this should be declared volatile, but that's incosnsistent  */
        !            73: /* with some library routine declarations.                             */
        !            74: typedef struct GC_Thread_Rep {
        !            75:     struct GC_Thread_Rep * next;  /* More recently allocated threads   */
        !            76:                                  /* with a given pthread id come       */
        !            77:                                  /* first.  (All but the first are     */
        !            78:                                  /* guaranteed to be dead, but we may  */
        !            79:                                  /* not yet have registered the join.) */
        !            80:     pthread_t id;
        !            81:     word stop;
        !            82: #      define NOT_STOPPED 0
        !            83: #      define PLEASE_STOP 1
        !            84: #      define STOPPED 2
        !            85:     word flags;
        !            86: #      define FINISHED 1       /* Thread has exited.   */
        !            87: #      define DETACHED 2       /* Thread is intended to be detached.   */
        !            88: #      define CLIENT_OWNS_STACK        4
        !            89:                                /* Stack was supplied by client.        */
        !            90:     ptr_t stack;
        !            91:     ptr_t stack_ptr;           /* Valid only when stopped. */
        !            92:                                /* But must be within stack region at   */
        !            93:                                /* all times.                           */
        !            94:     size_t stack_size;         /* 0 for original thread.       */
        !            95:     void * status;             /* Used only to avoid premature         */
        !            96:                                /* reclamation of any data it might     */
        !            97:                                /* reference.                           */
        !            98: } * GC_thread;
        !            99:
        !           100: GC_thread GC_lookup_thread(pthread_t id);
        !           101:
        !           102: /*
        !           103:  * The only way to suspend threads given the pthread interface is to send
        !           104:  * signals.  Unfortunately, this means we have to reserve
        !           105:  * a signal, and intercept client calls to change the signal mask.
        !           106:  */
        !           107: # define SIG_SUSPEND (SIGRTMIN + 6)
        !           108:
        !           109: pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
        !           110:                                /* Number of threads stopped so far     */
        !           111: pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER;
        !           112: pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER;
        !           113:
        !           114: void GC_suspend_handler(int sig)
        !           115: {
        !           116:     int dummy;
        !           117:     GC_thread me;
        !           118:     sigset_t all_sigs;
        !           119:     sigset_t old_sigs;
        !           120:     int i;
        !           121:
        !           122:     if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
        !           123:     me = GC_lookup_thread(pthread_self());
        !           124:     /* The lookup here is safe, since I'm doing this on behalf  */
        !           125:     /* of a thread which holds the allocation lock in order    */
        !           126:     /* to stop the world.  Thus concurrent modification of the */
        !           127:     /* data structure is impossible.                           */
        !           128:     if (PLEASE_STOP != me -> stop) {
        !           129:        /* Misdirected signal.  */
        !           130:        pthread_mutex_unlock(&GC_suspend_lock);
        !           131:        return;
        !           132:     }
        !           133:     pthread_mutex_lock(&GC_suspend_lock);
        !           134:     me -> stack_ptr = (ptr_t)(&dummy);
        !           135:     me -> stop = STOPPED;
        !           136:     pthread_cond_signal(&GC_suspend_ack_cv);
        !           137:     pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock);
        !           138:     pthread_mutex_unlock(&GC_suspend_lock);
        !           139:     /* GC_printf1("Continuing 0x%x\n", pthread_self()); */
        !           140: }
        !           141:
        !           142:
        !           143: GC_bool GC_thr_initialized = FALSE;
        !           144:
        !           145: size_t GC_min_stack_sz;
        !           146:
        !           147: size_t GC_page_sz;
        !           148:
        !           149: # define N_FREE_LISTS 25
        !           150: ptr_t GC_stack_free_lists[N_FREE_LISTS] = { 0 };
        !           151:                /* GC_stack_free_lists[i] is free list for stacks of    */
        !           152:                /* size GC_min_stack_sz*2**i.                           */
        !           153:                /* Free lists are linked through first word.            */
        !           154:
        !           155: /* Return a stack of size at least *stack_size.  *stack_size is        */
        !           156: /* replaced by the actual stack size.                          */
        !           157: /* Caller holds allocation lock.                               */
        !           158: ptr_t GC_stack_alloc(size_t * stack_size)
        !           159: {
        !           160:     register size_t requested_sz = *stack_size;
        !           161:     register size_t search_sz = GC_min_stack_sz;
        !           162:     register int index = 0;    /* = log2(search_sz/GC_min_stack_sz) */
        !           163:     register ptr_t result;
        !           164:
        !           165:     while (search_sz < requested_sz) {
        !           166:         search_sz *= 2;
        !           167:         index++;
        !           168:     }
        !           169:     if ((result = GC_stack_free_lists[index]) == 0
        !           170:         && (result = GC_stack_free_lists[index+1]) != 0) {
        !           171:         /* Try next size up. */
        !           172:         search_sz *= 2; index++;
        !           173:     }
        !           174:     if (result != 0) {
        !           175:         GC_stack_free_lists[index] = *(ptr_t *)result;
        !           176:     } else {
        !           177:         result = (ptr_t) GC_scratch_alloc(search_sz + 2*GC_page_sz);
        !           178:         result = (ptr_t)(((word)result + GC_page_sz) & ~(GC_page_sz - 1));
        !           179:         /* Protect hottest page to detect overflow. */
        !           180: #      ifdef STACK_GROWS_UP
        !           181:           /* mprotect(result + search_sz, GC_page_sz, PROT_NONE); */
        !           182: #      else
        !           183:           /* mprotect(result, GC_page_sz, PROT_NONE); */
        !           184:           result += GC_page_sz;
        !           185: #      endif
        !           186:     }
        !           187:     *stack_size = search_sz;
        !           188:     return(result);
        !           189: }
        !           190:
        !           191: /* Caller holds allocation lock.                                       */
        !           192: void GC_stack_free(ptr_t stack, size_t size)
        !           193: {
        !           194:     register int index = 0;
        !           195:     register size_t search_sz = GC_min_stack_sz;
        !           196:
        !           197:     while (search_sz < size) {
        !           198:         search_sz *= 2;
        !           199:         index++;
        !           200:     }
        !           201:     if (search_sz != size) ABORT("Bad stack size");
        !           202:     *(ptr_t *)stack = GC_stack_free_lists[index];
        !           203:     GC_stack_free_lists[index] = stack;
        !           204: }
        !           205:
        !           206:
        !           207:
        !           208: # define THREAD_TABLE_SZ 128   /* Must be power of 2   */
        !           209: volatile GC_thread GC_threads[THREAD_TABLE_SZ];
        !           210:
        !           211: /* Add a thread to GC_threads.  We assume it wasn't already there.     */
        !           212: /* Caller holds allocation lock.                                       */
        !           213: GC_thread GC_new_thread(pthread_t id)
        !           214: {
        !           215:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           216:     GC_thread result;
        !           217:     static struct GC_Thread_Rep first_thread;
        !           218:     static GC_bool first_thread_used = FALSE;
        !           219:
        !           220:     if (!first_thread_used) {
        !           221:        result = &first_thread;
        !           222:        first_thread_used = TRUE;
        !           223:        /* Dont acquire allocation lock, since we may already hold it. */
        !           224:     } else {
        !           225:         result = (struct GC_Thread_Rep *)
        !           226:                 GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
        !           227:     }
        !           228:     if (result == 0) return(0);
        !           229:     result -> id = id;
        !           230:     result -> next = GC_threads[hv];
        !           231:     GC_threads[hv] = result;
        !           232:     /* result -> flags = 0;     */
        !           233:     /* result -> stop = 0;     */
        !           234:     return(result);
        !           235: }
        !           236:
        !           237: /* Delete a thread from GC_threads.  We assume it is there.    */
        !           238: /* (The code intentionally traps if it wasn't.)                        */
        !           239: /* Caller holds allocation lock.                               */
        !           240: void GC_delete_thread(pthread_t id)
        !           241: {
        !           242:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           243:     register GC_thread p = GC_threads[hv];
        !           244:     register GC_thread prev = 0;
        !           245:
        !           246:     while (!pthread_equal(p -> id, id)) {
        !           247:         prev = p;
        !           248:         p = p -> next;
        !           249:     }
        !           250:     if (prev == 0) {
        !           251:         GC_threads[hv] = p -> next;
        !           252:     } else {
        !           253:         prev -> next = p -> next;
        !           254:     }
        !           255: }
        !           256:
        !           257: /* If a thread has been joined, but we have not yet            */
        !           258: /* been notified, then there may be more than one thread       */
        !           259: /* in the table with the same pthread id.                      */
        !           260: /* This is OK, but we need a way to delete a specific one.     */
        !           261: void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
        !           262: {
        !           263:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           264:     register GC_thread p = GC_threads[hv];
        !           265:     register GC_thread prev = 0;
        !           266:
        !           267:     while (p != gc_id) {
        !           268:         prev = p;
        !           269:         p = p -> next;
        !           270:     }
        !           271:     if (prev == 0) {
        !           272:         GC_threads[hv] = p -> next;
        !           273:     } else {
        !           274:         prev -> next = p -> next;
        !           275:     }
        !           276: }
        !           277:
        !           278: /* Return a GC_thread corresponding to a given thread_t.       */
        !           279: /* Returns 0 if it's not there.                                        */
        !           280: /* Caller holds  allocation lock or otherwise inhibits                 */
        !           281: /* updates.                                                    */
        !           282: /* If there is more than one thread with the given id we       */
        !           283: /* return the most recent one.                                 */
        !           284: GC_thread GC_lookup_thread(pthread_t id)
        !           285: {
        !           286:     int hv = ((word)id) % THREAD_TABLE_SZ;
        !           287:     register GC_thread p = GC_threads[hv];
        !           288:
        !           289:     while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
        !           290:     return(p);
        !           291: }
        !           292:
        !           293:
        !           294: /* Caller holds allocation lock.       */
        !           295: void GC_stop_world()
        !           296: {
        !           297:     pthread_t my_thread = pthread_self();
        !           298:     register int i;
        !           299:     register GC_thread p;
        !           300:     register int result;
        !           301:     struct timespec timeout;
        !           302:
        !           303:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
        !           304:       for (p = GC_threads[i]; p != 0; p = p -> next) {
        !           305:         if (p -> id != my_thread) {
        !           306:             if (p -> flags & FINISHED) {
        !           307:                p -> stop = STOPPED;
        !           308:                continue;
        !           309:            }
        !           310:            p -> stop = PLEASE_STOP;
        !           311:             result = pthread_kill(p -> id, SIG_SUSPEND);
        !           312:            /* GC_printf1("Sent signal to 0x%x\n", p -> id); */
        !           313:            switch(result) {
        !           314:                 case ESRCH:
        !           315:                     /* Not really there anymore.  Possible? */
        !           316:                     p -> stop = STOPPED;
        !           317:                     break;
        !           318:                 case 0:
        !           319:                     break;
        !           320:                 default:
        !           321:                     ABORT("pthread_kill failed");
        !           322:             }
        !           323:         }
        !           324:       }
        !           325:     }
        !           326:     pthread_mutex_lock(&GC_suspend_lock);
        !           327:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
        !           328:       for (p = GC_threads[i]; p != 0; p = p -> next) {
        !           329:         while (p -> id != my_thread && p -> stop != STOPPED) {
        !           330:            clock_gettime(CLOCK_REALTIME, &timeout);
        !           331:             timeout.tv_nsec += 50000000; /* 50 msecs */
        !           332:             if (timeout.tv_nsec >= 1000000000) {
        !           333:                 timeout.tv_nsec -= 1000000000;
        !           334:                 ++timeout.tv_sec;
        !           335:             }
        !           336:             result = pthread_cond_timedwait(&GC_suspend_ack_cv,
        !           337:                                            &GC_suspend_lock,
        !           338:                                             &timeout);
        !           339:             if (result == ETIMEDOUT) {
        !           340:                 /* Signal was lost or misdirected.  Try again.      */
        !           341:                 /* Duplicate signals should be benign.              */
        !           342:                 result = pthread_kill(p -> id, SIG_SUSPEND);
        !           343:            }
        !           344:        }
        !           345:       }
        !           346:     }
        !           347:     pthread_mutex_unlock(&GC_suspend_lock);
        !           348:     /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
        !           349: }
        !           350:
        !           351: /* Caller holds allocation lock.       */
        !           352: void GC_start_world()
        !           353: {
        !           354:     GC_thread p;
        !           355:     unsigned i;
        !           356:
        !           357:     /* GC_printf0("World starting\n"); */
        !           358:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
        !           359:       for (p = GC_threads[i]; p != 0; p = p -> next) {
        !           360:        p -> stop = NOT_STOPPED;
        !           361:       }
        !           362:     }
        !           363:     pthread_mutex_lock(&GC_suspend_lock);
        !           364:     /* All other threads are at pthread_cond_wait in signal handler.   */
        !           365:     /* Otherwise we couldn't have acquired the lock.                   */
        !           366:     pthread_mutex_unlock(&GC_suspend_lock);
        !           367:     pthread_cond_broadcast(&GC_continue_cv);
        !           368: }
        !           369:
        !           370: # ifdef MMAP_STACKS
        !           371: --> not really supported yet.
        !           372: int GC_is_thread_stack(ptr_t addr)
        !           373: {
        !           374:     register int i;
        !           375:     register GC_thread p;
        !           376:
        !           377:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
        !           378:       for (p = GC_threads[i]; p != 0; p = p -> next) {
        !           379:         if (p -> stack_size != 0) {
        !           380:             if (p -> stack <= addr &&
        !           381:                 addr < p -> stack + p -> stack_size)
        !           382:                    return 1;
        !           383:        }
        !           384:       }
        !           385:     }
        !           386:     return 0;
        !           387: }
        !           388: # endif
        !           389:
        !           390: /* We hold allocation lock.  We assume the world is stopped.   */
        !           391: void GC_push_all_stacks()
        !           392: {
        !           393:     register int i;
        !           394:     register GC_thread p;
        !           395:     register ptr_t sp = GC_approx_sp();
        !           396:     register ptr_t hot, cold;
        !           397:     pthread_t me = pthread_self();
        !           398:
        !           399:     if (!GC_thr_initialized) GC_thr_init();
        !           400:     /* GC_printf1("Pushing stacks from thread 0x%x\n", me); */
        !           401:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
        !           402:       for (p = GC_threads[i]; p != 0; p = p -> next) {
        !           403:         if (p -> flags & FINISHED) continue;
        !           404:         if (pthread_equal(p -> id, me)) {
        !           405:            hot = GC_approx_sp();
        !           406:        } else {
        !           407:            hot = p -> stack_ptr;
        !           408:        }
        !           409:         if (p -> stack_size != 0) {
        !           410: #        ifdef STACK_GROWS_UP
        !           411:            cold = p -> stack;
        !           412: #        else
        !           413:             cold = p -> stack + p -> stack_size;
        !           414: #        endif
        !           415:         } else {
        !           416:             /* The original stack. */
        !           417:             cold = GC_stackbottom;
        !           418:         }
        !           419: #      ifdef STACK_GROWS_UP
        !           420:           GC_push_all_stack(cold, hot);
        !           421: #      else
        !           422:           GC_push_all_stack(hot, cold);
        !           423: #      endif
        !           424:       }
        !           425:     }
        !           426: }
        !           427:
        !           428:
        !           429: /* We hold the allocation lock.        */
        !           430: void GC_thr_init()
        !           431: {
        !           432:     GC_thread t;
        !           433:     struct sigaction act;
        !           434:
        !           435:     if (GC_thr_initialized) return;
        !           436:     GC_thr_initialized = TRUE;
        !           437:     GC_min_stack_sz = HBLKSIZE;
        !           438:     GC_page_sz = sysconf(_SC_PAGESIZE);
        !           439:     (void) sigaction(SIG_SUSPEND, 0, &act);
        !           440:     if (act.sa_handler != SIG_DFL)
        !           441:        ABORT("Previously installed SIG_SUSPEND handler");
        !           442:     /* Install handler.        */
        !           443:        act.sa_handler = GC_suspend_handler;
        !           444:        act.sa_flags = SA_RESTART;
        !           445:        (void) sigemptyset(&act.sa_mask);
        !           446:         if (0 != sigaction(SIG_SUSPEND, &act, 0))
        !           447:            ABORT("Failed to install SIG_SUSPEND handler");
        !           448:     /* Add the initial thread, so we can stop it.      */
        !           449:       t = GC_new_thread(pthread_self());
        !           450:       t -> stack_size = 0;
        !           451:       t -> stack_ptr = (ptr_t)(&t);
        !           452:       t -> flags = DETACHED;
        !           453: }
        !           454:
        !           455: int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
        !           456: {
        !           457:     sigset_t fudged_set;
        !           458:
        !           459:     if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
        !           460:         fudged_set = *set;
        !           461:         sigdelset(&fudged_set, SIG_SUSPEND);
        !           462:         set = &fudged_set;
        !           463:     }
        !           464:     return(pthread_sigmask(how, set, oset));
        !           465: }
        !           466:
        !           467: struct start_info {
        !           468:     void *(*start_routine)(void *);
        !           469:     void *arg;
        !           470:     word flags;
        !           471:     ptr_t stack;
        !           472:     size_t stack_size;
        !           473:     sem_t registered;          /* 1 ==> in our thread table, but       */
        !           474:                                /* parent hasn't yet noticed.           */
        !           475: };
        !           476:
        !           477: void GC_thread_exit_proc(void *arg)
        !           478: {
        !           479:     GC_thread me;
        !           480:
        !           481:     LOCK();
        !           482:     me = GC_lookup_thread(pthread_self());
        !           483:     if (me -> flags & DETACHED) {
        !           484:        GC_delete_thread(pthread_self());
        !           485:     } else {
        !           486:        me -> flags |= FINISHED;
        !           487:     }
        !           488:     UNLOCK();
        !           489: }
        !           490:
        !           491: int GC_pthread_join(pthread_t thread, void **retval)
        !           492: {
        !           493:     int result;
        !           494:     GC_thread thread_gc_id;
        !           495:
        !           496:     LOCK();
        !           497:     thread_gc_id = GC_lookup_thread(thread);
        !           498:     /* This is guaranteed to be the intended one, since the thread id  */
        !           499:     /* cant have been recycled by pthreads.                            */
        !           500:     UNLOCK();
        !           501:     result = pthread_join(thread, retval);
        !           502:     /* Some versions of the Irix pthreads library can erroneously      */
        !           503:     /* return EINTR when the call succeeds.                            */
        !           504:        if (EINTR == result) result = 0;
        !           505:     LOCK();
        !           506:     /* Here the pthread thread id may have been recycled. */
        !           507:     GC_delete_gc_thread(thread, thread_gc_id);
        !           508:     UNLOCK();
        !           509:     return result;
        !           510: }
        !           511:
        !           512: void * GC_start_routine(void * arg)
        !           513: {
        !           514:     struct start_info * si = arg;
        !           515:     void * result;
        !           516:     GC_thread me;
        !           517:     pthread_t my_pthread;
        !           518:     void *(*start)(void *);
        !           519:     void *start_arg;
        !           520:
        !           521:     my_pthread = pthread_self();
        !           522:     /* If a GC occurs before the thread is registered, that GC will    */
        !           523:     /* ignore this thread.  That's fine, since it will block trying to  */
        !           524:     /* acquire the allocation lock, and won't yet hold interesting     */
        !           525:     /* pointers.                                                       */
        !           526:     LOCK();
        !           527:     /* We register the thread here instead of in the parent, so that   */
        !           528:     /* we don't need to hold the allocation lock during pthread_create. */
        !           529:     /* Holding the allocation lock there would make REDIRECT_MALLOC    */
        !           530:     /* impossible.  It probably still doesn't work, but we're a little  */
        !           531:     /* closer ...                                                      */
        !           532:     /* This unfortunately means that we have to be careful the parent  */
        !           533:     /* doesn't try to do a pthread_join before we're registered.       */
        !           534:     me = GC_new_thread(my_pthread);
        !           535:     me -> flags = si -> flags;
        !           536:     me -> stack = si -> stack;
        !           537:     me -> stack_size = si -> stack_size;
        !           538:     me -> stack_ptr = (ptr_t)si -> stack + si -> stack_size - sizeof(word);
        !           539:     UNLOCK();
        !           540:     start = si -> start_routine;
        !           541:     start_arg = si -> arg;
        !           542:     sem_post(&(si -> registered));
        !           543:     pthread_cleanup_push(GC_thread_exit_proc, 0);
        !           544:     result = (*start)(start_arg);
        !           545:     me -> status = result;
        !           546:     me -> flags |= FINISHED;
        !           547:     pthread_cleanup_pop(1);
        !           548:        /* This involves acquiring the lock, ensuring that we can't exit */
        !           549:        /* while a collection that thinks we're alive is trying to stop  */
        !           550:        /* us.                                                           */
        !           551:     return(result);
        !           552: }
        !           553:
        !           554: # ifdef HPUX_THREADS
        !           555:   /* pthread_attr_t is not a structure, thus a simple structure copy   */
        !           556:   /* won't work.                                                       */
        !           557:   static void copy_attr(pthread_attr_t * pa_ptr,
        !           558:                        const pthread_attr_t  * source) {
        !           559:     int tmp;
        !           560:     size_t stmp;
        !           561:     void * vtmp;
        !           562:     struct sched_param sp_tmp;
        !           563:     pthread_spu_t ps_tmp;
        !           564:     (void) pthread_attr_init(pa_ptr);
        !           565:     (void) pthread_attr_getdetachstate(source, &tmp);
        !           566:     (void) pthread_attr_setdetachstate(pa_ptr, tmp);
        !           567:     (void) pthread_attr_getinheritsched(source, &tmp);
        !           568:     (void) pthread_attr_setinheritsched(pa_ptr, tmp);
        !           569:     (void) pthread_attr_getschedpolicy(source, &tmp);
        !           570:     (void) pthread_attr_setschedpolicy(pa_ptr, tmp);
        !           571:     (void) pthread_attr_getstacksize(source, &stmp);
        !           572:     (void) pthread_attr_setstacksize(pa_ptr, stmp);
        !           573:     (void) pthread_attr_getguardsize(source, &stmp);
        !           574:     (void) pthread_attr_setguardsize(pa_ptr, stmp);
        !           575:     (void) pthread_attr_getstackaddr(source, &vtmp);
        !           576:     (void) pthread_attr_setstackaddr(pa_ptr, vtmp);
        !           577:     (void) pthread_attr_getscope(source, &tmp);
        !           578:     (void) pthread_attr_setscope(pa_ptr, tmp);
        !           579:     (void) pthread_attr_getschedparam(source, &sp_tmp);
        !           580:     (void) pthread_attr_setschedparam(pa_ptr, &sp_tmp);
        !           581:     (void) pthread_attr_getprocessor_np(source, &ps_tmp, &tmp);
        !           582:     (void) pthread_attr_setprocessor_np(pa_ptr, ps_tmp, tmp);
        !           583:   }
        !           584: # else
        !           585: #   define copy_attr(pa_ptr, source) *(pa_ptr) = *(source)
        !           586: # endif
        !           587:
        !           588: int
        !           589: GC_pthread_create(pthread_t *new_thread,
        !           590:                  const pthread_attr_t *attr,
        !           591:                   void *(*start_routine)(void *), void *arg)
        !           592: {
        !           593:     int result;
        !           594:     GC_thread t;
        !           595:     void * stack;
        !           596:     size_t stacksize;
        !           597:     pthread_attr_t new_attr;
        !           598:     int detachstate;
        !           599:     word my_flags = 0;
        !           600:     struct start_info * si = GC_malloc(sizeof(struct start_info));
        !           601:        /* This is otherwise saved only in an area mmapped by the thread */
        !           602:        /* library, which isn't visible to the collector.                */
        !           603:
        !           604:     if (0 == si) return(ENOMEM);
        !           605:     if (0 != sem_init(&(si -> registered), 0, 0)) {
        !           606:         ABORT("sem_init failed");
        !           607:     }
        !           608:     si -> start_routine = start_routine;
        !           609:     si -> arg = arg;
        !           610:     LOCK();
        !           611:     if (!GC_thr_initialized) GC_thr_init();
        !           612:     if (NULL == attr) {
        !           613:         stack = 0;
        !           614:        (void) pthread_attr_init(&new_attr);
        !           615:     } else {
        !           616:        copy_attr(&new_attr, attr);
        !           617:        pthread_attr_getstackaddr(&new_attr, &stack);
        !           618:     }
        !           619:     pthread_attr_getstacksize(&new_attr, &stacksize);
        !           620:     pthread_attr_getdetachstate(&new_attr, &detachstate);
        !           621:     if (stacksize < GC_min_stack_sz) ABORT("Stack too small");
        !           622:     if (0 == stack) {
        !           623:        stack = (void *)GC_stack_alloc(&stacksize);
        !           624:        if (0 == stack) {
        !           625:            UNLOCK();
        !           626:            return(ENOMEM);
        !           627:        }
        !           628:        pthread_attr_setstackaddr(&new_attr, stack);
        !           629:     } else {
        !           630:        my_flags |= CLIENT_OWNS_STACK;
        !           631:     }
        !           632:     if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
        !           633:     si -> flags = my_flags;
        !           634:     si -> stack = stack;
        !           635:     si -> stack_size = stacksize;
        !           636:     result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
        !           637:     if (0 == new_thread && !(my_flags & CLIENT_OWNS_STACK)) {
        !           638:        GC_stack_free(stack, stacksize);
        !           639:     }
        !           640:     UNLOCK();
        !           641:     /* Wait until child has been added to the thread table.            */
        !           642:     /* This also ensures that we hold onto si until the child is done  */
        !           643:     /* with it.  Thus it doesn't matter whether it is otherwise                */
        !           644:     /* visible to the collector.                                       */
        !           645:         while (0 != sem_wait(&(si -> registered))) {
        !           646:          if (errno != EINTR) {
        !           647:            GC_printf1("Sem_wait: errno = %ld\n", (unsigned long) errno);
        !           648:            ABORT("sem_wait failed");
        !           649:          }
        !           650:        }
        !           651:         sem_destroy(&(si -> registered));
        !           652:     pthread_attr_destroy(&new_attr);  /* Not a no-op under HPUX */
        !           653:     return(result);
        !           654: }
        !           655:
        !           656: #ifndef HPUX_THREADS
        !           657: /* For now we use the pthreads locking primitives on HP/UX */
        !           658:
        !           659: GC_bool GC_collecting = 0; /* A hint that we're in the collector and       */
        !           660:                         /* holding the allocation lock for an           */
        !           661:                         /* extended period.                             */
        !           662:
        !           663: /* Reasonably fast spin locks.  Basically the same implementation */
        !           664: /* as STL alloc.h.                                               */
        !           665:
        !           666: #define SLEEP_THRESHOLD 3
        !           667:
        !           668: #ifdef HPUX
        !           669:    unsigned long GC_allocate_lock = 1;
        !           670: #  define GC_TRY_LOCK() GC_test_and_clear(&GC_allocate_lock)
        !           671: #  define GC_LOCK_TAKEN !GC_allocate_lock
        !           672: #else
        !           673:    unsigned long GC_allocate_lock = 0;
        !           674: #  define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock,1)
        !           675: #  define GC_LOCK_TAKEN GC_allocate_lock
        !           676: #endif
        !           677:
        !           678: void GC_lock()
        !           679: {
        !           680: #   define low_spin_max 30  /* spin cycles if we suspect uniprocessor */
        !           681: #   define high_spin_max 1000 /* spin cycles for multiprocessor */
        !           682:     static unsigned spin_max = low_spin_max;
        !           683:     unsigned my_spin_max;
        !           684:     static unsigned last_spins = 0;
        !           685:     unsigned my_last_spins;
        !           686:     volatile unsigned junk;
        !           687: #   define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
        !           688:     int i;
        !           689:
        !           690:     if (GC_TRY_LOCK()) {
        !           691:         return;
        !           692:     }
        !           693:     junk = 0;
        !           694:     my_spin_max = spin_max;
        !           695:     my_last_spins = last_spins;
        !           696:     for (i = 0; i < my_spin_max; i++) {
        !           697:         if (GC_collecting) goto yield;
        !           698:         if (i < my_last_spins/2 || GC_LOCK_TAKEN) {
        !           699:             PAUSE;
        !           700:             continue;
        !           701:         }
        !           702:         if (GC_TRY_LOCK()) {
        !           703:            /*
        !           704:              * got it!
        !           705:              * Spinning worked.  Thus we're probably not being scheduled
        !           706:              * against the other process with which we were contending.
        !           707:              * Thus it makes sense to spin longer the next time.
        !           708:             */
        !           709:             last_spins = i;
        !           710:             spin_max = high_spin_max;
        !           711:             return;
        !           712:         }
        !           713:     }
        !           714:     /* We are probably being scheduled against the other process.  Sleep. */
        !           715:     spin_max = low_spin_max;
        !           716: yield:
        !           717:     for (i = 0;; ++i) {
        !           718:         if (GC_TRY_LOCK()) {
        !           719:             return;
        !           720:         }
        !           721:         if (i < SLEEP_THRESHOLD) {
        !           722:             sched_yield();
        !           723:        } else {
        !           724:            struct timespec ts;
        !           725:
        !           726:            if (i > 26) i = 26;
        !           727:                        /* Don't wait for more than about 60msecs, even */
        !           728:                        /* under extreme contention.                    */
        !           729:            ts.tv_sec = 0;
        !           730:            ts.tv_nsec = 1 << i;
        !           731:            nanosleep(&ts, 0);
        !           732:        }
        !           733:     }
        !           734: }
        !           735:
        !           736: #endif /* !HPUX_THREADS */
        !           737:
        !           738: # else
        !           739:
        !           740: #ifndef LINT
        !           741:   int GC_no_Irix_threads;
        !           742: #endif
        !           743:
        !           744: # endif /* IRIX_THREADS */
        !           745:

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