Back to index

lightning-sunbird  0.9+nobinonly
ptthread.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039 ** File:            ptthread.c
00040 ** Descritpion:        Implemenation for threds using pthreds
00041 ** Exports:            ptthread.h
00042 */
00043 
00044 #if defined(_PR_PTHREADS) || defined(_PR_DCETHREADS)
00045 
00046 #include "prlog.h"
00047 #include "primpl.h"
00048 #include "prpdce.h"
00049 
00050 #include <pthread.h>
00051 #include <unistd.h>
00052 #include <string.h>
00053 #include <signal.h>
00054 
00055 /*
00056  * Record whether or not we have the privilege to set the scheduling
00057  * policy and priority of threads.  0 means that privilege is available.
00058  * EPERM means that privilege is not available.
00059  */
00060 
00061 static PRIntn pt_schedpriv = 0;
00062 extern PRLock *_pr_sleeplock;
00063 
00064 static struct _PT_Bookeeping
00065 {
00066     PRLock *ml;                 /* a lock to protect ourselves */
00067     PRCondVar *cv;              /* used to signal global things */
00068     PRInt32 system, user;       /* a count of the two different types */
00069     PRUintn this_many;          /* number of threads allowed for exit */
00070     pthread_key_t key;          /* private private data key */
00071     PRThread *first, *last;     /* list of threads we know about */
00072 #if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
00073     PRInt32 minPrio, maxPrio;   /* range of scheduling priorities */
00074 #endif
00075 } pt_book = {0};
00076 
00077 static void _pt_thread_death(void *arg);
00078 static void init_pthread_gc_support(void);
00079 
00080 #if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
00081 static PRIntn pt_PriorityMap(PRThreadPriority pri)
00082 {
00083 #ifdef NTO
00084     /* This priority algorithm causes lots of problems on Neutrino
00085      * for now I have just hard coded everything to run at priority 10
00086      * until I can come up with a new algorithm.
00087      *     Jerry.Kirk@Nexwarecorp.com
00088      */
00089     return 10;
00090 #else
00091     return pt_book.minPrio +
00092            pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST;
00093 #endif
00094 }
00095 #endif
00096 
00097 #if defined(GC_LEAK_DETECTOR) && (__GLIBC__ >= 2) && defined(__i386__) 
00098 
00099 #include <setjmp.h>
00100 
00101 typedef struct stack_frame stack_frame;
00102 
00103 struct stack_frame {
00104     stack_frame* next;
00105     void* pc;
00106 };
00107 
00108 static stack_frame* GetStackFrame()
00109 {
00110     jmp_buf jb;
00111     stack_frame* currentFrame;
00112     setjmp(jb);
00113     currentFrame = (stack_frame*)(jb[0].__jmpbuf[JB_BP]);
00114     currentFrame = currentFrame->next;
00115     return currentFrame;
00116 }
00117 
00118 static void* GetStackTop()
00119 {
00120     stack_frame* frame;
00121     frame = GetStackFrame();
00122     while (frame != NULL)
00123     {
00124         ptrdiff_t pc = (ptrdiff_t)frame->pc;
00125         if ((pc < 0x08000000) || (pc > 0x7fffffff) || (frame->next < frame))
00126             return frame;
00127         frame = frame->next;
00128     }
00129     return NULL;
00130 }
00131 #endif /* GC_LEAK_DETECTOR && (__GLIBC__ >= 2) && __i386__ */
00132 
00133 /*
00134 ** Initialize a stack for a native pthread thread
00135 */
00136 static void _PR_InitializeStack(PRThreadStack *ts)
00137 {
00138     if( ts && (ts->stackTop == 0) ) {
00139         ts->allocBase = (char *) &ts;
00140         ts->allocSize = ts->stackSize;
00141 
00142         /*
00143         ** Setup stackTop and stackBottom values.
00144         */
00145 #ifdef HAVE_STACK_GROWING_UP
00146         ts->stackBottom = ts->allocBase + ts->stackSize;
00147         ts->stackTop = ts->allocBase;
00148 #else
00149 #ifdef GC_LEAK_DETECTOR
00150         ts->stackTop    = GetStackTop();
00151         ts->stackBottom = ts->stackTop - ts->stackSize;
00152 #else
00153         ts->stackTop    = ts->allocBase;
00154         ts->stackBottom = ts->allocBase - ts->stackSize;
00155 #endif
00156 #endif
00157     }
00158 }
00159 
00160 static void *_pt_root(void *arg)
00161 {
00162     PRIntn rv;
00163     PRThread *thred = (PRThread*)arg;
00164     PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE;
00165 
00166     /*
00167      * Both the parent thread and this new thread set thred->id.
00168      * The new thread must ensure that thred->id is set before
00169      * it executes its startFunc.  The parent thread must ensure
00170      * that thred->id is set before PR_CreateThread() returns.
00171      * Both threads set thred->id without holding a lock.  Since
00172      * they are writing the same value, this unprotected double
00173      * write should be safe.
00174      */
00175     thred->id = pthread_self();
00176 
00177     /*
00178     ** DCE Threads can't detach during creation, so do it late.
00179     ** I would like to do it only here, but that doesn't seem
00180     ** to work.
00181     */
00182 #if defined(_PR_DCETHREADS)
00183     if (detached)
00184     {
00185         /* pthread_detach() modifies its argument, so we must pass a copy */
00186         pthread_t self = thred->id;
00187         rv = pthread_detach(&self);
00188         PR_ASSERT(0 == rv);
00189     }
00190 #endif /* defined(_PR_DCETHREADS) */
00191 
00192     /* Set up the thread stack information */
00193     _PR_InitializeStack(thred->stack);
00194 
00195     /*
00196      * Set within the current thread the pointer to our object.
00197      * This object will be deleted when the thread termintates,
00198      * whether in a join or detached (see _PR_InitThreads()).
00199      */
00200     rv = pthread_setspecific(pt_book.key, thred);
00201     PR_ASSERT(0 == rv);
00202 
00203     /* make the thread visible to the rest of the runtime */
00204     PR_Lock(pt_book.ml);
00205 
00206     /* If this is a GCABLE thread, set its state appropriately */
00207     if (thred->suspend & PT_THREAD_SETGCABLE)
00208            thred->state |= PT_THREAD_GCABLE;
00209     thred->suspend = 0;
00210 
00211     thred->prev = pt_book.last;
00212     if (pt_book.last)
00213         pt_book.last->next = thred;
00214     else
00215         pt_book.first = thred;
00216     thred->next = NULL;
00217     pt_book.last = thred;
00218     PR_Unlock(pt_book.ml);
00219 
00220     thred->startFunc(thred->arg);  /* make visible to the client */
00221 
00222     /* unhook the thread from the runtime */
00223     PR_Lock(pt_book.ml);
00224     /*
00225      * At this moment, PR_CreateThread() may not have set thred->id yet.
00226      * It is safe for a detached thread to free thred only after
00227      * PR_CreateThread() has set thred->id.
00228      */
00229     if (detached)
00230     {
00231         while (!thred->okToDelete)
00232             PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
00233     }
00234 
00235     if (thred->state & PT_THREAD_SYSTEM)
00236         pt_book.system -= 1;
00237     else if (--pt_book.user == pt_book.this_many)
00238         PR_NotifyAllCondVar(pt_book.cv);
00239     if (NULL == thred->prev)
00240         pt_book.first = thred->next;
00241     else
00242         thred->prev->next = thred->next;
00243     if (NULL == thred->next)
00244         pt_book.last = thred->prev;
00245     else
00246         thred->next->prev = thred->prev;
00247     PR_Unlock(pt_book.ml);
00248 
00249     /*
00250     * Here we set the pthread's backpointer to the PRThread to NULL.
00251     * Otherwise the destructor would get called eagerly as the thread
00252     * returns to the pthread runtime. The joining thread would them be
00253     * the proud possessor of a dangling reference. However, this is the
00254     * last chance to delete the object if the thread is detached, so
00255     * just let the destructor do the work.
00256     */
00257     if (PR_FALSE == detached)
00258     {
00259         rv = pthread_setspecific(pt_book.key, NULL);
00260         PR_ASSERT(0 == rv);
00261     }
00262 
00263     return NULL;
00264 }  /* _pt_root */
00265 
00266 static PRThread* pt_AttachThread(void)
00267 {
00268     PRThread *thred = NULL;
00269 
00270     /*
00271      * NSPR must have been initialized when PR_AttachThread is called.
00272      * We cannot have PR_AttachThread call implicit initialization
00273      * because if multiple threads call PR_AttachThread simultaneously,
00274      * NSPR may be initialized more than once.
00275      * We can't call any function that calls PR_GetCurrentThread()
00276      * either (e.g., PR_SetError()) as that will result in infinite
00277      * recursion.
00278      */
00279     if (!_pr_initialized) return NULL;
00280 
00281     /* PR_NEWZAP must not call PR_GetCurrentThread() */
00282     thred = PR_NEWZAP(PRThread);
00283     if (NULL != thred)
00284     {
00285         int rv;
00286 
00287         thred->priority = PR_PRIORITY_NORMAL;
00288         thred->id = pthread_self();
00289         rv = pthread_setspecific(pt_book.key, thred);
00290         PR_ASSERT(0 == rv);
00291 
00292         thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN;
00293         PR_Lock(pt_book.ml);
00294 
00295         /* then put it into the list */
00296         thred->prev = pt_book.last;
00297         if (pt_book.last)
00298             pt_book.last->next = thred;
00299         else
00300             pt_book.first = thred;
00301         thred->next = NULL;
00302         pt_book.last = thred;
00303         PR_Unlock(pt_book.ml);
00304 
00305     }
00306     return thred;  /* may be NULL */
00307 }  /* pt_AttachThread */
00308 
00309 static PRThread* _PR_CreateThread(
00310     PRThreadType type, void (*start)(void *arg),
00311     void *arg, PRThreadPriority priority, PRThreadScope scope,
00312     PRThreadState state, PRUint32 stackSize, PRBool isGCAble)
00313 {
00314     int rv;
00315     PRThread *thred;
00316     pthread_attr_t tattr;
00317 
00318     if (!_pr_initialized) _PR_ImplicitInitialization();
00319 
00320     if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority)
00321         priority = PR_PRIORITY_FIRST;
00322     else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority)
00323         priority = PR_PRIORITY_LAST;
00324 
00325     rv = _PT_PTHREAD_ATTR_INIT(&tattr);
00326     PR_ASSERT(0 == rv);
00327 
00328     if (EPERM != pt_schedpriv)
00329     {
00330 #if !defined(_PR_DCETHREADS) && defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
00331         struct sched_param schedule;
00332 #endif
00333 
00334 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
00335         rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
00336         PR_ASSERT(0 == rv);
00337 #endif
00338 
00339         /* Use the default scheduling policy */
00340 
00341 #if defined(_PR_DCETHREADS)
00342         rv = pthread_attr_setprio(&tattr, pt_PriorityMap(priority));
00343         PR_ASSERT(0 == rv);
00344 #elif defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
00345         rv = pthread_attr_getschedparam(&tattr, &schedule);
00346         PR_ASSERT(0 == rv);
00347         schedule.sched_priority = pt_PriorityMap(priority);
00348         rv = pthread_attr_setschedparam(&tattr, &schedule);
00349         PR_ASSERT(0 == rv);
00350 #ifdef NTO
00351         rv = pthread_attr_setschedpolicy(&tattr, SCHED_RR); /* Round Robin */
00352         PR_ASSERT(0 == rv);
00353 #endif
00354 #endif /* !defined(_PR_DCETHREADS) */
00355     }
00356 
00357     /*
00358      * DCE threads can't set detach state before creating the thread.
00359      * AIX can't set detach late. Why can't we all just get along?
00360      */
00361 #if !defined(_PR_DCETHREADS)
00362     rv = pthread_attr_setdetachstate(&tattr,
00363         ((PR_JOINABLE_THREAD == state) ?
00364             PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED));
00365     PR_ASSERT(0 == rv);
00366 #endif /* !defined(_PR_DCETHREADS) */
00367 
00368     /*
00369      * If stackSize is 0, we use the default pthread stack size.
00370      */
00371     if (stackSize)
00372     {
00373 #ifdef _MD_MINIMUM_STACK_SIZE
00374         if (stackSize < _MD_MINIMUM_STACK_SIZE)
00375             stackSize = _MD_MINIMUM_STACK_SIZE;
00376 #endif
00377         rv = pthread_attr_setstacksize(&tattr, stackSize);
00378         PR_ASSERT(0 == rv);
00379     }
00380 
00381     thred = PR_NEWZAP(PRThread);
00382     if (NULL == thred)
00383     {
00384         PR_SetError(PR_OUT_OF_MEMORY_ERROR, errno);
00385         goto done;
00386     }
00387     else
00388     {
00389         pthread_t id;
00390 
00391         thred->arg = arg;
00392         thred->startFunc = start;
00393         thred->priority = priority;
00394         if (PR_UNJOINABLE_THREAD == state)
00395             thred->state |= PT_THREAD_DETACHED;
00396 
00397         if (PR_LOCAL_THREAD == scope)
00398               scope = PR_GLOBAL_THREAD;
00399                      
00400         if (PR_GLOBAL_BOUND_THREAD == scope) {
00401 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
00402               rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
00403                      if (rv) {
00404                             /*
00405                              * system scope not supported
00406                              */
00407                      scope = PR_GLOBAL_THREAD;
00408                             /*
00409                              * reset scope
00410                              */
00411                      rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS);
00412                      PR_ASSERT(0 == rv);
00413                      }
00414 #endif
00415               }
00416         if (PR_GLOBAL_THREAD == scope)
00417             thred->state |= PT_THREAD_GLOBAL;
00418         else if (PR_GLOBAL_BOUND_THREAD == scope)
00419             thred->state |= (PT_THREAD_GLOBAL | PT_THREAD_BOUND);
00420               else   /* force it global */
00421             thred->state |= PT_THREAD_GLOBAL;
00422         if (PR_SYSTEM_THREAD == type)
00423             thred->state |= PT_THREAD_SYSTEM;
00424 
00425         thred->suspend =(isGCAble) ? PT_THREAD_SETGCABLE : 0;
00426 
00427         thred->stack = PR_NEWZAP(PRThreadStack);
00428         if (thred->stack == NULL) {
00429             PRIntn oserr = errno;
00430             PR_Free(thred);  /* all that work ... poof! */
00431             PR_SetError(PR_OUT_OF_MEMORY_ERROR, oserr);
00432             thred = NULL;  /* and for what? */
00433             goto done;
00434         }
00435         thred->stack->stackSize = stackSize;
00436         thred->stack->thr = thred;
00437 
00438 #ifdef PT_NO_SIGTIMEDWAIT
00439         pthread_mutex_init(&thred->suspendResumeMutex,NULL);
00440         pthread_cond_init(&thred->suspendResumeCV,NULL);
00441 #endif
00442 
00443         /* make the thread counted to the rest of the runtime */
00444         PR_Lock(pt_book.ml);
00445         if (PR_SYSTEM_THREAD == type)
00446             pt_book.system += 1;
00447         else pt_book.user += 1;
00448         PR_Unlock(pt_book.ml);
00449 
00450         /*
00451          * We pass a pointer to a local copy (instead of thred->id)
00452          * to pthread_create() because who knows what wacky things
00453          * pthread_create() may be doing to its argument.
00454          */
00455         rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
00456 
00457 #if !defined(_PR_DCETHREADS)
00458         if (EPERM == rv)
00459         {
00460 #if defined(IRIX)
00461               if (PR_GLOBAL_BOUND_THREAD == scope) {
00462                             /*
00463                              * SCOPE_SYSTEM requires appropriate privilege
00464                              * reset to process scope and try again
00465                              */
00466                      rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS);
00467                      PR_ASSERT(0 == rv);
00468               thred->state &= ~PT_THREAD_BOUND;
00469                      }
00470 #else
00471             /* Remember that we don't have thread scheduling privilege. */
00472             pt_schedpriv = EPERM;
00473             PR_LOG(_pr_thread_lm, PR_LOG_MIN,
00474                 ("_PR_CreateThread: no thread scheduling privilege"));
00475             /* Try creating the thread again without setting priority. */
00476 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
00477             rv = pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED);
00478             PR_ASSERT(0 == rv);
00479 #endif
00480 #endif /* IRIX */
00481             rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
00482         }
00483 #endif
00484 
00485         if (0 != rv)
00486         {
00487 #if defined(_PR_DCETHREADS)
00488             PRIntn oserr = errno;
00489 #else
00490             PRIntn oserr = rv;
00491 #endif
00492             PR_Lock(pt_book.ml);
00493             if (thred->state & PT_THREAD_SYSTEM)
00494                 pt_book.system -= 1;
00495             else if (--pt_book.user == pt_book.this_many)
00496                 PR_NotifyAllCondVar(pt_book.cv);
00497             PR_Unlock(pt_book.ml);
00498 
00499             PR_Free(thred->stack);
00500             PR_Free(thred);  /* all that work ... poof! */
00501             PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, oserr);
00502             thred = NULL;  /* and for what? */
00503             goto done;
00504         }
00505 
00506         /*
00507          * Both the parent thread and this new thread set thred->id.
00508          * The parent thread must ensure that thred->id is set before
00509          * PR_CreateThread() returns.  (See comments in _pt_root().)
00510          */
00511         thred->id = id;
00512 
00513         /*
00514          * If the new thread is detached, tell it that PR_CreateThread()
00515          * has set thred->id so it's ok to delete thred.
00516          */
00517         if (PR_UNJOINABLE_THREAD == state)
00518         {
00519             PR_Lock(pt_book.ml);
00520             thred->okToDelete = PR_TRUE;
00521             PR_NotifyAllCondVar(pt_book.cv);
00522             PR_Unlock(pt_book.ml);
00523         }
00524     }
00525 
00526 done:
00527     rv = _PT_PTHREAD_ATTR_DESTROY(&tattr);
00528     PR_ASSERT(0 == rv);
00529 
00530     return thred;
00531 }  /* _PR_CreateThread */
00532 
00533 PR_IMPLEMENT(PRThread*) PR_CreateThread(
00534     PRThreadType type, void (*start)(void *arg), void *arg,
00535     PRThreadPriority priority, PRThreadScope scope,
00536     PRThreadState state, PRUint32 stackSize)
00537 {
00538     return _PR_CreateThread(
00539         type, start, arg, priority, scope, state, stackSize, PR_FALSE);
00540 } /* PR_CreateThread */
00541 
00542 PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble(
00543     PRThreadType type, void (*start)(void *arg), void *arg, 
00544     PRThreadPriority priority, PRThreadScope scope,
00545     PRThreadState state, PRUint32 stackSize)
00546 {
00547     return _PR_CreateThread(
00548         type, start, arg, priority, scope, state, stackSize, PR_TRUE);
00549 }  /* PR_CreateThreadGCAble */
00550 
00551 PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thred)
00552 {
00553     return thred->environment;
00554 }  /* GetExecutionEnvironment */
00555  
00556 PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thred, void *env)
00557 {
00558     thred->environment = env;
00559 }  /* SetExecutionEnvironment */
00560 
00561 PR_IMPLEMENT(PRThread*) PR_AttachThread(
00562     PRThreadType type, PRThreadPriority priority, PRThreadStack *stack)
00563 {
00564     return PR_GetCurrentThread();
00565 }  /* PR_AttachThread */
00566 
00567 
00568 PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thred)
00569 {
00570     int rv = -1;
00571     void *result = NULL;
00572     PR_ASSERT(thred != NULL);
00573 
00574     if ((0xafafafaf == thred->state)
00575     || (PT_THREAD_DETACHED == (PT_THREAD_DETACHED & thred->state))
00576     || (PT_THREAD_FOREIGN == (PT_THREAD_FOREIGN & thred->state)))
00577     {
00578         /*
00579          * This might be a bad address, but if it isn't, the state should
00580          * either be an unjoinable thread or it's already had the object
00581          * deleted. However, the client that called join on a detached
00582          * thread deserves all the rath I can muster....
00583          */
00584         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
00585         PR_LogPrint(
00586             "PR_JoinThread: %p not joinable | already smashed\n", thred);
00587     }
00588     else
00589     {
00590         pthread_t id = thred->id;
00591         rv = pthread_join(id, &result);
00592         PR_ASSERT(rv == 0 && result == NULL);
00593         if (0 == rv)
00594         {
00595 #ifdef _PR_DCETHREADS
00596             rv = pthread_detach(&id);
00597             PR_ASSERT(0 == rv);
00598 #endif
00599             _pt_thread_death(thred);
00600         }
00601         else
00602         {
00603             PRErrorCode prerror;
00604             switch (rv)
00605             {
00606                 case EINVAL:  /* not a joinable thread */
00607                 case ESRCH:   /* no thread with given ID */
00608                     prerror = PR_INVALID_ARGUMENT_ERROR;
00609                     break;
00610                 case EDEADLK: /* a thread joining with itself */
00611                     prerror = PR_DEADLOCK_ERROR;
00612                     break;
00613                 default:
00614                     prerror = PR_UNKNOWN_ERROR;
00615                     break;
00616             }
00617             PR_SetError(prerror, rv);
00618         }
00619     }
00620     return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
00621 }  /* PR_JoinThread */
00622 
00623 PR_IMPLEMENT(void) PR_DetachThread(void) { }  /* PR_DetachThread */
00624 
00625 PR_IMPLEMENT(PRThread*) PR_GetCurrentThread(void)
00626 {
00627     void *thred;
00628 
00629     if (!_pr_initialized) _PR_ImplicitInitialization();
00630 
00631     _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
00632     if (NULL == thred) thred = pt_AttachThread();
00633     PR_ASSERT(NULL != thred);
00634     return (PRThread*)thred;
00635 }  /* PR_GetCurrentThread */
00636 
00637 PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thred)
00638 {
00639     return (thred->state & PT_THREAD_BOUND) ?
00640         PR_GLOBAL_BOUND_THREAD : PR_GLOBAL_THREAD;
00641 }  /* PR_GetThreadScope() */
00642 
00643 PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thred)
00644 {
00645     return (thred->state & PT_THREAD_SYSTEM) ?
00646         PR_SYSTEM_THREAD : PR_USER_THREAD;
00647 }
00648 
00649 PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thred)
00650 {
00651     return (thred->state & PT_THREAD_DETACHED) ?
00652         PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD;
00653 }  /* PR_GetThreadState */
00654 
00655 PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thred)
00656 {
00657     PR_ASSERT(thred != NULL);
00658     return thred->priority;
00659 }  /* PR_GetThreadPriority */
00660 
00661 PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred, PRThreadPriority newPri)
00662 {
00663     PRIntn rv = -1;
00664 
00665     PR_ASSERT(NULL != thred);
00666 
00667     if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)newPri)
00668         newPri = PR_PRIORITY_FIRST;
00669     else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)newPri)
00670         newPri = PR_PRIORITY_LAST;
00671 
00672 #if defined(_PR_DCETHREADS)
00673     rv = pthread_setprio(thred->id, pt_PriorityMap(newPri));
00674     /* pthread_setprio returns the old priority */
00675 #elif defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
00676     if (EPERM != pt_schedpriv)
00677     {
00678         int policy;
00679         struct sched_param schedule;
00680 
00681         rv = pthread_getschedparam(thred->id, &policy, &schedule);
00682         if(0 == rv) {
00683                      schedule.sched_priority = pt_PriorityMap(newPri);
00684                      rv = pthread_setschedparam(thred->id, policy, &schedule);
00685                      if (EPERM == rv)
00686                      {
00687                             pt_schedpriv = EPERM;
00688                             PR_LOG(_pr_thread_lm, PR_LOG_MIN,
00689                                    ("PR_SetThreadPriority: no thread scheduling privilege"));
00690                      }
00691               }
00692               if (rv != 0)
00693                      rv = -1;
00694     }
00695 #endif
00696 
00697     thred->priority = newPri;
00698 }  /* PR_SetThreadPriority */
00699 
00700 PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thred)
00701 {
00702     /*
00703     ** If the target thread indicates that it's waiting,
00704     ** find the condition and broadcast to it. Broadcast
00705     ** since we don't know which thread (if there are more
00706     ** than one). This sounds risky, but clients must
00707     ** test their invariants when resumed from a wait and
00708     ** I don't expect very many threads to be waiting on
00709     ** a single condition and I don't expect interrupt to
00710     ** be used very often.
00711     **
00712     ** I don't know why I thought this would work. Must have
00713     ** been one of those weaker momements after I'd been
00714     ** smelling the vapors.
00715     **
00716     ** Even with the followng changes it is possible that
00717     ** the pointer to the condition variable is pointing
00718     ** at a bogus value. Will the unerlying code detect
00719     ** that?
00720     */
00721     PRCondVar *cv;
00722     PR_ASSERT(NULL != thred);
00723     if (NULL == thred) return PR_FAILURE;
00724 
00725     thred->state |= PT_THREAD_ABORTED;
00726 
00727     cv = thred->waiting;
00728     if ((NULL != cv) && !thred->interrupt_blocked)
00729     {
00730         PRIntn rv;
00731         (void)PR_AtomicIncrement(&cv->notify_pending);
00732         rv = pthread_cond_broadcast(&cv->cv);
00733         PR_ASSERT(0 == rv);
00734         if (0 > PR_AtomicDecrement(&cv->notify_pending))
00735             PR_DestroyCondVar(cv);
00736     }
00737     return PR_SUCCESS;
00738 }  /* PR_Interrupt */
00739 
00740 PR_IMPLEMENT(void) PR_ClearInterrupt(void)
00741 {
00742     PRThread *me = PR_CurrentThread();
00743     me->state &= ~PT_THREAD_ABORTED;
00744 }  /* PR_ClearInterrupt */
00745 
00746 PR_IMPLEMENT(void) PR_BlockInterrupt(void)
00747 {
00748     PRThread *me = PR_CurrentThread();
00749     _PT_THREAD_BLOCK_INTERRUPT(me);
00750 }  /* PR_BlockInterrupt */
00751 
00752 PR_IMPLEMENT(void) PR_UnblockInterrupt(void)
00753 {
00754     PRThread *me = PR_CurrentThread();
00755     _PT_THREAD_UNBLOCK_INTERRUPT(me);
00756 }  /* PR_UnblockInterrupt */
00757 
00758 PR_IMPLEMENT(PRStatus) PR_Yield(void)
00759 {
00760     static PRBool warning = PR_TRUE;
00761     if (warning) warning = _PR_Obsolete(
00762         "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)");
00763     return PR_Sleep(PR_INTERVAL_NO_WAIT);
00764 }
00765 
00766 PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime ticks)
00767 {
00768     PRStatus rv = PR_SUCCESS;
00769 
00770     if (!_pr_initialized) _PR_ImplicitInitialization();
00771 
00772     if (PR_INTERVAL_NO_WAIT == ticks)
00773     {
00774         _PT_PTHREAD_YIELD();
00775     }
00776     else
00777     {
00778         PRCondVar *cv;
00779         PRIntervalTime timein;
00780 
00781         timein = PR_IntervalNow();
00782         cv = PR_NewCondVar(_pr_sleeplock);
00783         PR_ASSERT(cv != NULL);
00784         PR_Lock(_pr_sleeplock);
00785         do
00786         {
00787             PRIntervalTime now = PR_IntervalNow();
00788             PRIntervalTime delta = now - timein;
00789             if (delta > ticks) break;
00790             rv = PR_WaitCondVar(cv, ticks - delta);
00791         } while (PR_SUCCESS == rv);
00792         PR_Unlock(_pr_sleeplock);
00793         PR_DestroyCondVar(cv);
00794     }
00795     return rv;
00796 }  /* PR_Sleep */
00797 
00798 static void _pt_thread_death(void *arg)
00799 {
00800     PRThread *thred = (PRThread*)arg;
00801 
00802     if (thred->state & (PT_THREAD_FOREIGN|PT_THREAD_PRIMORD))
00803     {
00804         PR_Lock(pt_book.ml);
00805         if (NULL == thred->prev)
00806             pt_book.first = thred->next;
00807         else
00808             thred->prev->next = thred->next;
00809         if (NULL == thred->next)
00810             pt_book.last = thred->prev;
00811         else
00812             thred->next->prev = thred->prev;
00813         PR_Unlock(pt_book.ml);
00814     }
00815     _PR_DestroyThreadPrivate(thred);
00816     PR_Free(thred->privateData);
00817     if (NULL != thred->errorString)
00818         PR_Free(thred->errorString);
00819     PR_Free(thred->stack);
00820     if (NULL != thred->syspoll_list)
00821         PR_Free(thred->syspoll_list);
00822 #if defined(_PR_POLL_WITH_SELECT)
00823     if (NULL != thred->selectfd_list)
00824         PR_Free(thred->selectfd_list);
00825 #endif
00826 #if defined(DEBUG)
00827     memset(thred, 0xaf, sizeof(PRThread));
00828 #endif /* defined(DEBUG) */
00829     PR_Free(thred);
00830 }  /* _pt_thread_death */
00831 
00832 void _PR_InitThreads(
00833     PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs)
00834 {
00835     int rv;
00836     PRThread *thred;
00837 
00838 #ifdef _PR_NEED_PTHREAD_INIT
00839     /*
00840      * On BSD/OS (3.1 and 4.0), the pthread subsystem is lazily
00841      * initialized, but pthread_self() fails to initialize
00842      * pthreads and hence returns a null thread ID if invoked
00843      * by the primordial thread before any other pthread call.
00844      * So we explicitly initialize pthreads here.
00845      */
00846     pthread_init();
00847 #endif
00848 
00849 #if defined(_PR_DCETHREADS) || defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
00850 #if defined(FREEBSD)
00851     {
00852     pthread_attr_t attr;
00853     int policy;
00854     /* get the min and max priorities of the default policy */
00855     pthread_attr_init(&attr);
00856     pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
00857     pthread_attr_getschedpolicy(&attr, &policy);
00858     pt_book.minPrio = sched_get_priority_min(policy);
00859     PR_ASSERT(-1 != pt_book.minPrio);
00860     pt_book.maxPrio = sched_get_priority_max(policy);
00861     PR_ASSERT(-1 != pt_book.maxPrio);
00862     pthread_attr_destroy(&attr);
00863     }
00864 #else
00865     /*
00866     ** These might be function evaluations
00867     */
00868     pt_book.minPrio = PT_PRIO_MIN;
00869     pt_book.maxPrio = PT_PRIO_MAX;
00870 #endif
00871 #endif
00872     
00873     PR_ASSERT(NULL == pt_book.ml);
00874     pt_book.ml = PR_NewLock();
00875     PR_ASSERT(NULL != pt_book.ml);
00876     pt_book.cv = PR_NewCondVar(pt_book.ml);
00877     PR_ASSERT(NULL != pt_book.cv);
00878     thred = PR_NEWZAP(PRThread);
00879     PR_ASSERT(NULL != thred);
00880     thred->arg = NULL;
00881     thred->startFunc = NULL;
00882     thred->priority = priority;
00883     thred->id = pthread_self();
00884 
00885     thred->state = (PT_THREAD_DETACHED | PT_THREAD_PRIMORD);
00886     if (PR_SYSTEM_THREAD == type)
00887     {
00888         thred->state |= PT_THREAD_SYSTEM;
00889         pt_book.system += 1;
00890            pt_book.this_many = 0;
00891     }
00892     else
00893     {
00894            pt_book.user += 1;
00895            pt_book.this_many = 1;
00896     }
00897     thred->next = thred->prev = NULL;
00898     pt_book.first = pt_book.last = thred;
00899 
00900     thred->stack = PR_NEWZAP(PRThreadStack);
00901     PR_ASSERT(thred->stack != NULL);
00902     thred->stack->stackSize = 0;
00903     thred->stack->thr = thred;
00904        _PR_InitializeStack(thred->stack);
00905 
00906     /*
00907      * Create a key for our use to store a backpointer in the pthread
00908      * to our PRThread object. This object gets deleted when the thread
00909      * returns from its root in the case of a detached thread. Other
00910      * threads delete the objects in Join.
00911      *
00912      * NB: The destructor logic seems to have a bug so it isn't used.
00913      * NBB: Oh really? I'm going to give it a spin - AOF 19 June 1998.
00914      * More info - the problem is that pthreads calls the destructor
00915      * eagerly as the thread returns from its root, rather than lazily
00916      * after the thread is joined. Therefore, threads that are joining
00917      * and holding PRThread references are actually holding pointers to
00918      * nothing.
00919      */
00920     rv = _PT_PTHREAD_KEY_CREATE(&pt_book.key, _pt_thread_death);
00921     PR_ASSERT(0 == rv);
00922     rv = pthread_setspecific(pt_book.key, thred);
00923     PR_ASSERT(0 == rv);    
00924     PR_SetThreadPriority(thred, priority);
00925 }  /* _PR_InitThreads */
00926 
00927 PR_IMPLEMENT(PRStatus) PR_Cleanup(void)
00928 {
00929     PRThread *me = PR_CurrentThread();
00930     int rv;
00931     PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR"));
00932     PR_ASSERT(me->state & PT_THREAD_PRIMORD);
00933     if (me->state & PT_THREAD_PRIMORD)
00934     {
00935         PR_Lock(pt_book.ml);
00936         while (pt_book.user > pt_book.this_many)
00937             PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
00938         PR_Unlock(pt_book.ml);
00939 
00940         _PR_CleanupMW();
00941         _PR_CleanupTime();
00942         _PR_CleanupDtoa();
00943         _PR_CleanupCallOnce();
00944         _PR_ShutdownLinker();
00945         _PR_LogCleanup();
00946         _PR_CleanupNet();
00947         /* Close all the fd's before calling _PR_CleanupIO */
00948         _PR_CleanupIO();
00949 
00950         _pt_thread_death(me);
00951         rv = pthread_setspecific(pt_book.key, NULL);
00952         PR_ASSERT(0 == rv);
00953         /*
00954          * I am not sure if it's safe to delete the cv and lock here,
00955          * since there may still be "system" threads around. If this
00956          * call isn't immediately prior to exiting, then there's a
00957          * problem.
00958          */
00959         if (0 == pt_book.system)
00960         {
00961             PR_DestroyCondVar(pt_book.cv); pt_book.cv = NULL;
00962             PR_DestroyLock(pt_book.ml); pt_book.ml = NULL;
00963         }
00964         PR_DestroyLock(_pr_sleeplock);
00965         _pr_sleeplock = NULL;
00966         _PR_CleanupLayerCache();
00967         _PR_CleanupEnv();
00968 #ifdef _PR_ZONE_ALLOCATOR
00969         _PR_DestroyZones();
00970 #endif
00971         _pr_initialized = PR_FALSE;
00972         return PR_SUCCESS;
00973     }
00974     return PR_FAILURE;
00975 }  /* PR_Cleanup */
00976 
00977 PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status)
00978 {
00979     _exit(status);
00980 }
00981 
00982 PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thred)
00983 {
00984 #if defined(_PR_DCETHREADS)
00985     return (PRUint32)&thred->id;  /* this is really a sham! */
00986 #else
00987     return (PRUint32)thred->id;  /* and I don't know what they will do with it */
00988 #endif
00989 }
00990 
00991 /*
00992  * $$$
00993  * The following two thread-to-processor affinity functions are not
00994  * yet implemented for pthreads.  By the way, these functions should return
00995  * PRStatus rather than PRInt32 to indicate the success/failure status.
00996  * $$$
00997  */
00998 
00999 PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask)
01000 {
01001     return 0;  /* not implemented */
01002 }
01003 
01004 PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask )
01005 {
01006     return 0;  /* not implemented */
01007 }
01008 
01009 PR_IMPLEMENT(void)
01010 PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg)
01011 {
01012     thread->dump = dump;
01013     thread->dumpArg = arg;
01014 }
01015 
01016 /* 
01017  * Garbage collection support follows.
01018  */
01019 
01020 #if defined(_PR_DCETHREADS)
01021 
01022 /*
01023  * statics for Garbage Collection support.  We don't need to protect these
01024  * signal masks since the garbage collector itself is protected by a lock
01025  * and multiple threads will not be garbage collecting at the same time.
01026  */
01027 static sigset_t javagc_vtalarm_sigmask;
01028 static sigset_t javagc_intsoff_sigmask;
01029 
01030 #else /* defined(_PR_DCETHREADS) */
01031 
01032 /* a bogus signal mask for forcing a timed wait */
01033 /* Not so bogus in AIX as we really do a sigwait */
01034 static sigset_t sigwait_set;
01035 
01036 static struct timespec onemillisec = {0, 1000000L};
01037 #ifndef PT_NO_SIGTIMEDWAIT
01038 static struct timespec hundredmillisec = {0, 100000000L};
01039 #endif
01040 
01041 static void suspend_signal_handler(PRIntn sig);
01042 
01043 #ifdef PT_NO_SIGTIMEDWAIT
01044 static void null_signal_handler(PRIntn sig);
01045 #endif
01046 
01047 #endif /* defined(_PR_DCETHREADS) */
01048 
01049 /*
01050  * Linux pthreads use SIGUSR1 and SIGUSR2 internally, which
01051  * conflict with the use of these two signals in our GC support.
01052  * So we don't know how to support GC on Linux pthreads.
01053  */
01054 static void init_pthread_gc_support(void)
01055 {
01056     PRIntn rv;
01057 
01058 #if defined(_PR_DCETHREADS)
01059        rv = sigemptyset(&javagc_vtalarm_sigmask);
01060     PR_ASSERT(0 == rv);
01061        rv = sigaddset(&javagc_vtalarm_sigmask, SIGVTALRM);
01062     PR_ASSERT(0 == rv);
01063 #else  /* defined(_PR_DCETHREADS) */
01064        {
01065            struct sigaction sigact_usr2;
01066 
01067            sigact_usr2.sa_handler = suspend_signal_handler;
01068            sigact_usr2.sa_flags = SA_RESTART;
01069            sigemptyset (&sigact_usr2.sa_mask);
01070 
01071         rv = sigaction (SIGUSR2, &sigact_usr2, NULL);
01072         PR_ASSERT(0 == rv);
01073 
01074         sigemptyset (&sigwait_set);
01075 #if defined(PT_NO_SIGTIMEDWAIT)
01076         sigaddset (&sigwait_set, SIGUSR1);
01077 #else
01078         sigaddset (&sigwait_set, SIGUSR2);
01079 #endif  /* defined(PT_NO_SIGTIMEDWAIT) */
01080        }
01081 #if defined(PT_NO_SIGTIMEDWAIT)
01082        {
01083            struct sigaction sigact_null;
01084            sigact_null.sa_handler = null_signal_handler;
01085            sigact_null.sa_flags = SA_RESTART;
01086            sigemptyset (&sigact_null.sa_mask);
01087         rv = sigaction (SIGUSR1, &sigact_null, NULL);
01088            PR_ASSERT(0 ==rv); 
01089     }
01090 #endif  /* defined(PT_NO_SIGTIMEDWAIT) */
01091 #endif /* defined(_PR_DCETHREADS) */
01092 }
01093 
01094 PR_IMPLEMENT(void) PR_SetThreadGCAble(void)
01095 {
01096     PR_Lock(pt_book.ml);
01097        PR_CurrentThread()->state |= PT_THREAD_GCABLE;
01098     PR_Unlock(pt_book.ml);
01099 }
01100 
01101 PR_IMPLEMENT(void) PR_ClearThreadGCAble(void)
01102 {
01103     PR_Lock(pt_book.ml);
01104        PR_CurrentThread()->state &= (~PT_THREAD_GCABLE);
01105     PR_Unlock(pt_book.ml);
01106 }
01107 
01108 #if defined(DEBUG)
01109 static PRBool suspendAllOn = PR_FALSE;
01110 #endif
01111 
01112 static PRBool suspendAllSuspended = PR_FALSE;
01113 
01114 PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
01115 {
01116     PRIntn count = 0;
01117     PRStatus rv = PR_SUCCESS;
01118     PRThread* thred = pt_book.first;
01119     PRThread *me = PR_CurrentThread();
01120 
01121     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_EnumerateThreads\n"));
01122     /*
01123      * $$$
01124      * Need to suspend all threads other than me before doing this.
01125      * This is really a gross and disgusting thing to do. The only
01126      * good thing is that since all other threads are suspended, holding
01127      * the lock during a callback seems like child's play.
01128      * $$$
01129      */
01130     PR_ASSERT(suspendAllOn);
01131 
01132     while (thred != NULL)
01133     {
01134         /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking
01135          * qp->next after applying the function "func".  In particular, "func"
01136          * might remove the thread from the queue and put it into another one in
01137          * which case qp->next no longer points to the next entry in the original
01138          * queue.
01139          *
01140          * To get around this problem, we save qp->next in qp_next before applying
01141          * "func" and use that saved value as the next value after applying "func".
01142          */
01143         PRThread* next = thred->next;
01144 
01145         if (_PT_IS_GCABLE_THREAD(thred))
01146         {
01147 #if !defined(_PR_DCETHREADS)
01148             PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED));
01149 #endif
01150             PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
01151                    ("In PR_EnumerateThreads callback thread %p thid = %X\n", 
01152                     thred, thred->id));
01153 
01154             rv = func(thred, count++, arg);
01155             if (rv != PR_SUCCESS)
01156                 return rv;
01157         }
01158         thred = next;
01159     }
01160     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
01161           ("End PR_EnumerateThreads count = %d \n", count));
01162     return rv;
01163 }  /* PR_EnumerateThreads */
01164 
01165 /*
01166  * PR_SuspendAll and PR_ResumeAll are called during garbage collection.  The strategy 
01167  * we use is to send a SIGUSR2 signal to every gc able thread that we intend to suspend.
01168  * The signal handler will record the stack pointer and will block until resumed by
01169  * the resume call.  Since the signal handler is the last routine called for the
01170  * suspended thread, the stack pointer will also serve as a place where all the
01171  * registers have been saved on the stack for the previously executing routines.
01172  *
01173  * Through global variables, we also make sure that PR_Suspend and PR_Resume does not
01174  * proceed until the thread is suspended or resumed.
01175  */
01176 
01177 #if !defined(_PR_DCETHREADS)
01178 
01179 /*
01180  * In the signal handler, we can not use condition variable notify or wait.
01181  * This does not work consistently across all pthread platforms.  We also can not 
01182  * use locking since that does not seem to work reliably across platforms.
01183  * Only thing we can do is yielding while testing for a global condition
01184  * to change.  This does work on pthread supported platforms.  We may have
01185  * to play with priortities if there are any problems detected.
01186  */
01187 
01188  /* 
01189   * In AIX, you cannot use ANY pthread calls in the signal handler except perhaps
01190   * pthread_yield. But that is horribly inefficient. Hence we use only sigwait, no
01191   * sigtimedwait is available. We need to use another user signal, SIGUSR1. Actually
01192   * SIGUSR1 is also used by exec in Java. So our usage here breaks the exec in Java,
01193   * for AIX. You cannot use pthread_cond_wait or pthread_delay_np in the signal
01194   * handler as all synchronization mechanisms just break down. 
01195   */
01196 
01197 #if defined(PT_NO_SIGTIMEDWAIT)
01198 static void null_signal_handler(PRIntn sig)
01199 {
01200        return;
01201 }
01202 #endif
01203 
01204 static void suspend_signal_handler(PRIntn sig)
01205 {
01206        PRThread *me = PR_CurrentThread();
01207 
01208        PR_ASSERT(me != NULL);
01209        PR_ASSERT(_PT_IS_GCABLE_THREAD(me));
01210        PR_ASSERT((me->suspend & PT_THREAD_SUSPENDED) == 0);
01211 
01212        PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
01213         ("Begin suspend_signal_handler thred %p thread id = %X\n", 
01214               me, me->id));
01215 
01216        /*
01217         * save stack pointer
01218         */
01219        me->sp = &me;
01220 
01221        /* 
01222           At this point, the thread's stack pointer has been saved,
01223           And it is going to enter a wait loop until it is resumed.
01224           So it is _really_ suspended 
01225        */
01226 
01227        me->suspend |= PT_THREAD_SUSPENDED;
01228 
01229        /*
01230         * now, block current thread
01231         */
01232 #if defined(PT_NO_SIGTIMEDWAIT)
01233        pthread_cond_signal(&me->suspendResumeCV);
01234        while (me->suspend & PT_THREAD_SUSPENDED)
01235        {
01236 #if !defined(FREEBSD) && !defined(NETBSD) && !defined(OPENBSD) \
01237     && !defined(BSDI) && !defined(VMS) && !defined(UNIXWARE) \
01238     && !defined(DARWIN) && !defined(RISCOS) /*XXX*/
01239         PRIntn rv;
01240            sigwait(&sigwait_set, &rv);
01241 #endif
01242        }
01243        me->suspend |= PT_THREAD_RESUMED;
01244        pthread_cond_signal(&me->suspendResumeCV);
01245 #else /* defined(PT_NO_SIGTIMEDWAIT) */
01246        while (me->suspend & PT_THREAD_SUSPENDED)
01247        {
01248               PRIntn rv = sigtimedwait(&sigwait_set, NULL, &hundredmillisec);
01249        PR_ASSERT(-1 == rv);
01250        }
01251        me->suspend |= PT_THREAD_RESUMED;
01252 #endif
01253 
01254     /*
01255      * At this point, thread has been resumed, so set a global condition.
01256      * The ResumeAll needs to know that this has really been resumed. 
01257      * So the signal handler sets a flag which PR_ResumeAll will reset. 
01258      * The PR_ResumeAll must reset this flag ...
01259      */
01260 
01261     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
01262         ("End suspend_signal_handler thred = %p tid = %X\n", me, me->id));
01263 }  /* suspend_signal_handler */
01264 
01265 static void pt_SuspendSet(PRThread *thred)
01266 {
01267     PRIntn rv;
01268 
01269     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
01270           ("pt_SuspendSet thred %p thread id = %X\n", thred, thred->id));
01271 
01272 
01273     /*
01274      * Check the thread state and signal the thread to suspend
01275      */
01276 
01277     PR_ASSERT((thred->suspend & PT_THREAD_SUSPENDED) == 0);
01278 
01279     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
01280           ("doing pthread_kill in pt_SuspendSet thred %p tid = %X\n",
01281           thred, thred->id));
01282 #if defined(VMS)
01283     rv = thread_suspend(thred);
01284 #else
01285     rv = pthread_kill (thred->id, SIGUSR2);
01286 #endif
01287     PR_ASSERT(0 == rv);
01288 }
01289 
01290 static void pt_SuspendTest(PRThread *thred)
01291 {
01292     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
01293           ("Begin pt_SuspendTest thred %p thread id = %X\n", thred, thred->id));
01294 
01295 
01296     /*
01297      * Wait for the thread to be really suspended. This happens when the
01298      * suspend signal handler stores the stack pointer and sets the state
01299      * to suspended. 
01300      */
01301 
01302 #if defined(PT_NO_SIGTIMEDWAIT)
01303     pthread_mutex_lock(&thred->suspendResumeMutex);
01304     while ((thred->suspend & PT_THREAD_SUSPENDED) == 0)
01305     {
01306            pthread_cond_timedwait(
01307                &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
01308        }
01309        pthread_mutex_unlock(&thred->suspendResumeMutex);
01310 #else
01311     while ((thred->suspend & PT_THREAD_SUSPENDED) == 0)
01312     {
01313               PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
01314        PR_ASSERT(-1 == rv);
01315        }
01316 #endif
01317 
01318     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
01319         ("End pt_SuspendTest thred %p tid %X\n", thred, thred->id));
01320 }  /* pt_SuspendTest */
01321 
01322 static void pt_ResumeSet(PRThread *thred)
01323 {
01324     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
01325           ("pt_ResumeSet thred %p thread id = %X\n", thred, thred->id));
01326 
01327     /*
01328      * Clear the global state and set the thread state so that it will
01329      * continue past yield loop in the suspend signal handler
01330      */
01331 
01332     PR_ASSERT(thred->suspend & PT_THREAD_SUSPENDED);
01333 
01334 
01335     thred->suspend &= ~PT_THREAD_SUSPENDED;
01336 
01337 #if defined(PT_NO_SIGTIMEDWAIT)
01338 #if defined(VMS)
01339        thread_resume(thred);
01340 #else
01341        pthread_kill(thred->id, SIGUSR1);
01342 #endif
01343 #endif
01344 
01345 }  /* pt_ResumeSet */
01346 
01347 static void pt_ResumeTest(PRThread *thred)
01348 {
01349     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
01350           ("Begin pt_ResumeTest thred %p thread id = %X\n", thred, thred->id));
01351 
01352     /*
01353      * Wait for the threads resume state to change
01354      * to indicate it is really resumed 
01355      */
01356 #if defined(PT_NO_SIGTIMEDWAIT)
01357     pthread_mutex_lock(&thred->suspendResumeMutex);
01358     while ((thred->suspend & PT_THREAD_RESUMED) == 0)
01359     {
01360            pthread_cond_timedwait(
01361                &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
01362     }
01363     pthread_mutex_unlock(&thred->suspendResumeMutex);
01364 #else
01365     while ((thred->suspend & PT_THREAD_RESUMED) == 0) {
01366               PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
01367        PR_ASSERT(-1 == rv);
01368        }
01369 #endif
01370 
01371     thred->suspend &= ~PT_THREAD_RESUMED;
01372 
01373     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, (
01374         "End pt_ResumeTest thred %p tid %X\n", thred, thred->id));
01375 }  /* pt_ResumeTest */
01376 
01377 static pthread_once_t pt_gc_support_control = PTHREAD_ONCE_INIT;
01378 
01379 PR_IMPLEMENT(void) PR_SuspendAll(void)
01380 {
01381 #ifdef DEBUG
01382     PRIntervalTime stime, etime;
01383 #endif
01384     PRThread* thred = pt_book.first;
01385     PRThread *me = PR_CurrentThread();
01386     int rv;
01387 
01388     rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support);
01389     PR_ASSERT(0 == rv);
01390     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n"));
01391     /*
01392      * Stop all threads which are marked GC able.
01393      */
01394     PR_Lock(pt_book.ml);
01395 #ifdef DEBUG
01396     suspendAllOn = PR_TRUE;
01397     stime = PR_IntervalNow();
01398 #endif
01399     while (thred != NULL)
01400     {
01401            if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
01402               pt_SuspendSet(thred);
01403         thred = thred->next;
01404     }
01405 
01406     /* Wait till they are really suspended */
01407     thred = pt_book.first;
01408     while (thred != NULL)
01409     {
01410            if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
01411             pt_SuspendTest(thred);
01412         thred = thred->next;
01413     }
01414 
01415     suspendAllSuspended = PR_TRUE;
01416 
01417 #ifdef DEBUG
01418     etime = PR_IntervalNow();
01419     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,\
01420         ("End PR_SuspendAll (time %dms)\n",
01421         PR_IntervalToMilliseconds(etime - stime)));
01422 #endif
01423 }  /* PR_SuspendAll */
01424 
01425 PR_IMPLEMENT(void) PR_ResumeAll(void)
01426 {
01427 #ifdef DEBUG
01428     PRIntervalTime stime, etime;
01429 #endif
01430     PRThread* thred = pt_book.first;
01431     PRThread *me = PR_CurrentThread();
01432     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n"));
01433     /*
01434      * Resume all previously suspended GC able threads.
01435      */
01436     suspendAllSuspended = PR_FALSE;
01437 #ifdef DEBUG
01438     stime = PR_IntervalNow();
01439 #endif
01440 
01441     while (thred != NULL)
01442     {
01443            if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
01444            pt_ResumeSet(thred);
01445         thred = thred->next;
01446     }
01447 
01448     thred = pt_book.first;
01449     while (thred != NULL)
01450     {
01451            if ((thred != me) && _PT_IS_GCABLE_THREAD(thred))
01452            pt_ResumeTest(thred);
01453         thred = thred->next;
01454     }
01455 
01456     PR_Unlock(pt_book.ml);
01457 #ifdef DEBUG
01458     suspendAllOn = PR_FALSE;
01459     etime = PR_IntervalNow();
01460     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
01461         ("End PR_ResumeAll (time %dms)\n",
01462         PR_IntervalToMilliseconds(etime - stime)));
01463 #endif
01464 }  /* PR_ResumeAll */
01465 
01466 /* Return the stack pointer for the given thread- used by the GC */
01467 PR_IMPLEMENT(void *)PR_GetSP(PRThread *thred)
01468 {
01469     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, 
01470            ("in PR_GetSP thred %p thid = %X, sp = %p\n", 
01471            thred, thred->id, thred->sp));
01472     return thred->sp;
01473 }  /* PR_GetSP */
01474 
01475 #else /* !defined(_PR_DCETHREADS) */
01476 
01477 static pthread_once_t pt_gc_support_control = pthread_once_init;
01478 
01479 /*
01480  * For DCE threads, there is no pthread_kill or a way of suspending or resuming a
01481  * particular thread.  We will just disable the preemption (virtual timer alarm) and
01482  * let the executing thread finish the garbage collection.  This stops all other threads
01483  * (GC able or not) and is very inefficient but there is no other choice.
01484  */
01485 PR_IMPLEMENT(void) PR_SuspendAll()
01486 {
01487     PRIntn rv;
01488 
01489     rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support);
01490     PR_ASSERT(0 == rv);  /* returns -1 on failure */
01491 #ifdef DEBUG
01492     suspendAllOn = PR_TRUE;
01493 #endif
01494     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n"));
01495     /* 
01496      * turn off preemption - i.e add virtual alarm signal to the set of 
01497      * blocking signals 
01498      */
01499     rv = sigprocmask(
01500         SIG_BLOCK, &javagc_vtalarm_sigmask, &javagc_intsoff_sigmask);
01501     PR_ASSERT(0 == rv);
01502     suspendAllSuspended = PR_TRUE;
01503     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_SuspendAll\n"));
01504 }  /* PR_SuspendAll */
01505 
01506 PR_IMPLEMENT(void) PR_ResumeAll()
01507 {
01508     PRIntn rv;
01509     
01510     suspendAllSuspended = PR_FALSE;
01511     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n"));
01512     /* turn on preemption - i.e re-enable virtual alarm signal */
01513 
01514     rv = sigprocmask(SIG_SETMASK, &javagc_intsoff_sigmask, (sigset_t *)NULL);
01515     PR_ASSERT(0 == rv);
01516 #ifdef DEBUG
01517     suspendAllOn = PR_FALSE;
01518 #endif
01519 
01520     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_ResumeAll\n"));
01521 }  /* PR_ResumeAll */
01522 
01523 /* Return the stack pointer for the given thread- used by the GC */
01524 PR_IMPLEMENT(void*)PR_GetSP(PRThread *thred)
01525 {
01526        pthread_t tid = thred->id;
01527        char *thread_tcb, *top_sp;
01528 
01529        /*
01530         * For HPUX DCE threads, pthread_t is a struct with the
01531         * following three fields (see pthread.h, dce/cma.h):
01532         *     cma_t_address       field1;
01533         *     short int           field2;
01534         *     short int           field3;
01535         * where cma_t_address is typedef'd to be either void*
01536         * or char*.
01537         */
01538        PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_GetSP\n"));
01539        thread_tcb = (char*)tid.field1;
01540        top_sp = *(char**)(thread_tcb + 128);
01541        PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_GetSP %p \n", top_sp));
01542        return top_sp;
01543 }  /* PR_GetSP */
01544 
01545 #endif /* !defined(_PR_DCETHREADS) */
01546 
01547 #endif  /* defined(_PR_PTHREADS) || defined(_PR_DCETHREADS) */
01548 
01549 /* ptthread.c */