[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.2

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:
1.1.1.2 ! maekawa   390: /* We hold allocation lock.  Should do exactly the right thing if the  */
        !           391: /* world is stopped.  Should not fail if it isn't.                     */
1.1       maekawa   392: void GC_push_all_stacks()
                    393: {
                    394:     register int i;
                    395:     register GC_thread p;
                    396:     register ptr_t sp = GC_approx_sp();
                    397:     register ptr_t hot, cold;
                    398:     pthread_t me = pthread_self();
                    399:
                    400:     if (!GC_thr_initialized) GC_thr_init();
                    401:     /* GC_printf1("Pushing stacks from thread 0x%x\n", me); */
                    402:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
                    403:       for (p = GC_threads[i]; p != 0; p = p -> next) {
                    404:         if (p -> flags & FINISHED) continue;
                    405:         if (pthread_equal(p -> id, me)) {
                    406:            hot = GC_approx_sp();
                    407:        } else {
                    408:            hot = p -> stack_ptr;
                    409:        }
                    410:         if (p -> stack_size != 0) {
                    411: #        ifdef STACK_GROWS_UP
                    412:            cold = p -> stack;
                    413: #        else
                    414:             cold = p -> stack + p -> stack_size;
                    415: #        endif
                    416:         } else {
                    417:             /* The original stack. */
                    418:             cold = GC_stackbottom;
                    419:         }
                    420: #      ifdef STACK_GROWS_UP
                    421:           GC_push_all_stack(cold, hot);
                    422: #      else
                    423:           GC_push_all_stack(hot, cold);
                    424: #      endif
                    425:       }
                    426:     }
                    427: }
                    428:
                    429:
                    430: /* We hold the allocation lock.        */
                    431: void GC_thr_init()
                    432: {
                    433:     GC_thread t;
                    434:     struct sigaction act;
                    435:
                    436:     if (GC_thr_initialized) return;
                    437:     GC_thr_initialized = TRUE;
                    438:     GC_min_stack_sz = HBLKSIZE;
                    439:     GC_page_sz = sysconf(_SC_PAGESIZE);
                    440:     (void) sigaction(SIG_SUSPEND, 0, &act);
                    441:     if (act.sa_handler != SIG_DFL)
                    442:        ABORT("Previously installed SIG_SUSPEND handler");
                    443:     /* Install handler.        */
                    444:        act.sa_handler = GC_suspend_handler;
                    445:        act.sa_flags = SA_RESTART;
                    446:        (void) sigemptyset(&act.sa_mask);
                    447:         if (0 != sigaction(SIG_SUSPEND, &act, 0))
                    448:            ABORT("Failed to install SIG_SUSPEND handler");
                    449:     /* Add the initial thread, so we can stop it.      */
                    450:       t = GC_new_thread(pthread_self());
                    451:       t -> stack_size = 0;
                    452:       t -> stack_ptr = (ptr_t)(&t);
                    453:       t -> flags = DETACHED;
                    454: }
                    455:
                    456: int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
                    457: {
                    458:     sigset_t fudged_set;
                    459:
                    460:     if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
                    461:         fudged_set = *set;
                    462:         sigdelset(&fudged_set, SIG_SUSPEND);
                    463:         set = &fudged_set;
                    464:     }
                    465:     return(pthread_sigmask(how, set, oset));
                    466: }
                    467:
                    468: struct start_info {
                    469:     void *(*start_routine)(void *);
                    470:     void *arg;
                    471:     word flags;
                    472:     ptr_t stack;
                    473:     size_t stack_size;
                    474:     sem_t registered;          /* 1 ==> in our thread table, but       */
                    475:                                /* parent hasn't yet noticed.           */
                    476: };
                    477:
                    478: void GC_thread_exit_proc(void *arg)
                    479: {
                    480:     GC_thread me;
                    481:
                    482:     LOCK();
                    483:     me = GC_lookup_thread(pthread_self());
                    484:     if (me -> flags & DETACHED) {
                    485:        GC_delete_thread(pthread_self());
                    486:     } else {
                    487:        me -> flags |= FINISHED;
                    488:     }
                    489:     UNLOCK();
                    490: }
                    491:
                    492: int GC_pthread_join(pthread_t thread, void **retval)
                    493: {
                    494:     int result;
                    495:     GC_thread thread_gc_id;
                    496:
                    497:     LOCK();
                    498:     thread_gc_id = GC_lookup_thread(thread);
                    499:     /* This is guaranteed to be the intended one, since the thread id  */
                    500:     /* cant have been recycled by pthreads.                            */
                    501:     UNLOCK();
                    502:     result = pthread_join(thread, retval);
                    503:     /* Some versions of the Irix pthreads library can erroneously      */
                    504:     /* return EINTR when the call succeeds.                            */
                    505:        if (EINTR == result) result = 0;
                    506:     LOCK();
                    507:     /* Here the pthread thread id may have been recycled. */
                    508:     GC_delete_gc_thread(thread, thread_gc_id);
                    509:     UNLOCK();
                    510:     return result;
                    511: }
                    512:
                    513: void * GC_start_routine(void * arg)
                    514: {
                    515:     struct start_info * si = arg;
                    516:     void * result;
                    517:     GC_thread me;
                    518:     pthread_t my_pthread;
                    519:     void *(*start)(void *);
                    520:     void *start_arg;
                    521:
                    522:     my_pthread = pthread_self();
                    523:     /* If a GC occurs before the thread is registered, that GC will    */
                    524:     /* ignore this thread.  That's fine, since it will block trying to  */
                    525:     /* acquire the allocation lock, and won't yet hold interesting     */
                    526:     /* pointers.                                                       */
                    527:     LOCK();
                    528:     /* We register the thread here instead of in the parent, so that   */
                    529:     /* we don't need to hold the allocation lock during pthread_create. */
                    530:     /* Holding the allocation lock there would make REDIRECT_MALLOC    */
                    531:     /* impossible.  It probably still doesn't work, but we're a little  */
                    532:     /* closer ...                                                      */
                    533:     /* This unfortunately means that we have to be careful the parent  */
                    534:     /* doesn't try to do a pthread_join before we're registered.       */
                    535:     me = GC_new_thread(my_pthread);
                    536:     me -> flags = si -> flags;
                    537:     me -> stack = si -> stack;
                    538:     me -> stack_size = si -> stack_size;
                    539:     me -> stack_ptr = (ptr_t)si -> stack + si -> stack_size - sizeof(word);
                    540:     UNLOCK();
                    541:     start = si -> start_routine;
                    542:     start_arg = si -> arg;
                    543:     sem_post(&(si -> registered));
                    544:     pthread_cleanup_push(GC_thread_exit_proc, 0);
                    545:     result = (*start)(start_arg);
                    546:     me -> status = result;
                    547:     me -> flags |= FINISHED;
                    548:     pthread_cleanup_pop(1);
                    549:        /* This involves acquiring the lock, ensuring that we can't exit */
                    550:        /* while a collection that thinks we're alive is trying to stop  */
                    551:        /* us.                                                           */
                    552:     return(result);
                    553: }
                    554:
                    555: # ifdef HPUX_THREADS
                    556:   /* pthread_attr_t is not a structure, thus a simple structure copy   */
                    557:   /* won't work.                                                       */
                    558:   static void copy_attr(pthread_attr_t * pa_ptr,
                    559:                        const pthread_attr_t  * source) {
                    560:     int tmp;
                    561:     size_t stmp;
                    562:     void * vtmp;
                    563:     struct sched_param sp_tmp;
                    564:     pthread_spu_t ps_tmp;
                    565:     (void) pthread_attr_init(pa_ptr);
                    566:     (void) pthread_attr_getdetachstate(source, &tmp);
                    567:     (void) pthread_attr_setdetachstate(pa_ptr, tmp);
                    568:     (void) pthread_attr_getinheritsched(source, &tmp);
                    569:     (void) pthread_attr_setinheritsched(pa_ptr, tmp);
                    570:     (void) pthread_attr_getschedpolicy(source, &tmp);
                    571:     (void) pthread_attr_setschedpolicy(pa_ptr, tmp);
                    572:     (void) pthread_attr_getstacksize(source, &stmp);
                    573:     (void) pthread_attr_setstacksize(pa_ptr, stmp);
                    574:     (void) pthread_attr_getguardsize(source, &stmp);
                    575:     (void) pthread_attr_setguardsize(pa_ptr, stmp);
                    576:     (void) pthread_attr_getstackaddr(source, &vtmp);
                    577:     (void) pthread_attr_setstackaddr(pa_ptr, vtmp);
                    578:     (void) pthread_attr_getscope(source, &tmp);
                    579:     (void) pthread_attr_setscope(pa_ptr, tmp);
                    580:     (void) pthread_attr_getschedparam(source, &sp_tmp);
                    581:     (void) pthread_attr_setschedparam(pa_ptr, &sp_tmp);
                    582:     (void) pthread_attr_getprocessor_np(source, &ps_tmp, &tmp);
                    583:     (void) pthread_attr_setprocessor_np(pa_ptr, ps_tmp, tmp);
                    584:   }
                    585: # else
                    586: #   define copy_attr(pa_ptr, source) *(pa_ptr) = *(source)
                    587: # endif
                    588:
                    589: int
                    590: GC_pthread_create(pthread_t *new_thread,
                    591:                  const pthread_attr_t *attr,
                    592:                   void *(*start_routine)(void *), void *arg)
                    593: {
                    594:     int result;
                    595:     GC_thread t;
                    596:     void * stack;
                    597:     size_t stacksize;
                    598:     pthread_attr_t new_attr;
                    599:     int detachstate;
                    600:     word my_flags = 0;
                    601:     struct start_info * si = GC_malloc(sizeof(struct start_info));
                    602:        /* This is otherwise saved only in an area mmapped by the thread */
                    603:        /* library, which isn't visible to the collector.                */
                    604:
                    605:     if (0 == si) return(ENOMEM);
                    606:     if (0 != sem_init(&(si -> registered), 0, 0)) {
                    607:         ABORT("sem_init failed");
                    608:     }
                    609:     si -> start_routine = start_routine;
                    610:     si -> arg = arg;
                    611:     LOCK();
                    612:     if (!GC_thr_initialized) GC_thr_init();
                    613:     if (NULL == attr) {
                    614:         stack = 0;
                    615:        (void) pthread_attr_init(&new_attr);
                    616:     } else {
                    617:        copy_attr(&new_attr, attr);
                    618:        pthread_attr_getstackaddr(&new_attr, &stack);
                    619:     }
                    620:     pthread_attr_getstacksize(&new_attr, &stacksize);
                    621:     pthread_attr_getdetachstate(&new_attr, &detachstate);
                    622:     if (stacksize < GC_min_stack_sz) ABORT("Stack too small");
                    623:     if (0 == stack) {
                    624:        stack = (void *)GC_stack_alloc(&stacksize);
                    625:        if (0 == stack) {
                    626:            UNLOCK();
                    627:            return(ENOMEM);
                    628:        }
                    629:        pthread_attr_setstackaddr(&new_attr, stack);
                    630:     } else {
                    631:        my_flags |= CLIENT_OWNS_STACK;
                    632:     }
                    633:     if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
                    634:     si -> flags = my_flags;
                    635:     si -> stack = stack;
                    636:     si -> stack_size = stacksize;
                    637:     result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
                    638:     if (0 == new_thread && !(my_flags & CLIENT_OWNS_STACK)) {
                    639:        GC_stack_free(stack, stacksize);
                    640:     }
                    641:     UNLOCK();
                    642:     /* Wait until child has been added to the thread table.            */
                    643:     /* This also ensures that we hold onto si until the child is done  */
                    644:     /* with it.  Thus it doesn't matter whether it is otherwise                */
                    645:     /* visible to the collector.                                       */
                    646:         while (0 != sem_wait(&(si -> registered))) {
                    647:          if (errno != EINTR) {
                    648:            GC_printf1("Sem_wait: errno = %ld\n", (unsigned long) errno);
                    649:            ABORT("sem_wait failed");
                    650:          }
                    651:        }
                    652:         sem_destroy(&(si -> registered));
                    653:     pthread_attr_destroy(&new_attr);  /* Not a no-op under HPUX */
                    654:     return(result);
                    655: }
                    656:
                    657: #ifndef HPUX_THREADS
                    658: /* For now we use the pthreads locking primitives on HP/UX */
                    659:
                    660: GC_bool GC_collecting = 0; /* A hint that we're in the collector and       */
                    661:                         /* holding the allocation lock for an           */
                    662:                         /* extended period.                             */
                    663:
                    664: /* Reasonably fast spin locks.  Basically the same implementation */
                    665: /* as STL alloc.h.                                               */
                    666:
                    667: #define SLEEP_THRESHOLD 3
                    668:
                    669: #ifdef HPUX
                    670:    unsigned long GC_allocate_lock = 1;
                    671: #  define GC_TRY_LOCK() GC_test_and_clear(&GC_allocate_lock)
                    672: #  define GC_LOCK_TAKEN !GC_allocate_lock
                    673: #else
                    674:    unsigned long GC_allocate_lock = 0;
                    675: #  define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock,1)
                    676: #  define GC_LOCK_TAKEN GC_allocate_lock
                    677: #endif
                    678:
                    679: void GC_lock()
                    680: {
                    681: #   define low_spin_max 30  /* spin cycles if we suspect uniprocessor */
                    682: #   define high_spin_max 1000 /* spin cycles for multiprocessor */
                    683:     static unsigned spin_max = low_spin_max;
                    684:     unsigned my_spin_max;
                    685:     static unsigned last_spins = 0;
                    686:     unsigned my_last_spins;
                    687:     volatile unsigned junk;
                    688: #   define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
                    689:     int i;
                    690:
                    691:     if (GC_TRY_LOCK()) {
                    692:         return;
                    693:     }
                    694:     junk = 0;
                    695:     my_spin_max = spin_max;
                    696:     my_last_spins = last_spins;
                    697:     for (i = 0; i < my_spin_max; i++) {
                    698:         if (GC_collecting) goto yield;
                    699:         if (i < my_last_spins/2 || GC_LOCK_TAKEN) {
                    700:             PAUSE;
                    701:             continue;
                    702:         }
                    703:         if (GC_TRY_LOCK()) {
                    704:            /*
                    705:              * got it!
                    706:              * Spinning worked.  Thus we're probably not being scheduled
                    707:              * against the other process with which we were contending.
                    708:              * Thus it makes sense to spin longer the next time.
                    709:             */
                    710:             last_spins = i;
                    711:             spin_max = high_spin_max;
                    712:             return;
                    713:         }
                    714:     }
                    715:     /* We are probably being scheduled against the other process.  Sleep. */
                    716:     spin_max = low_spin_max;
                    717: yield:
                    718:     for (i = 0;; ++i) {
                    719:         if (GC_TRY_LOCK()) {
                    720:             return;
                    721:         }
                    722:         if (i < SLEEP_THRESHOLD) {
                    723:             sched_yield();
                    724:        } else {
                    725:            struct timespec ts;
                    726:
                    727:            if (i > 26) i = 26;
                    728:                        /* Don't wait for more than about 60msecs, even */
                    729:                        /* under extreme contention.                    */
                    730:            ts.tv_sec = 0;
                    731:            ts.tv_nsec = 1 << i;
                    732:            nanosleep(&ts, 0);
                    733:        }
                    734:     }
                    735: }
                    736:
                    737: #endif /* !HPUX_THREADS */
                    738:
                    739: # else
                    740:
                    741: #ifndef LINT
                    742:   int GC_no_Irix_threads;
                    743: #endif
                    744:
                    745: # endif /* IRIX_THREADS */
                    746:

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