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

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

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

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