(linenum→info "unix/slp.c:2238")

gauche/0.8.12/gc/aix_irix_threads.c

    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:     ptr_t stack_cold;           /* cold end of the stack         */
   91:     ptr_t stack_hot;            /* Valid only when stopped. */
   92:                                 /* But must be within stack region at       */
   93:                                 /* all times.                               */
   94:     void * status;              /* Used only to avoid premature     */
   95:                                 /* reclamation of any data it might         */
   96:                                 /* reference.                               */
   97: } * GC_thread;
   98: 
   99: GC_thread GC_lookup_thread(pthread_t id);
  100: 
  101: /*
  102:  * The only way to suspend threads given the pthread interface is to send
  103:  * signals.  Unfortunately, this means we have to reserve
  104:  * a signal, and intercept client calls to change the signal mask.
  105:  */
  106: #if 0 /* DOB: 6.1 */
  107: # if defined(GC_AIX_THREADS)
  108: #   define SIG_SUSPEND SIGUSR1
  109: # else
  110: #   define SIG_SUSPEND (SIGRTMIN + 6)
  111: # endif
  112: #endif
  113: 
  114: pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
  115:                                 /* Number of threads stopped so far */
  116: pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER;
  117: pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER;
  118: 
  119: void GC_suspend_handler(int sig)
  120: {
  121:     int dummy;
  122:     GC_thread me;
  123:     sigset_t all_sigs;
  124:     sigset_t old_sigs;
  125:     int i;
  126: 
  127:     if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
  128:     me = GC_lookup_thread(pthread_self());
  129:     /* The lookup here is safe, since I'm doing this on behalf  */
  130:     /* of a thread which holds the allocation lock in order     */
  131:     /* to stop the world.  Thus concurrent modification of the  */
  132:     /* data structure is impossible.                            */
  133:     if (PLEASE_STOP != me -> stop) {
  134:         /* Misdirected signal. */
  135:         pthread_mutex_unlock(&GC_suspend_lock);
  136:         return;
  137:     }
  138:     pthread_mutex_lock(&GC_suspend_lock);
  139:     me -> stack_hot = (ptr_t)(&dummy);
  140:     me -> stop = STOPPED;
  141:     pthread_cond_signal(&GC_suspend_ack_cv);
  142:     pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock);
  143:     pthread_mutex_unlock(&GC_suspend_lock);
  144:     /* GC_printf1("Continuing 0x%x\n", pthread_self()); */
  145: }
  146: 
  147: 
  148: GC_bool GC_thr_initialized = FALSE;
  149: 
  150: 
  151: # define THREAD_TABLE_SZ 128    /* Must be power of 2      */
  152: volatile GC_thread GC_threads[THREAD_TABLE_SZ];
  153: 
  154: void GC_push_thread_structures GC_PROTO((void))
  155: {
  156:     GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
  157: }
  158: 
  159: /* Add a thread to GC_threads.  We assume it wasn't already there.      */
  160: /* Caller holds allocation lock.                                        */
  161: GC_thread GC_new_thread(pthread_t id)
  162: {
  163:     int hv = ((word)id) % THREAD_TABLE_SZ;
  164:     GC_thread result;
  165:     static struct GC_Thread_Rep first_thread;
  166:     static GC_bool first_thread_used = FALSE;
  167:     
  168:     GC_ASSERT(I_HOLD_LOCK());
  169:     if (!first_thread_used) {
  170:         result = &first_thread;
  171:         first_thread_used = TRUE;
  172:         /* Dont acquire allocation lock, since we may already hold it. */
  173:     } else {
  174:         result = (struct GC_Thread_Rep *)
  175:                  GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
  176:     }
  177:     if (result == 0) return(0);
  178:     result -> id = id;
  179:     result -> next = GC_threads[hv];
  180:     GC_threads[hv] = result;
  181:     /* result -> flags = 0;     */
  182:     /* result -> stop = 0;      */
  183:     return(result);
  184: }
  185: 
  186: /* Delete a thread from GC_threads.  We assume it is there.     */
  187: /* (The code intentionally traps if it wasn't.)                 */
  188: /* Caller holds allocation lock.                                */
  189: /* We explicitly pass in the GC_thread we're looking for, since */
  190: /* if a thread has been joined, but we have not yet             */
  191: /* been notified, then there may be more than one thread        */
  192: /* in the table with the same pthread id.                       */
  193: /* This is OK, but we need a way to delete a specific one.      */
  194: void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
  195: {
  196:     int hv = ((word)id) % THREAD_TABLE_SZ;
  197:     register GC_thread p = GC_threads[hv];
  198:     register GC_thread prev = 0;
  199: 
  200:     GC_ASSERT(I_HOLD_LOCK());
  201:     while (p != gc_id) {
  202:         prev = p;
  203:         p = p -> next;
  204:     }
  205:     if (prev == 0) {
  206:         GC_threads[hv] = p -> next;
  207:     } else {
  208:         prev -> next = p -> next;
  209:     }
  210: }
  211: 
  212: /* Return a GC_thread corresponding to a given thread_t.        */
  213: /* Returns 0 if it's not there.                                 */
  214: /* Caller holds  allocation lock or otherwise inhibits          */
  215: /* updates.                                                     */
  216: /* If there is more than one thread with the given id we        */
  217: /* return the most recent one.                                  */
  218: GC_thread GC_lookup_thread(pthread_t id)
  219: {
  220:     int hv = ((word)id) % THREAD_TABLE_SZ;
  221:     register GC_thread p = GC_threads[hv];
  222:     
  223:     /* I either hold the lock, or i'm being called from the stop-the-world
  224:      * handler. */
  225: #if defined(GC_AIX_THREADS)
  226:     GC_ASSERT(I_HOLD_LOCK()); /* no stop-the-world handler needed on AIX */
  227: #endif
  228:     while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
  229:     return(p);
  230: }
  231: 
  232: #if defined(GC_AIX_THREADS)
  233: void GC_stop_world()
  234: {
  235:     pthread_t my_thread = pthread_self();
  236:     register int i;
  237:     register GC_thread p;
  238:     register int result;
  239:     struct timespec timeout;
  240: 
  241:     GC_ASSERT(I_HOLD_LOCK());
  242:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
  243:       for (p = GC_threads[i]; p != 0; p = p -> next) {
  244:         if (p -> id != my_thread) {
  245:           pthread_suspend_np(p->id);
  246:         }
  247:       }
  248:     }
  249:     /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
  250: }
  251: 
  252: void GC_start_world()
  253: {
  254:     GC_thread p;
  255:     unsigned i;
  256:     pthread_t my_thread = pthread_self();
  257: 
  258:     /* GC_printf0("World starting\n"); */
  259:     GC_ASSERT(I_HOLD_LOCK());
  260:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
  261:       for (p = GC_threads[i]; p != 0; p = p -> next) {
  262:         if (p -> id != my_thread) {
  263:           pthread_continue_np(p->id);
  264:         }
  265:       }
  266:     }
  267: }
  268: 
  269: #else /* GC_AIX_THREADS */
  270: 
  271: /* Caller holds allocation lock.        */
  272: void GC_stop_world()
  273: {
  274:     pthread_t my_thread = pthread_self();
  275:     register int i;
  276:     register GC_thread p;
  277:     register int result;
  278:     struct timespec timeout;
  279:     
  280:     GC_ASSERT(I_HOLD_LOCK());
  281:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
  282:       for (p = GC_threads[i]; p != 0; p = p -> next) {
  283:         if (p -> id != my_thread) {
  284:             if (p -> flags & FINISHED) {
  285:                 p -> stop = STOPPED;
  286:                 continue;
  287:             }
  288:             p -> stop = PLEASE_STOP;
  289:             result = pthread_kill(p -> id, SIG_SUSPEND);
  290:             /* GC_printf1("Sent signal to 0x%x\n", p -> id); */
  291:             switch(result) {
  292:                 case ESRCH:
  293:                     /* Not really there anymore.  Possible? */
  294:                     p -> stop = STOPPED;
  295:                     break;
  296:                 case 0:
  297:                     break;
  298:                 default:
  299:                     ABORT("pthread_kill failed");
  300:             }
  301:         }
  302:       }
  303:     }
  304:     pthread_mutex_lock(&GC_suspend_lock);
  305:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
  306:       for (p = GC_threads[i]; p != 0; p = p -> next) {
  307:         while (p -> id != my_thread && p -> stop != STOPPED) {
  308:             clock_gettime(CLOCK_REALTIME, &timeout);
  309:             timeout.tv_nsec += 50000000; /* 50 msecs */
  310:             if (timeout.tv_nsec >= 1000000000) {
  311:                 timeout.tv_nsec -= 1000000000;
  312:                 ++timeout.tv_sec;
  313:             }
  314:             result = pthread_cond_timedwait(&GC_suspend_ack_cv,
  315:                                             &GC_suspend_lock,
  316:                                             &timeout);
  317:             if (result == ETIMEDOUT) {
  318:                 /* Signal was lost or misdirected.  Try again.      */
  319:                 /* Duplicate signals should be benign.              */
  320:                 result = pthread_kill(p -> id, SIG_SUSPEND);
  321:             }
  322:         }
  323:       }
  324:     }
  325:     pthread_mutex_unlock(&GC_suspend_lock);
  326:     /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
  327: }
  328: 
  329: /* Caller holds allocation lock.        */
  330: void GC_start_world()
  331: {
  332:     GC_thread p;
  333:     unsigned i;
  334: 
  335:     /* GC_printf0("World starting\n"); */
  336:     GC_ASSERT(I_HOLD_LOCK());
  337:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
  338:       for (p = GC_threads[i]; p != 0; p = p -> next) {
  339:         p -> stop = NOT_STOPPED;
  340:       }
  341:     }
  342:     pthread_mutex_lock(&GC_suspend_lock);
  343:     /* All other threads are at pthread_cond_wait in signal handler.    */
  344:     /* Otherwise we couldn't have acquired the lock.                    */
  345:     pthread_mutex_unlock(&GC_suspend_lock);
  346:     pthread_cond_broadcast(&GC_continue_cv);
  347: }
  348: 
  349: #endif /* GC_AIX_THREADS */
  350: 
  351: 
  352: /* We hold allocation lock.  Should do exactly the right thing if the   */
  353: /* world is stopped.  Should not fail if it isn't.                      */
  354: void GC_push_all_stacks()
  355: {
  356:     register int i;
  357:     register GC_thread p;
  358:     register ptr_t hot, cold;
  359:     pthread_t me = pthread_self();
  360:     
  361:     /* GC_init() should have been called before GC_push_all_stacks is
  362:      * invoked, and GC_init calls GC_thr_init(), which sets
  363:      * GC_thr_initialized. */
  364:     GC_ASSERT(GC_thr_initialized);
  365: 
  366:     /* GC_printf1("Pushing stacks from thread 0x%x\n", me); */
  367:     GC_ASSERT(I_HOLD_LOCK());
  368:     for (i = 0; i < THREAD_TABLE_SZ; i++) {
  369:       for (p = GC_threads[i]; p != 0; p = p -> next) {
  370:         if (p -> flags & FINISHED) continue;
  371:         cold = p->stack_cold;
  372:         if (!cold) cold=GC_stackbottom; /* 0 indicates 'original stack' */
  373:         if (pthread_equal(p -> id, me)) {
  374:             hot = GC_approx_sp();
  375:         } else {
  376: #        ifdef GC_AIX_THREADS
  377:           /* AIX doesn't use signals to suspend, so we need to get an */
  378:           /* accurate hot stack pointer.                             */
  379:           /* See http://publib16.boulder.ibm.com/pseries/en_US/libs/basetrf1/pthread_getthrds_np.htm */
  380:           pthread_t id = p -> id;
  381:           struct __pthrdsinfo pinfo;
  382:           int regbuf[64];
  383:           int val = sizeof(regbuf);
  384:           int retval = pthread_getthrds_np(&id, PTHRDSINFO_QUERY_ALL, &pinfo,
  385:                                         sizeof(pinfo), regbuf, &val);
  386:           if (retval != 0) {
  387:             printf("ERROR: pthread_getthrds_np() failed in GC\n");
  388:             abort();
  389:           }
  390:           /* according to the AIX ABI, 
  391:              "the lowest possible valid stack address is 288 bytes (144 + 144)
  392:              less than the current value of the stack pointer.  Functions may
  393:              use this stack space as volatile storage which is not preserved
  394:              across function calls."
  395:              ftp://ftp.penguinppc64.org/pub/people/amodra/PPC-elf64abi.txt.gz
  396:           */
  397:           hot = (ptr_t)(unsigned long)pinfo.__pi_ustk-288;
  398:           cold = (ptr_t)pinfo.__pi_stackend; /* more precise */
  399:           /* push the registers too, because they won't be on stack */
  400:           GC_push_all_eager((ptr_t)&pinfo.__pi_context,
  401:                             (ptr_t)((&pinfo.__pi_context)+1));
  402:           GC_push_all_eager((ptr_t)regbuf, ((ptr_t)regbuf)+val);
  403: #        else
  404:               hot = p -> stack_hot;
  405: #        endif
  406:         }
  407: #       ifdef STACK_GROWS_UP
  408:           GC_push_all_stack(cold, hot);
  409: #       else
  410:  /* printf("thread 0x%x: hot=0x%08x cold=0x%08x\n", p -> id, hot, cold); */
  411:           GC_push_all_stack(hot, cold);
  412: #       endif
  413:       }
  414:     }
  415: }
  416: 
  417: 
  418: /* We hold the allocation lock. */
  419: void GC_thr_init()
  420: {
  421:     GC_thread t;
  422:     struct sigaction act;
  423: 
  424:     if (GC_thr_initialized) return;
  425:     GC_ASSERT(I_HOLD_LOCK());
  426:     GC_thr_initialized = TRUE;
  427: #ifndef GC_AIX_THREADS
  428:     (void) sigaction(SIG_SUSPEND, 0, &act);
  429:     if (act.sa_handler != SIG_DFL)
  430:         ABORT("Previously installed SIG_SUSPEND handler");
  431:     /* Install handler. */
  432:         act.sa_handler = GC_suspend_handler;
  433:         act.sa_flags = SA_RESTART;
  434:         (void) sigemptyset(&act.sa_mask);
  435:         if (0 != sigaction(SIG_SUSPEND, &act, 0))
  436:             ABORT("Failed to install SIG_SUSPEND handler");
  437: #endif
  438:     /* Add the initial thread, so we can stop it.       */
  439:       t = GC_new_thread(pthread_self());
  440:       /* use '0' to indicate GC_stackbottom, since GC_init() has not
  441:        * completed by the time we are called (from GC_init_inner()) */
  442:       t -> stack_cold = 0; /* the original stack. */
  443:       t -> stack_hot = (ptr_t)(&t);
  444:       t -> flags = DETACHED;
  445: }
  446: 
  447: int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
  448: {
  449:     sigset_t fudged_set;
  450:     
  451: #ifdef GC_AIX_THREADS
  452:     return(pthread_sigmask(how, set, oset));
  453: #endif
  454: 
  455:     if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
  456:         fudged_set = *set;
  457:         sigdelset(&fudged_set, SIG_SUSPEND);
  458:         set = &fudged_set;
  459:     }
  460:     return(pthread_sigmask(how, set, oset));
  461: }
  462: 
  463: struct start_info {
  464:     void *(*start_routine)(void *);
  465:     void *arg;
  466:     word flags;
  467:     pthread_mutex_t registeredlock;
  468:     pthread_cond_t registered;     
  469:     int volatile registereddone;
  470: };
  471: 
  472: void GC_thread_exit_proc(void *arg)
  473: {
  474:     GC_thread me;
  475: 
  476:     LOCK();
  477:     me = GC_lookup_thread(pthread_self());
  478:     me -> flags |= FINISHED;
  479:     /* reclaim DETACHED thread right away; otherwise wait until join() */
  480:     if (me -> flags & DETACHED) {
  481:         GC_delete_gc_thread(pthread_self(), me);
  482:     }
  483:     UNLOCK();
  484: }
  485: 
  486: int GC_pthread_join(pthread_t thread, void **retval)
  487: {
  488:     int result;
  489:     GC_thread thread_gc_id;
  490:     
  491:     LOCK();
  492:     thread_gc_id = GC_lookup_thread(thread);
  493:     /* This is guaranteed to be the intended one, since the thread id   */
  494:     /* cant have been recycled by pthreads.                             */
  495:     UNLOCK();
  496:     GC_ASSERT(!(thread_gc_id->flags & DETACHED));
  497:     result = pthread_join(thread, retval);
  498:     /* Some versions of the Irix pthreads library can erroneously       */
  499:     /* return EINTR when the call succeeds.                             */
  500:         if (EINTR == result) result = 0;
  501:     GC_ASSERT(thread_gc_id->flags & FINISHED);
  502:     LOCK();
  503:     /* Here the pthread thread id may have been recycled. */
  504:     GC_delete_gc_thread(thread, thread_gc_id);
  505:     UNLOCK();
  506:     return result;
  507: }
  508: 
  509: void * GC_start_routine(void * arg)
  510: {
  511:     int dummy;
  512:     struct start_info * si = arg;
  513:     void * result;
  514:     GC_thread me;
  515:     pthread_t my_pthread;
  516:     void *(*start)(void *);
  517:     void *start_arg;
  518: 
  519:     my_pthread = pthread_self();
  520:     /* If a GC occurs before the thread is registered, that GC will     */
  521:     /* ignore this thread.  That's fine, since it will block trying to  */
  522:     /* acquire the allocation lock, and won't yet hold interesting      */
  523:     /* pointers.                                                        */
  524:     LOCK();
  525:     /* We register the thread here instead of in the parent, so that    */
  526:     /* we don't need to hold the allocation lock during pthread_create. */
  527:     /* Holding the allocation lock there would make REDIRECT_MALLOC     */
  528:     /* impossible.  It probably still doesn't work, but we're a little  */
  529:     /* closer ...                                                       */
  530:     /* This unfortunately means that we have to be careful the parent   */
  531:     /* doesn't try to do a pthread_join before we're registered.        */
  532:     me = GC_new_thread(my_pthread);
  533:     me -> flags = si -> flags;
  534:     me -> stack_cold = (ptr_t) &dummy; /* this now the 'start of stack' */
  535:     me -> stack_hot = me->stack_cold;/* this field should always be sensible */
  536:     UNLOCK();
  537:     start = si -> start_routine;
  538:     start_arg = si -> arg;
  539: 
  540:     pthread_mutex_lock(&(si->registeredlock));
  541:     si->registereddone = 1;
  542:     pthread_cond_signal(&(si->registered));
  543:     pthread_mutex_unlock(&(si->registeredlock));
  544:     /* si went away as soon as we did this unlock */
  545: 
  546:     pthread_cleanup_push(GC_thread_exit_proc, 0);
  547:     result = (*start)(start_arg);
  548:     me