| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |  | 
| 2 /* This Source Code Form is subject to the terms of the Mozilla Public |  | 
| 3  * License, v. 2.0. If a copy of the MPL was not distributed with this |  | 
| 4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |  | 
| 5 |  | 
| 6 /* |  | 
| 7 ** File:            ptthread.c |  | 
| 8 ** Descritpion:        Implemenation for threds using pthreds |  | 
| 9 ** Exports:            ptthread.h |  | 
| 10 */ |  | 
| 11 |  | 
| 12 #if defined(_PR_PTHREADS) || defined(_PR_DCETHREADS) |  | 
| 13 |  | 
| 14 #include "prlog.h" |  | 
| 15 #include "primpl.h" |  | 
| 16 #include "prpdce.h" |  | 
| 17 |  | 
| 18 #include <pthread.h> |  | 
| 19 #include <unistd.h> |  | 
| 20 #include <string.h> |  | 
| 21 #include <signal.h> |  | 
| 22 #include <dlfcn.h> |  | 
| 23 |  | 
| 24 #if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY) |  | 
| 25 #include <pthread_np.h> |  | 
| 26 #endif |  | 
| 27 |  | 
| 28 #ifdef SYMBIAN |  | 
| 29 /* In Open C sched_get_priority_min/max do not work properly, so we undefine |  | 
| 30  * _POSIX_THREAD_PRIORITY_SCHEDULING here. |  | 
| 31  */ |  | 
| 32 #undef _POSIX_THREAD_PRIORITY_SCHEDULING |  | 
| 33 #endif |  | 
| 34 |  | 
| 35 #ifdef _PR_NICE_PRIORITY_SCHEDULING |  | 
| 36 #undef _POSIX_THREAD_PRIORITY_SCHEDULING |  | 
| 37 #include <sys/resource.h> |  | 
| 38 #ifndef HAVE_GETTID |  | 
| 39 #define gettid() (syscall(SYS_gettid)) |  | 
| 40 #endif |  | 
| 41 #endif |  | 
| 42 |  | 
| 43 /* |  | 
| 44  * Record whether or not we have the privilege to set the scheduling |  | 
| 45  * policy and priority of threads.  0 means that privilege is available. |  | 
| 46  * EPERM means that privilege is not available. |  | 
| 47  */ |  | 
| 48 |  | 
| 49 static PRIntn pt_schedpriv = 0; |  | 
| 50 extern PRLock *_pr_sleeplock; |  | 
| 51 |  | 
| 52 static struct _PT_Bookeeping |  | 
| 53 { |  | 
| 54     PRLock *ml;                 /* a lock to protect ourselves */ |  | 
| 55     PRCondVar *cv;              /* used to signal global things */ |  | 
| 56     PRInt32 system, user;       /* a count of the two different types */ |  | 
| 57     PRUintn this_many;          /* number of threads allowed for exit */ |  | 
| 58     pthread_key_t key;          /* thread private data key */ |  | 
| 59     PRBool keyCreated;          /* whether 'key' should be deleted */ |  | 
| 60     PRThread *first, *last;     /* list of threads we know about */ |  | 
| 61 #if defined(_PR_DCETHREADS) || _POSIX_THREAD_PRIORITY_SCHEDULING > 0 |  | 
| 62     PRInt32 minPrio, maxPrio;   /* range of scheduling priorities */ |  | 
| 63 #endif |  | 
| 64 } pt_book = {0}; |  | 
| 65 |  | 
| 66 static void _pt_thread_death(void *arg); |  | 
| 67 static void _pt_thread_death_internal(void *arg, PRBool callDestructors); |  | 
| 68 static void init_pthread_gc_support(void); |  | 
| 69 |  | 
| 70 #if defined(_PR_DCETHREADS) || _POSIX_THREAD_PRIORITY_SCHEDULING > 0 |  | 
| 71 static PRIntn pt_PriorityMap(PRThreadPriority pri) |  | 
| 72 { |  | 
| 73 #ifdef NTO |  | 
| 74     /* This priority algorithm causes lots of problems on Neutrino |  | 
| 75      * for now I have just hard coded everything to run at priority 10 |  | 
| 76      * until I can come up with a new algorithm. |  | 
| 77      *     Jerry.Kirk@Nexwarecorp.com |  | 
| 78      */ |  | 
| 79     return 10; |  | 
| 80 #else |  | 
| 81     return pt_book.minPrio + |  | 
| 82             pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST; |  | 
| 83 #endif |  | 
| 84 } |  | 
| 85 #elif defined(_PR_NICE_PRIORITY_SCHEDULING) |  | 
| 86 /* |  | 
| 87  * This functions maps higher priorities to lower nice values relative to the |  | 
| 88  * nice value specified in the |nice| parameter. The corresponding relative |  | 
| 89  * adjustments are: |  | 
| 90  * |  | 
| 91  * PR_PRIORITY_LOW    +1 |  | 
| 92  * PR_PRIORITY_NORMAL  0 |  | 
| 93  * PR_PRIORITY_HIGH   -1 |  | 
| 94  * PR_PRIORITY_URGENT -2 |  | 
| 95  */ |  | 
| 96 static int pt_RelativePriority(int nice, PRThreadPriority pri) |  | 
| 97 { |  | 
| 98     return nice + (1 - pri); |  | 
| 99 } |  | 
| 100 #endif |  | 
| 101 |  | 
| 102 /* |  | 
| 103 ** Initialize a stack for a native pthread thread |  | 
| 104 */ |  | 
| 105 static void _PR_InitializeStack(PRThreadStack *ts) |  | 
| 106 { |  | 
| 107     if( ts && (ts->stackTop == 0) ) { |  | 
| 108         ts->allocBase = (char *) &ts; |  | 
| 109         ts->allocSize = ts->stackSize; |  | 
| 110 |  | 
| 111         /* |  | 
| 112         ** Setup stackTop and stackBottom values. |  | 
| 113         */ |  | 
| 114 #ifdef HAVE_STACK_GROWING_UP |  | 
| 115         ts->stackBottom = ts->allocBase + ts->stackSize; |  | 
| 116         ts->stackTop = ts->allocBase; |  | 
| 117 #else |  | 
| 118         ts->stackTop    = ts->allocBase; |  | 
| 119         ts->stackBottom = ts->allocBase - ts->stackSize; |  | 
| 120 #endif |  | 
| 121     } |  | 
| 122 } |  | 
| 123 |  | 
| 124 static void *_pt_root(void *arg) |  | 
| 125 { |  | 
| 126     PRIntn rv; |  | 
| 127     PRThread *thred = (PRThread*)arg; |  | 
| 128     PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE; |  | 
| 129     pthread_t id = pthread_self(); |  | 
| 130 #ifdef _PR_NICE_PRIORITY_SCHEDULING |  | 
| 131     pid_t tid; |  | 
| 132 #endif |  | 
| 133 |  | 
| 134 #ifdef _PR_NICE_PRIORITY_SCHEDULING |  | 
| 135     /* |  | 
| 136      * We need to know the kernel thread ID of each thread in order to |  | 
| 137      * set its nice value hence we do it here instead of at creation time. |  | 
| 138      */ |  | 
| 139     tid = gettid(); |  | 
| 140     errno = 0; |  | 
| 141     rv = getpriority(PRIO_PROCESS, 0); |  | 
| 142 |  | 
| 143     /* If we cannot read the main thread's nice value don't try to change the |  | 
| 144      * new thread's nice value. */ |  | 
| 145     if (errno == 0) { |  | 
| 146         setpriority(PRIO_PROCESS, tid, |  | 
| 147                     pt_RelativePriority(rv, thred->priority)); |  | 
| 148     } |  | 
| 149 #endif |  | 
| 150 |  | 
| 151     /* |  | 
| 152     ** DCE Threads can't detach during creation, so do it late. |  | 
| 153     ** I would like to do it only here, but that doesn't seem |  | 
| 154     ** to work. |  | 
| 155     */ |  | 
| 156 #if defined(_PR_DCETHREADS) |  | 
| 157     if (detached) |  | 
| 158     { |  | 
| 159         /* pthread_detach() modifies its argument, so we must pass a copy */ |  | 
| 160         pthread_t self = id; |  | 
| 161         rv = pthread_detach(&self); |  | 
| 162         PR_ASSERT(0 == rv); |  | 
| 163     } |  | 
| 164 #endif /* defined(_PR_DCETHREADS) */ |  | 
| 165 |  | 
| 166     /* Set up the thread stack information */ |  | 
| 167     _PR_InitializeStack(thred->stack); |  | 
| 168 |  | 
| 169     /* |  | 
| 170      * Set within the current thread the pointer to our object. |  | 
| 171      * This object will be deleted when the thread termintates, |  | 
| 172      * whether in a join or detached (see _PR_InitThreads()). |  | 
| 173      */ |  | 
| 174     rv = pthread_setspecific(pt_book.key, thred); |  | 
| 175     PR_ASSERT(0 == rv); |  | 
| 176 |  | 
| 177     /* make the thread visible to the rest of the runtime */ |  | 
| 178     PR_Lock(pt_book.ml); |  | 
| 179     /* |  | 
| 180      * Both the parent thread and this new thread set thred->id. |  | 
| 181      * The new thread must ensure that thred->id is set before |  | 
| 182      * it executes its startFunc.  The parent thread must ensure |  | 
| 183      * that thred->id is set before PR_CreateThread() returns. |  | 
| 184      * Both threads set thred->id while holding pt_book.ml and |  | 
| 185      * use thred->idSet to ensure thred->id is written only once. |  | 
| 186      */ |  | 
| 187     if (!thred->idSet) |  | 
| 188     { |  | 
| 189         thred->id = id; |  | 
| 190         thred->idSet = PR_TRUE; |  | 
| 191     } |  | 
| 192     else |  | 
| 193     { |  | 
| 194         PR_ASSERT(pthread_equal(thred->id, id)); |  | 
| 195     } |  | 
| 196 |  | 
| 197 #ifdef _PR_NICE_PRIORITY_SCHEDULING |  | 
| 198     thred->tid = tid; |  | 
| 199     PR_NotifyAllCondVar(pt_book.cv); |  | 
| 200 #endif |  | 
| 201 |  | 
| 202     /* If this is a GCABLE thread, set its state appropriately */ |  | 
| 203     if (thred->suspend & PT_THREAD_SETGCABLE) |  | 
| 204             thred->state |= PT_THREAD_GCABLE; |  | 
| 205     thred->suspend = 0; |  | 
| 206 |  | 
| 207     thred->prev = pt_book.last; |  | 
| 208     if (pt_book.last) |  | 
| 209         pt_book.last->next = thred; |  | 
| 210     else |  | 
| 211         pt_book.first = thred; |  | 
| 212     thred->next = NULL; |  | 
| 213     pt_book.last = thred; |  | 
| 214     PR_Unlock(pt_book.ml); |  | 
| 215 |  | 
| 216     thred->startFunc(thred->arg);  /* make visible to the client */ |  | 
| 217 |  | 
| 218     /* unhook the thread from the runtime */ |  | 
| 219     PR_Lock(pt_book.ml); |  | 
| 220     /* |  | 
| 221      * At this moment, PR_CreateThread() may not have set thred->id yet. |  | 
| 222      * It is safe for a detached thread to free thred only after |  | 
| 223      * PR_CreateThread() has accessed thred->id and thred->idSet. |  | 
| 224      */ |  | 
| 225     if (detached) |  | 
| 226     { |  | 
| 227         while (!thred->okToDelete) |  | 
| 228             PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); |  | 
| 229     } |  | 
| 230 |  | 
| 231     if (thred->state & PT_THREAD_SYSTEM) |  | 
| 232         pt_book.system -= 1; |  | 
| 233     else if (--pt_book.user == pt_book.this_many) |  | 
| 234         PR_NotifyAllCondVar(pt_book.cv); |  | 
| 235     if (NULL == thred->prev) |  | 
| 236         pt_book.first = thred->next; |  | 
| 237     else |  | 
| 238         thred->prev->next = thred->next; |  | 
| 239     if (NULL == thred->next) |  | 
| 240         pt_book.last = thred->prev; |  | 
| 241     else |  | 
| 242         thred->next->prev = thred->prev; |  | 
| 243     PR_Unlock(pt_book.ml); |  | 
| 244 |  | 
| 245     /* |  | 
| 246     * Here we set the pthread's backpointer to the PRThread to NULL. |  | 
| 247     * Otherwise the destructor would get called eagerly as the thread |  | 
| 248     * returns to the pthread runtime. The joining thread would them be |  | 
| 249     * the proud possessor of a dangling reference. However, this is the |  | 
| 250     * last chance to delete the object if the thread is detached, so |  | 
| 251     * just let the destructor do the work. |  | 
| 252     */ |  | 
| 253     if (PR_FALSE == detached) |  | 
| 254     { |  | 
| 255         /* Call TPD destructors on this thread. */ |  | 
| 256         _PR_DestroyThreadPrivate(thred); |  | 
| 257         rv = pthread_setspecific(pt_book.key, NULL); |  | 
| 258         PR_ASSERT(0 == rv); |  | 
| 259     } |  | 
| 260 |  | 
| 261     return NULL; |  | 
| 262 }  /* _pt_root */ |  | 
| 263 |  | 
| 264 static PRThread* pt_AttachThread(void) |  | 
| 265 { |  | 
| 266     PRThread *thred = NULL; |  | 
| 267 |  | 
| 268     /* |  | 
| 269      * NSPR must have been initialized when PR_AttachThread is called. |  | 
| 270      * We cannot have PR_AttachThread call implicit initialization |  | 
| 271      * because if multiple threads call PR_AttachThread simultaneously, |  | 
| 272      * NSPR may be initialized more than once. |  | 
| 273      * We can't call any function that calls PR_GetCurrentThread() |  | 
| 274      * either (e.g., PR_SetError()) as that will result in infinite |  | 
| 275      * recursion. |  | 
| 276      */ |  | 
| 277     if (!_pr_initialized) return NULL; |  | 
| 278 |  | 
| 279     /* PR_NEWZAP must not call PR_GetCurrentThread() */ |  | 
| 280     thred = PR_NEWZAP(PRThread); |  | 
| 281     if (NULL != thred) |  | 
| 282     { |  | 
| 283         int rv; |  | 
| 284 |  | 
| 285         thred->priority = PR_PRIORITY_NORMAL; |  | 
| 286         thred->id = pthread_self(); |  | 
| 287         thred->idSet = PR_TRUE; |  | 
| 288 #ifdef _PR_NICE_PRIORITY_SCHEDULING |  | 
| 289         thred->tid = gettid(); |  | 
| 290 #endif |  | 
| 291         rv = pthread_setspecific(pt_book.key, thred); |  | 
| 292         PR_ASSERT(0 == rv); |  | 
| 293 |  | 
| 294         thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN; |  | 
| 295         PR_Lock(pt_book.ml); |  | 
| 296 |  | 
| 297         /* then put it into the list */ |  | 
| 298         thred->prev = pt_book.last; |  | 
| 299         if (pt_book.last) |  | 
| 300             pt_book.last->next = thred; |  | 
| 301         else |  | 
| 302             pt_book.first = thred; |  | 
| 303         thred->next = NULL; |  | 
| 304         pt_book.last = thred; |  | 
| 305         PR_Unlock(pt_book.ml); |  | 
| 306 |  | 
| 307     } |  | 
| 308     return thred;  /* may be NULL */ |  | 
| 309 }  /* pt_AttachThread */ |  | 
| 310 |  | 
| 311 static PRThread* _PR_CreateThread( |  | 
| 312     PRThreadType type, void (*start)(void *arg), |  | 
| 313     void *arg, PRThreadPriority priority, PRThreadScope scope, |  | 
| 314     PRThreadState state, PRUint32 stackSize, PRBool isGCAble) |  | 
| 315 { |  | 
| 316     int rv; |  | 
| 317     PRThread *thred; |  | 
| 318     pthread_attr_t tattr; |  | 
| 319 |  | 
| 320     if (!_pr_initialized) _PR_ImplicitInitialization(); |  | 
| 321 |  | 
| 322     if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority) |  | 
| 323         priority = PR_PRIORITY_FIRST; |  | 
| 324     else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority) |  | 
| 325         priority = PR_PRIORITY_LAST; |  | 
| 326 |  | 
| 327     rv = _PT_PTHREAD_ATTR_INIT(&tattr); |  | 
| 328     PR_ASSERT(0 == rv); |  | 
| 329 |  | 
| 330     if (EPERM != pt_schedpriv) |  | 
| 331     { |  | 
| 332 #if !defined(_PR_DCETHREADS) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 |  | 
| 333         struct sched_param schedule; |  | 
| 334 #endif |  | 
| 335 |  | 
| 336 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 |  | 
| 337         rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED); |  | 
| 338         PR_ASSERT(0 == rv); |  | 
| 339 #endif |  | 
| 340 |  | 
| 341         /* Use the default scheduling policy */ |  | 
| 342 |  | 
| 343 #if defined(_PR_DCETHREADS) |  | 
| 344         rv = pthread_attr_setprio(&tattr, pt_PriorityMap(priority)); |  | 
| 345         PR_ASSERT(0 == rv); |  | 
| 346 #elif _POSIX_THREAD_PRIORITY_SCHEDULING > 0 |  | 
| 347         rv = pthread_attr_getschedparam(&tattr, &schedule); |  | 
| 348         PR_ASSERT(0 == rv); |  | 
| 349         schedule.sched_priority = pt_PriorityMap(priority); |  | 
| 350         rv = pthread_attr_setschedparam(&tattr, &schedule); |  | 
| 351         PR_ASSERT(0 == rv); |  | 
| 352 #ifdef NTO |  | 
| 353         rv = pthread_attr_setschedpolicy(&tattr, SCHED_RR); /* Round Robin */ |  | 
| 354         PR_ASSERT(0 == rv); |  | 
| 355 #endif |  | 
| 356 #endif /* !defined(_PR_DCETHREADS) */ |  | 
| 357     } |  | 
| 358 |  | 
| 359     /* |  | 
| 360      * DCE threads can't set detach state before creating the thread. |  | 
| 361      * AIX can't set detach late. Why can't we all just get along? |  | 
| 362      */ |  | 
| 363 #if !defined(_PR_DCETHREADS) |  | 
| 364     rv = pthread_attr_setdetachstate(&tattr, |  | 
| 365         ((PR_JOINABLE_THREAD == state) ? |  | 
| 366             PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED)); |  | 
| 367     PR_ASSERT(0 == rv); |  | 
| 368 #endif /* !defined(_PR_DCETHREADS) */ |  | 
| 369 |  | 
| 370     /* |  | 
| 371      * If stackSize is 0, we use the default pthread stack size. |  | 
| 372      */ |  | 
| 373     if (stackSize) |  | 
| 374     { |  | 
| 375 #ifdef _MD_MINIMUM_STACK_SIZE |  | 
| 376         if (stackSize < _MD_MINIMUM_STACK_SIZE) |  | 
| 377             stackSize = _MD_MINIMUM_STACK_SIZE; |  | 
| 378 #endif |  | 
| 379         rv = pthread_attr_setstacksize(&tattr, stackSize); |  | 
| 380         PR_ASSERT(0 == rv); |  | 
| 381     } |  | 
| 382 |  | 
| 383     thred = PR_NEWZAP(PRThread); |  | 
| 384     if (NULL == thred) |  | 
| 385     { |  | 
| 386         PR_SetError(PR_OUT_OF_MEMORY_ERROR, errno); |  | 
| 387         goto done; |  | 
| 388     } |  | 
| 389     else |  | 
| 390     { |  | 
| 391         pthread_t id; |  | 
| 392 |  | 
| 393         thred->arg = arg; |  | 
| 394         thred->startFunc = start; |  | 
| 395         thred->priority = priority; |  | 
| 396         if (PR_UNJOINABLE_THREAD == state) |  | 
| 397             thred->state |= PT_THREAD_DETACHED; |  | 
| 398 |  | 
| 399         if (PR_LOCAL_THREAD == scope) |  | 
| 400                 scope = PR_GLOBAL_THREAD; |  | 
| 401 |  | 
| 402         if (PR_GLOBAL_BOUND_THREAD == scope) { |  | 
| 403 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 |  | 
| 404                 rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM); |  | 
| 405                         if (rv) { |  | 
| 406                                 /* |  | 
| 407                                  * system scope not supported |  | 
| 408                                  */ |  | 
| 409                         scope = PR_GLOBAL_THREAD; |  | 
| 410                                 /* |  | 
| 411                                  * reset scope |  | 
| 412                                  */ |  | 
| 413                         rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS
      ); |  | 
| 414                         PR_ASSERT(0 == rv); |  | 
| 415                         } |  | 
| 416 #endif |  | 
| 417                 } |  | 
| 418         if (PR_GLOBAL_THREAD == scope) |  | 
| 419             thred->state |= PT_THREAD_GLOBAL; |  | 
| 420         else if (PR_GLOBAL_BOUND_THREAD == scope) |  | 
| 421             thred->state |= (PT_THREAD_GLOBAL | PT_THREAD_BOUND); |  | 
| 422                 else    /* force it global */ |  | 
| 423             thred->state |= PT_THREAD_GLOBAL; |  | 
| 424         if (PR_SYSTEM_THREAD == type) |  | 
| 425             thred->state |= PT_THREAD_SYSTEM; |  | 
| 426 |  | 
| 427         thred->suspend =(isGCAble) ? PT_THREAD_SETGCABLE : 0; |  | 
| 428 |  | 
| 429         thred->stack = PR_NEWZAP(PRThreadStack); |  | 
| 430         if (thred->stack == NULL) { |  | 
| 431             PRIntn oserr = errno; |  | 
| 432             PR_Free(thred);  /* all that work ... poof! */ |  | 
| 433             PR_SetError(PR_OUT_OF_MEMORY_ERROR, oserr); |  | 
| 434             thred = NULL;  /* and for what? */ |  | 
| 435             goto done; |  | 
| 436         } |  | 
| 437         thred->stack->stackSize = stackSize; |  | 
| 438         thred->stack->thr = thred; |  | 
| 439 |  | 
| 440 #ifdef PT_NO_SIGTIMEDWAIT |  | 
| 441         pthread_mutex_init(&thred->suspendResumeMutex,NULL); |  | 
| 442         pthread_cond_init(&thred->suspendResumeCV,NULL); |  | 
| 443 #endif |  | 
| 444 |  | 
| 445         /* make the thread counted to the rest of the runtime */ |  | 
| 446         PR_Lock(pt_book.ml); |  | 
| 447         if (PR_SYSTEM_THREAD == type) |  | 
| 448             pt_book.system += 1; |  | 
| 449         else pt_book.user += 1; |  | 
| 450         PR_Unlock(pt_book.ml); |  | 
| 451 |  | 
| 452         /* |  | 
| 453          * We pass a pointer to a local copy (instead of thred->id) |  | 
| 454          * to pthread_create() because who knows what wacky things |  | 
| 455          * pthread_create() may be doing to its argument. |  | 
| 456          */ |  | 
| 457         rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred); |  | 
| 458 |  | 
| 459 #if !defined(_PR_DCETHREADS) |  | 
| 460         if (EPERM == rv) |  | 
| 461         { |  | 
| 462 #if defined(IRIX) |  | 
| 463                 if (PR_GLOBAL_BOUND_THREAD == scope) { |  | 
| 464                                 /* |  | 
| 465                                  * SCOPE_SYSTEM requires appropriate privilege |  | 
| 466                                  * reset to process scope and try again |  | 
| 467                                  */ |  | 
| 468                         rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS
      ); |  | 
| 469                         PR_ASSERT(0 == rv); |  | 
| 470                 thred->state &= ~PT_THREAD_BOUND; |  | 
| 471                         } |  | 
| 472 #else |  | 
| 473             /* Remember that we don't have thread scheduling privilege. */ |  | 
| 474             pt_schedpriv = EPERM; |  | 
| 475             PR_LOG(_pr_thread_lm, PR_LOG_MIN, |  | 
| 476                 ("_PR_CreateThread: no thread scheduling privilege")); |  | 
| 477             /* Try creating the thread again without setting priority. */ |  | 
| 478 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 |  | 
| 479             rv = pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED); |  | 
| 480             PR_ASSERT(0 == rv); |  | 
| 481 #endif |  | 
| 482 #endif  /* IRIX */ |  | 
| 483             rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred); |  | 
| 484         } |  | 
| 485 #endif |  | 
| 486 |  | 
| 487         if (0 != rv) |  | 
| 488         { |  | 
| 489 #if defined(_PR_DCETHREADS) |  | 
| 490             PRIntn oserr = errno; |  | 
| 491 #else |  | 
| 492             PRIntn oserr = rv; |  | 
| 493 #endif |  | 
| 494             PR_Lock(pt_book.ml); |  | 
| 495             if (thred->state & PT_THREAD_SYSTEM) |  | 
| 496                 pt_book.system -= 1; |  | 
| 497             else if (--pt_book.user == pt_book.this_many) |  | 
| 498                 PR_NotifyAllCondVar(pt_book.cv); |  | 
| 499             PR_Unlock(pt_book.ml); |  | 
| 500 |  | 
| 501             PR_Free(thred->stack); |  | 
| 502             PR_Free(thred);  /* all that work ... poof! */ |  | 
| 503             PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, oserr); |  | 
| 504             thred = NULL;  /* and for what? */ |  | 
| 505             goto done; |  | 
| 506         } |  | 
| 507 |  | 
| 508         PR_Lock(pt_book.ml); |  | 
| 509         /* |  | 
| 510          * Both the parent thread and this new thread set thred->id. |  | 
| 511          * The parent thread must ensure that thred->id is set before |  | 
| 512          * PR_CreateThread() returns.  (See comments in _pt_root().) |  | 
| 513          */ |  | 
| 514         if (!thred->idSet) |  | 
| 515         { |  | 
| 516             thred->id = id; |  | 
| 517             thred->idSet = PR_TRUE; |  | 
| 518         } |  | 
| 519         else |  | 
| 520         { |  | 
| 521             PR_ASSERT(pthread_equal(thred->id, id)); |  | 
| 522         } |  | 
| 523 |  | 
| 524         /* |  | 
| 525          * If the new thread is detached, tell it that PR_CreateThread() has |  | 
| 526          * accessed thred->id and thred->idSet so it's ok to delete thred. |  | 
| 527          */ |  | 
| 528         if (PR_UNJOINABLE_THREAD == state) |  | 
| 529         { |  | 
| 530             thred->okToDelete = PR_TRUE; |  | 
| 531             PR_NotifyAllCondVar(pt_book.cv); |  | 
| 532         } |  | 
| 533         PR_Unlock(pt_book.ml); |  | 
| 534     } |  | 
| 535 |  | 
| 536 done: |  | 
| 537     rv = _PT_PTHREAD_ATTR_DESTROY(&tattr); |  | 
| 538     PR_ASSERT(0 == rv); |  | 
| 539 |  | 
| 540     return thred; |  | 
| 541 }  /* _PR_CreateThread */ |  | 
| 542 |  | 
| 543 PR_IMPLEMENT(PRThread*) PR_CreateThread( |  | 
| 544     PRThreadType type, void (*start)(void *arg), void *arg, |  | 
| 545     PRThreadPriority priority, PRThreadScope scope, |  | 
| 546     PRThreadState state, PRUint32 stackSize) |  | 
| 547 { |  | 
| 548     return _PR_CreateThread( |  | 
| 549         type, start, arg, priority, scope, state, stackSize, PR_FALSE); |  | 
| 550 } /* PR_CreateThread */ |  | 
| 551 |  | 
| 552 PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble( |  | 
| 553     PRThreadType type, void (*start)(void *arg), void *arg, |  | 
| 554     PRThreadPriority priority, PRThreadScope scope, |  | 
| 555     PRThreadState state, PRUint32 stackSize) |  | 
| 556 { |  | 
| 557     return _PR_CreateThread( |  | 
| 558         type, start, arg, priority, scope, state, stackSize, PR_TRUE); |  | 
| 559 }  /* PR_CreateThreadGCAble */ |  | 
| 560 |  | 
| 561 PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thred) |  | 
| 562 { |  | 
| 563     return thred->environment; |  | 
| 564 }  /* GetExecutionEnvironment */ |  | 
| 565 |  | 
| 566 PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thred, void *env) |  | 
| 567 { |  | 
| 568     thred->environment = env; |  | 
| 569 }  /* SetExecutionEnvironment */ |  | 
| 570 |  | 
| 571 PR_IMPLEMENT(PRThread*) PR_AttachThread( |  | 
| 572     PRThreadType type, PRThreadPriority priority, PRThreadStack *stack) |  | 
| 573 { |  | 
| 574     return PR_GetCurrentThread(); |  | 
| 575 }  /* PR_AttachThread */ |  | 
| 576 |  | 
| 577 |  | 
| 578 PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thred) |  | 
| 579 { |  | 
| 580     int rv = -1; |  | 
| 581     void *result = NULL; |  | 
| 582     PR_ASSERT(thred != NULL); |  | 
| 583 |  | 
| 584     if ((0xafafafaf == thred->state) |  | 
| 585     || (PT_THREAD_DETACHED == (PT_THREAD_DETACHED & thred->state)) |  | 
| 586     || (PT_THREAD_FOREIGN == (PT_THREAD_FOREIGN & thred->state))) |  | 
| 587     { |  | 
| 588         /* |  | 
| 589          * This might be a bad address, but if it isn't, the state should |  | 
| 590          * either be an unjoinable thread or it's already had the object |  | 
| 591          * deleted. However, the client that called join on a detached |  | 
| 592          * thread deserves all the rath I can muster.... |  | 
| 593          */ |  | 
| 594         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |  | 
| 595         PR_LogPrint( |  | 
| 596             "PR_JoinThread: %p not joinable | already smashed\n", thred); |  | 
| 597     } |  | 
| 598     else |  | 
| 599     { |  | 
| 600         pthread_t id = thred->id; |  | 
| 601         rv = pthread_join(id, &result); |  | 
| 602         PR_ASSERT(rv == 0 && result == NULL); |  | 
| 603         if (0 == rv) |  | 
| 604         { |  | 
| 605 #ifdef _PR_DCETHREADS |  | 
| 606             rv = pthread_detach(&id); |  | 
| 607             PR_ASSERT(0 == rv); |  | 
| 608 #endif |  | 
| 609             /* |  | 
| 610              * PR_FALSE, because the thread already called the TPD |  | 
| 611              * destructors before exiting _pt_root. |  | 
| 612              */ |  | 
| 613             _pt_thread_death_internal(thred, PR_FALSE); |  | 
| 614         } |  | 
| 615         else |  | 
| 616         { |  | 
| 617             PRErrorCode prerror; |  | 
| 618             switch (rv) |  | 
| 619             { |  | 
| 620                 case EINVAL:  /* not a joinable thread */ |  | 
| 621                 case ESRCH:   /* no thread with given ID */ |  | 
| 622                     prerror = PR_INVALID_ARGUMENT_ERROR; |  | 
| 623                     break; |  | 
| 624                 case EDEADLK: /* a thread joining with itself */ |  | 
| 625                     prerror = PR_DEADLOCK_ERROR; |  | 
| 626                     break; |  | 
| 627                 default: |  | 
| 628                     prerror = PR_UNKNOWN_ERROR; |  | 
| 629                     break; |  | 
| 630             } |  | 
| 631             PR_SetError(prerror, rv); |  | 
| 632         } |  | 
| 633     } |  | 
| 634     return (0 == rv) ? PR_SUCCESS : PR_FAILURE; |  | 
| 635 }  /* PR_JoinThread */ |  | 
| 636 |  | 
| 637 PR_IMPLEMENT(void) PR_DetachThread(void) |  | 
| 638 { |  | 
| 639     void *thred; |  | 
| 640     int rv; |  | 
| 641 |  | 
| 642     _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); |  | 
| 643     if (NULL == thred) return; |  | 
| 644     _pt_thread_death(thred); |  | 
| 645     rv = pthread_setspecific(pt_book.key, NULL); |  | 
| 646     PR_ASSERT(0 == rv); |  | 
| 647 }  /* PR_DetachThread */ |  | 
| 648 |  | 
| 649 PR_IMPLEMENT(PRThread*) PR_GetCurrentThread(void) |  | 
| 650 { |  | 
| 651     void *thred; |  | 
| 652 |  | 
| 653     if (!_pr_initialized) _PR_ImplicitInitialization(); |  | 
| 654 |  | 
| 655     _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); |  | 
| 656     if (NULL == thred) thred = pt_AttachThread(); |  | 
| 657     PR_ASSERT(NULL != thred); |  | 
| 658     return (PRThread*)thred; |  | 
| 659 }  /* PR_GetCurrentThread */ |  | 
| 660 |  | 
| 661 PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thred) |  | 
| 662 { |  | 
| 663     return (thred->state & PT_THREAD_BOUND) ? |  | 
| 664         PR_GLOBAL_BOUND_THREAD : PR_GLOBAL_THREAD; |  | 
| 665 }  /* PR_GetThreadScope() */ |  | 
| 666 |  | 
| 667 PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thred) |  | 
| 668 { |  | 
| 669     return (thred->state & PT_THREAD_SYSTEM) ? |  | 
| 670         PR_SYSTEM_THREAD : PR_USER_THREAD; |  | 
| 671 } |  | 
| 672 |  | 
| 673 PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thred) |  | 
| 674 { |  | 
| 675     return (thred->state & PT_THREAD_DETACHED) ? |  | 
| 676         PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD; |  | 
| 677 }  /* PR_GetThreadState */ |  | 
| 678 |  | 
| 679 PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thred) |  | 
| 680 { |  | 
| 681     PR_ASSERT(thred != NULL); |  | 
| 682     return thred->priority; |  | 
| 683 }  /* PR_GetThreadPriority */ |  | 
| 684 |  | 
| 685 PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred, PRThreadPriority newPri
      ) |  | 
| 686 { |  | 
| 687     PRIntn rv; |  | 
| 688 |  | 
| 689     PR_ASSERT(NULL != thred); |  | 
| 690 |  | 
| 691     if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)newPri) |  | 
| 692         newPri = PR_PRIORITY_FIRST; |  | 
| 693     else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)newPri) |  | 
| 694         newPri = PR_PRIORITY_LAST; |  | 
| 695 |  | 
| 696 #if defined(_PR_DCETHREADS) |  | 
| 697     rv = pthread_setprio(thred->id, pt_PriorityMap(newPri)); |  | 
| 698     /* pthread_setprio returns the old priority */ |  | 
| 699 #elif _POSIX_THREAD_PRIORITY_SCHEDULING > 0 |  | 
| 700     if (EPERM != pt_schedpriv) |  | 
| 701     { |  | 
| 702         int policy; |  | 
| 703         struct sched_param schedule; |  | 
| 704 |  | 
| 705         rv = pthread_getschedparam(thred->id, &policy, &schedule); |  | 
| 706         if(0 == rv) { |  | 
| 707                         schedule.sched_priority = pt_PriorityMap(newPri); |  | 
| 708                         rv = pthread_setschedparam(thred->id, policy, &schedule)
      ; |  | 
| 709                         if (EPERM == rv) |  | 
| 710                         { |  | 
| 711                                 pt_schedpriv = EPERM; |  | 
| 712                                 PR_LOG(_pr_thread_lm, PR_LOG_MIN, |  | 
| 713                                         ("PR_SetThreadPriority: no thread schedu
      ling privilege")); |  | 
| 714                         } |  | 
| 715                 } |  | 
| 716                 if (rv != 0) |  | 
| 717                         rv = -1; |  | 
| 718     } |  | 
| 719 #elif defined(_PR_NICE_PRIORITY_SCHEDULING) |  | 
| 720     PR_Lock(pt_book.ml); |  | 
| 721     while (thred->tid == 0) |  | 
| 722         PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); |  | 
| 723     PR_Unlock(pt_book.ml); |  | 
| 724 |  | 
| 725     errno = 0; |  | 
| 726     rv = getpriority(PRIO_PROCESS, 0); |  | 
| 727 |  | 
| 728     /* Do not proceed unless we know the main thread's nice value. */ |  | 
| 729     if (errno == 0) { |  | 
| 730         rv = setpriority(PRIO_PROCESS, thred->tid, |  | 
| 731                          pt_RelativePriority(rv, newPri)); |  | 
| 732 |  | 
| 733         if (rv == -1) |  | 
| 734         { |  | 
| 735             /* We don't set pt_schedpriv to EPERM in case errno == EPERM |  | 
| 736              * because adjusting the nice value might be permitted for certain |  | 
| 737              * ranges but not for others. */ |  | 
| 738             PR_LOG(_pr_thread_lm, PR_LOG_MIN, |  | 
| 739                 ("PR_SetThreadPriority: setpriority failed with error %d", |  | 
| 740                  errno)); |  | 
| 741         } |  | 
| 742     } |  | 
| 743 #else |  | 
| 744     (void)rv; /* rv is unused */ |  | 
| 745 #endif |  | 
| 746 |  | 
| 747     thred->priority = newPri; |  | 
| 748 }  /* PR_SetThreadPriority */ |  | 
| 749 |  | 
| 750 PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thred) |  | 
| 751 { |  | 
| 752     /* |  | 
| 753     ** If the target thread indicates that it's waiting, |  | 
| 754     ** find the condition and broadcast to it. Broadcast |  | 
| 755     ** since we don't know which thread (if there are more |  | 
| 756     ** than one). This sounds risky, but clients must |  | 
| 757     ** test their invariants when resumed from a wait and |  | 
| 758     ** I don't expect very many threads to be waiting on |  | 
| 759     ** a single condition and I don't expect interrupt to |  | 
| 760     ** be used very often. |  | 
| 761     ** |  | 
| 762     ** I don't know why I thought this would work. Must have |  | 
| 763     ** been one of those weaker momements after I'd been |  | 
| 764     ** smelling the vapors. |  | 
| 765     ** |  | 
| 766     ** Even with the followng changes it is possible that |  | 
| 767     ** the pointer to the condition variable is pointing |  | 
| 768     ** at a bogus value. Will the unerlying code detect |  | 
| 769     ** that? |  | 
| 770     */ |  | 
| 771     PRCondVar *cv; |  | 
| 772     PR_ASSERT(NULL != thred); |  | 
| 773     if (NULL == thred) return PR_FAILURE; |  | 
| 774 |  | 
| 775     thred->state |= PT_THREAD_ABORTED; |  | 
| 776 |  | 
| 777     cv = thred->waiting; |  | 
| 778     if ((NULL != cv) && !thred->interrupt_blocked) |  | 
| 779     { |  | 
| 780         PRIntn rv; |  | 
| 781         (void)PR_ATOMIC_INCREMENT(&cv->notify_pending); |  | 
| 782         rv = pthread_cond_broadcast(&cv->cv); |  | 
| 783         PR_ASSERT(0 == rv); |  | 
| 784         if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) |  | 
| 785             PR_DestroyCondVar(cv); |  | 
| 786     } |  | 
| 787     return PR_SUCCESS; |  | 
| 788 }  /* PR_Interrupt */ |  | 
| 789 |  | 
| 790 PR_IMPLEMENT(void) PR_ClearInterrupt(void) |  | 
| 791 { |  | 
| 792     PRThread *me = PR_GetCurrentThread(); |  | 
| 793     me->state &= ~PT_THREAD_ABORTED; |  | 
| 794 }  /* PR_ClearInterrupt */ |  | 
| 795 |  | 
| 796 PR_IMPLEMENT(void) PR_BlockInterrupt(void) |  | 
| 797 { |  | 
| 798     PRThread *me = PR_GetCurrentThread(); |  | 
| 799     _PT_THREAD_BLOCK_INTERRUPT(me); |  | 
| 800 }  /* PR_BlockInterrupt */ |  | 
| 801 |  | 
| 802 PR_IMPLEMENT(void) PR_UnblockInterrupt(void) |  | 
| 803 { |  | 
| 804     PRThread *me = PR_GetCurrentThread(); |  | 
| 805     _PT_THREAD_UNBLOCK_INTERRUPT(me); |  | 
| 806 }  /* PR_UnblockInterrupt */ |  | 
| 807 |  | 
| 808 PR_IMPLEMENT(PRStatus) PR_Yield(void) |  | 
| 809 { |  | 
| 810     static PRBool warning = PR_TRUE; |  | 
| 811     if (warning) warning = _PR_Obsolete( |  | 
| 812         "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)"); |  | 
| 813     return PR_Sleep(PR_INTERVAL_NO_WAIT); |  | 
| 814 } |  | 
| 815 |  | 
| 816 PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime ticks) |  | 
| 817 { |  | 
| 818     PRStatus rv = PR_SUCCESS; |  | 
| 819 |  | 
| 820     if (!_pr_initialized) _PR_ImplicitInitialization(); |  | 
| 821 |  | 
| 822     if (PR_INTERVAL_NO_WAIT == ticks) |  | 
| 823     { |  | 
| 824         _PT_PTHREAD_YIELD(); |  | 
| 825     } |  | 
| 826     else |  | 
| 827     { |  | 
| 828         PRCondVar *cv; |  | 
| 829         PRIntervalTime timein; |  | 
| 830 |  | 
| 831         timein = PR_IntervalNow(); |  | 
| 832         cv = PR_NewCondVar(_pr_sleeplock); |  | 
| 833         PR_ASSERT(cv != NULL); |  | 
| 834         PR_Lock(_pr_sleeplock); |  | 
| 835         do |  | 
| 836         { |  | 
| 837             PRIntervalTime now = PR_IntervalNow(); |  | 
| 838             PRIntervalTime delta = now - timein; |  | 
| 839             if (delta > ticks) break; |  | 
| 840             rv = PR_WaitCondVar(cv, ticks - delta); |  | 
| 841         } while (PR_SUCCESS == rv); |  | 
| 842         PR_Unlock(_pr_sleeplock); |  | 
| 843         PR_DestroyCondVar(cv); |  | 
| 844     } |  | 
| 845     return rv; |  | 
| 846 }  /* PR_Sleep */ |  | 
| 847 |  | 
| 848 static void _pt_thread_death(void *arg) |  | 
| 849 { |  | 
| 850     void *thred; |  | 
| 851     int rv; |  | 
| 852 |  | 
| 853     _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); |  | 
| 854     if (NULL == thred) |  | 
| 855     { |  | 
| 856         /* |  | 
| 857          * Have PR_GetCurrentThread return the expected value to the |  | 
| 858          * destructors. |  | 
| 859          */ |  | 
| 860         rv = pthread_setspecific(pt_book.key, arg); |  | 
| 861         PR_ASSERT(0 == rv); |  | 
| 862     } |  | 
| 863 |  | 
| 864     /* PR_TRUE for: call destructors */ |  | 
| 865     _pt_thread_death_internal(arg, PR_TRUE); |  | 
| 866 |  | 
| 867     if (NULL == thred) |  | 
| 868     { |  | 
| 869         rv = pthread_setspecific(pt_book.key, NULL); |  | 
| 870         PR_ASSERT(0 == rv); |  | 
| 871     } |  | 
| 872 } |  | 
| 873 |  | 
| 874 static void _pt_thread_death_internal(void *arg, PRBool callDestructors) |  | 
| 875 { |  | 
| 876     PRThread *thred = (PRThread*)arg; |  | 
| 877 |  | 
| 878     if (thred->state & (PT_THREAD_FOREIGN|PT_THREAD_PRIMORD)) |  | 
| 879     { |  | 
| 880         PR_Lock(pt_book.ml); |  | 
| 881         if (NULL == thred->prev) |  | 
| 882             pt_book.first = thred->next; |  | 
| 883         else |  | 
| 884             thred->prev->next = thred->next; |  | 
| 885         if (NULL == thred->next) |  | 
| 886             pt_book.last = thred->prev; |  | 
| 887         else |  | 
| 888             thred->next->prev = thred->prev; |  | 
| 889         PR_Unlock(pt_book.ml); |  | 
| 890     } |  | 
| 891     if (callDestructors) |  | 
| 892         _PR_DestroyThreadPrivate(thred); |  | 
| 893     PR_Free(thred->privateData); |  | 
| 894     if (NULL != thred->errorString) |  | 
| 895         PR_Free(thred->errorString); |  | 
| 896     if (NULL != thred->name) |  | 
| 897         PR_Free(thred->name); |  | 
| 898     PR_Free(thred->stack); |  | 
| 899     if (NULL != thred->syspoll_list) |  | 
| 900         PR_Free(thred->syspoll_list); |  | 
| 901 #if defined(_PR_POLL_WITH_SELECT) |  | 
| 902     if (NULL != thred->selectfd_list) |  | 
| 903         PR_Free(thred->selectfd_list); |  | 
| 904 #endif |  | 
| 905 #if defined(DEBUG) |  | 
| 906     memset(thred, 0xaf, sizeof(PRThread)); |  | 
| 907 #endif /* defined(DEBUG) */ |  | 
| 908     PR_Free(thred); |  | 
| 909 }  /* _pt_thread_death */ |  | 
| 910 |  | 
| 911 void _PR_InitThreads( |  | 
| 912     PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs) |  | 
| 913 { |  | 
| 914     int rv; |  | 
| 915     PRThread *thred; |  | 
| 916 |  | 
| 917     PR_ASSERT(priority == PR_PRIORITY_NORMAL); |  | 
| 918 |  | 
| 919 #ifdef _PR_NEED_PTHREAD_INIT |  | 
| 920     /* |  | 
| 921      * On BSD/OS (3.1 and 4.0), the pthread subsystem is lazily |  | 
| 922      * initialized, but pthread_self() fails to initialize |  | 
| 923      * pthreads and hence returns a null thread ID if invoked |  | 
| 924      * by the primordial thread before any other pthread call. |  | 
| 925      * So we explicitly initialize pthreads here. |  | 
| 926      */ |  | 
| 927     pthread_init(); |  | 
| 928 #endif |  | 
| 929 |  | 
| 930 #if defined(_PR_DCETHREADS) || _POSIX_THREAD_PRIORITY_SCHEDULING > 0 |  | 
| 931 #if defined(FREEBSD) |  | 
| 932     { |  | 
| 933     pthread_attr_t attr; |  | 
| 934     int policy; |  | 
| 935     /* get the min and max priorities of the default policy */ |  | 
| 936     pthread_attr_init(&attr); |  | 
| 937     pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); |  | 
| 938     pthread_attr_getschedpolicy(&attr, &policy); |  | 
| 939     pt_book.minPrio = sched_get_priority_min(policy); |  | 
| 940     PR_ASSERT(-1 != pt_book.minPrio); |  | 
| 941     pt_book.maxPrio = sched_get_priority_max(policy); |  | 
| 942     PR_ASSERT(-1 != pt_book.maxPrio); |  | 
| 943     pthread_attr_destroy(&attr); |  | 
| 944     } |  | 
| 945 #else |  | 
| 946     /* |  | 
| 947     ** These might be function evaluations |  | 
| 948     */ |  | 
| 949     pt_book.minPrio = PT_PRIO_MIN; |  | 
| 950     pt_book.maxPrio = PT_PRIO_MAX; |  | 
| 951 #endif |  | 
| 952 #endif |  | 
| 953 |  | 
| 954     PR_ASSERT(NULL == pt_book.ml); |  | 
| 955     pt_book.ml = PR_NewLock(); |  | 
| 956     PR_ASSERT(NULL != pt_book.ml); |  | 
| 957     pt_book.cv = PR_NewCondVar(pt_book.ml); |  | 
| 958     PR_ASSERT(NULL != pt_book.cv); |  | 
| 959     thred = PR_NEWZAP(PRThread); |  | 
| 960     PR_ASSERT(NULL != thred); |  | 
| 961     thred->arg = NULL; |  | 
| 962     thred->startFunc = NULL; |  | 
| 963     thred->priority = priority; |  | 
| 964     thred->id = pthread_self(); |  | 
| 965     thred->idSet = PR_TRUE; |  | 
| 966 #ifdef _PR_NICE_PRIORITY_SCHEDULING |  | 
| 967     thred->tid = gettid(); |  | 
| 968 #endif |  | 
| 969 |  | 
| 970     thred->state = (PT_THREAD_DETACHED | PT_THREAD_PRIMORD); |  | 
| 971     if (PR_SYSTEM_THREAD == type) |  | 
| 972     { |  | 
| 973         thred->state |= PT_THREAD_SYSTEM; |  | 
| 974         pt_book.system += 1; |  | 
| 975             pt_book.this_many = 0; |  | 
| 976     } |  | 
| 977     else |  | 
| 978     { |  | 
| 979             pt_book.user += 1; |  | 
| 980             pt_book.this_many = 1; |  | 
| 981     } |  | 
| 982     thred->next = thred->prev = NULL; |  | 
| 983     pt_book.first = pt_book.last = thred; |  | 
| 984 |  | 
| 985     thred->stack = PR_NEWZAP(PRThreadStack); |  | 
| 986     PR_ASSERT(thred->stack != NULL); |  | 
| 987     thred->stack->stackSize = 0; |  | 
| 988     thred->stack->thr = thred; |  | 
| 989         _PR_InitializeStack(thred->stack); |  | 
| 990 |  | 
| 991     /* |  | 
| 992      * Create a key for our use to store a backpointer in the pthread |  | 
| 993      * to our PRThread object. This object gets deleted when the thread |  | 
| 994      * returns from its root in the case of a detached thread. Other |  | 
| 995      * threads delete the objects in Join. |  | 
| 996      * |  | 
| 997      * NB: The destructor logic seems to have a bug so it isn't used. |  | 
| 998      * NBB: Oh really? I'm going to give it a spin - AOF 19 June 1998. |  | 
| 999      * More info - the problem is that pthreads calls the destructor |  | 
| 1000      * eagerly as the thread returns from its root, rather than lazily |  | 
| 1001      * after the thread is joined. Therefore, threads that are joining |  | 
| 1002      * and holding PRThread references are actually holding pointers to |  | 
| 1003      * nothing. |  | 
| 1004      */ |  | 
| 1005     rv = _PT_PTHREAD_KEY_CREATE(&pt_book.key, _pt_thread_death); |  | 
| 1006     if (0 != rv) |  | 
| 1007         PR_Assert("0 == rv", __FILE__, __LINE__); |  | 
| 1008     pt_book.keyCreated = PR_TRUE; |  | 
| 1009     rv = pthread_setspecific(pt_book.key, thred); |  | 
| 1010     PR_ASSERT(0 == rv); |  | 
| 1011 }  /* _PR_InitThreads */ |  | 
| 1012 |  | 
| 1013 #ifdef __GNUC__ |  | 
| 1014 /* |  | 
| 1015  * GCC supports the constructor and destructor attributes as of |  | 
| 1016  * version 2.5. |  | 
| 1017  */ |  | 
| 1018 static void _PR_Fini(void) __attribute__ ((destructor)); |  | 
| 1019 #elif defined(__SUNPRO_C) |  | 
| 1020 /* |  | 
| 1021  * Sun Studio compiler |  | 
| 1022  */ |  | 
| 1023 #pragma fini(_PR_Fini) |  | 
| 1024 static void _PR_Fini(void); |  | 
| 1025 #elif defined(HPUX) |  | 
| 1026 /* |  | 
| 1027  * Current versions of HP C compiler define __HP_cc. |  | 
| 1028  * HP C compiler A.11.01.20 doesn't define __HP_cc. |  | 
| 1029  */ |  | 
| 1030 #if defined(__ia64) || defined(_LP64) |  | 
| 1031 #pragma FINI "_PR_Fini" |  | 
| 1032 static void _PR_Fini(void); |  | 
| 1033 #else |  | 
| 1034 /* |  | 
| 1035  * Only HP-UX 10.x style initializers are supported in 32-bit links. |  | 
| 1036  * Need to use the +I PR_HPUX10xInit linker option. |  | 
| 1037  */ |  | 
| 1038 #include <dl.h> |  | 
| 1039 |  | 
| 1040 static void _PR_Fini(void); |  | 
| 1041 |  | 
| 1042 void PR_HPUX10xInit(shl_t handle, int loading) |  | 
| 1043 { |  | 
| 1044     /* |  | 
| 1045      * This function is called when a shared library is loaded as well |  | 
| 1046      * as when the shared library is unloaded.  Note that it may not |  | 
| 1047      * be called when the user's program terminates. |  | 
| 1048      * |  | 
| 1049      * handle is the shl_load API handle for the shared library being |  | 
| 1050      * initialized. |  | 
| 1051      * |  | 
| 1052      * loading is non-zero at startup and zero at termination. |  | 
| 1053      */ |  | 
| 1054     if (loading) { |  | 
| 1055         /* ... do some initializations ... */ |  | 
| 1056     } else { |  | 
| 1057         _PR_Fini(); |  | 
| 1058     } |  | 
| 1059 } |  | 
| 1060 #endif |  | 
| 1061 #elif defined(AIX) |  | 
| 1062 /* Need to use the -binitfini::_PR_Fini linker option. */ |  | 
| 1063 #endif |  | 
| 1064 |  | 
| 1065 void _PR_Fini(void) |  | 
| 1066 { |  | 
| 1067     void *thred; |  | 
| 1068     int rv; |  | 
| 1069 |  | 
| 1070     if (!_pr_initialized) { |  | 
| 1071         /* Either NSPR was never successfully initialized or |  | 
| 1072          * PR_Cleanup has been called already. */ |  | 
| 1073         if (pt_book.keyCreated) |  | 
| 1074         { |  | 
| 1075             rv = pthread_key_delete(pt_book.key); |  | 
| 1076             PR_ASSERT(0 == rv); |  | 
| 1077             pt_book.keyCreated = PR_FALSE; |  | 
| 1078         } |  | 
| 1079         return; |  | 
| 1080     } |  | 
| 1081 |  | 
| 1082     _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); |  | 
| 1083     if (NULL != thred) |  | 
| 1084     { |  | 
| 1085         /* |  | 
| 1086          * PR_FALSE, because it is unsafe to call back to the |  | 
| 1087          * thread private data destructors at final cleanup. |  | 
| 1088          */ |  | 
| 1089         _pt_thread_death_internal(thred, PR_FALSE); |  | 
| 1090         rv = pthread_setspecific(pt_book.key, NULL); |  | 
| 1091         PR_ASSERT(0 == rv); |  | 
| 1092     } |  | 
| 1093     rv = pthread_key_delete(pt_book.key); |  | 
| 1094     PR_ASSERT(0 == rv); |  | 
| 1095     pt_book.keyCreated = PR_FALSE; |  | 
| 1096     /* TODO: free other resources used by NSPR */ |  | 
| 1097     /* _pr_initialized = PR_FALSE; */ |  | 
| 1098 }  /* _PR_Fini */ |  | 
| 1099 |  | 
| 1100 PR_IMPLEMENT(PRStatus) PR_Cleanup(void) |  | 
| 1101 { |  | 
| 1102     PRThread *me = PR_GetCurrentThread(); |  | 
| 1103     int rv; |  | 
| 1104     PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR")); |  | 
| 1105     PR_ASSERT(me->state & PT_THREAD_PRIMORD); |  | 
| 1106     if (me->state & PT_THREAD_PRIMORD) |  | 
| 1107     { |  | 
| 1108         PR_Lock(pt_book.ml); |  | 
| 1109         while (pt_book.user > pt_book.this_many) |  | 
| 1110             PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); |  | 
| 1111         if (me->state & PT_THREAD_SYSTEM) |  | 
| 1112             pt_book.system -= 1; |  | 
| 1113         else |  | 
| 1114             pt_book.user -= 1; |  | 
| 1115         PR_Unlock(pt_book.ml); |  | 
| 1116 |  | 
| 1117         _PR_MD_EARLY_CLEANUP(); |  | 
| 1118 |  | 
| 1119         _PR_CleanupMW(); |  | 
| 1120         _PR_CleanupTime(); |  | 
| 1121         _PR_CleanupDtoa(); |  | 
| 1122         _PR_CleanupCallOnce(); |  | 
| 1123         _PR_ShutdownLinker(); |  | 
| 1124         _PR_LogCleanup(); |  | 
| 1125         _PR_CleanupNet(); |  | 
| 1126         /* Close all the fd's before calling _PR_CleanupIO */ |  | 
| 1127         _PR_CleanupIO(); |  | 
| 1128         _PR_CleanupCMon(); |  | 
| 1129 |  | 
| 1130         _pt_thread_death(me); |  | 
| 1131         rv = pthread_setspecific(pt_book.key, NULL); |  | 
| 1132         PR_ASSERT(0 == rv); |  | 
| 1133         /* |  | 
| 1134          * I am not sure if it's safe to delete the cv and lock here, |  | 
| 1135          * since there may still be "system" threads around. If this |  | 
| 1136          * call isn't immediately prior to exiting, then there's a |  | 
| 1137          * problem. |  | 
| 1138          */ |  | 
| 1139         if (0 == pt_book.system) |  | 
| 1140         { |  | 
| 1141             PR_DestroyCondVar(pt_book.cv); pt_book.cv = NULL; |  | 
| 1142             PR_DestroyLock(pt_book.ml); pt_book.ml = NULL; |  | 
| 1143         } |  | 
| 1144         PR_DestroyLock(_pr_sleeplock); |  | 
| 1145         _pr_sleeplock = NULL; |  | 
| 1146         _PR_CleanupLayerCache(); |  | 
| 1147         _PR_CleanupEnv(); |  | 
| 1148 #ifdef _PR_ZONE_ALLOCATOR |  | 
| 1149         _PR_DestroyZones(); |  | 
| 1150 #endif |  | 
| 1151         _pr_initialized = PR_FALSE; |  | 
| 1152         return PR_SUCCESS; |  | 
| 1153     } |  | 
| 1154     return PR_FAILURE; |  | 
| 1155 }  /* PR_Cleanup */ |  | 
| 1156 |  | 
| 1157 PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status) |  | 
| 1158 { |  | 
| 1159     _exit(status); |  | 
| 1160 } |  | 
| 1161 |  | 
| 1162 PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thred) |  | 
| 1163 { |  | 
| 1164 #if defined(_PR_DCETHREADS) |  | 
| 1165     return (PRUint32)&thred->id;  /* this is really a sham! */ |  | 
| 1166 #else |  | 
| 1167     return (PRUint32)thred->id;  /* and I don't know what they will do with it *
      / |  | 
| 1168 #endif |  | 
| 1169 } |  | 
| 1170 |  | 
| 1171 /* |  | 
| 1172  * $$$ |  | 
| 1173  * The following two thread-to-processor affinity functions are not |  | 
| 1174  * yet implemented for pthreads.  By the way, these functions should return |  | 
| 1175  * PRStatus rather than PRInt32 to indicate the success/failure status. |  | 
| 1176  * $$$ |  | 
| 1177  */ |  | 
| 1178 |  | 
| 1179 PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask) |  | 
| 1180 { |  | 
| 1181     return 0;  /* not implemented */ |  | 
| 1182 } |  | 
| 1183 |  | 
| 1184 PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask ) |  | 
| 1185 { |  | 
| 1186     return 0;  /* not implemented */ |  | 
| 1187 } |  | 
| 1188 |  | 
| 1189 PR_IMPLEMENT(void) |  | 
| 1190 PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg) |  | 
| 1191 { |  | 
| 1192     thread->dump = dump; |  | 
| 1193     thread->dumpArg = arg; |  | 
| 1194 } |  | 
| 1195 |  | 
| 1196 /* |  | 
| 1197  * Garbage collection support follows. |  | 
| 1198  */ |  | 
| 1199 |  | 
| 1200 #if defined(_PR_DCETHREADS) |  | 
| 1201 |  | 
| 1202 /* |  | 
| 1203  * statics for Garbage Collection support.  We don't need to protect these |  | 
| 1204  * signal masks since the garbage collector itself is protected by a lock |  | 
| 1205  * and multiple threads will not be garbage collecting at the same time. |  | 
| 1206  */ |  | 
| 1207 static sigset_t javagc_vtalarm_sigmask; |  | 
| 1208 static sigset_t javagc_intsoff_sigmask; |  | 
| 1209 |  | 
| 1210 #else /* defined(_PR_DCETHREADS) */ |  | 
| 1211 |  | 
| 1212 /* a bogus signal mask for forcing a timed wait */ |  | 
| 1213 /* Not so bogus in AIX as we really do a sigwait */ |  | 
| 1214 static sigset_t sigwait_set; |  | 
| 1215 |  | 
| 1216 static struct timespec onemillisec = {0, 1000000L}; |  | 
| 1217 #ifndef PT_NO_SIGTIMEDWAIT |  | 
| 1218 static struct timespec hundredmillisec = {0, 100000000L}; |  | 
| 1219 #endif |  | 
| 1220 |  | 
| 1221 static void suspend_signal_handler(PRIntn sig); |  | 
| 1222 |  | 
| 1223 #ifdef PT_NO_SIGTIMEDWAIT |  | 
| 1224 static void null_signal_handler(PRIntn sig); |  | 
| 1225 #endif |  | 
| 1226 |  | 
| 1227 #endif /* defined(_PR_DCETHREADS) */ |  | 
| 1228 |  | 
| 1229 /* |  | 
| 1230  * Linux pthreads use SIGUSR1 and SIGUSR2 internally, which |  | 
| 1231  * conflict with the use of these two signals in our GC support. |  | 
| 1232  * So we don't know how to support GC on Linux pthreads. |  | 
| 1233  */ |  | 
| 1234 static void init_pthread_gc_support(void) |  | 
| 1235 { |  | 
| 1236 #ifndef SYMBIAN |  | 
| 1237     PRIntn rv; |  | 
| 1238 |  | 
| 1239 #if defined(_PR_DCETHREADS) |  | 
| 1240         rv = sigemptyset(&javagc_vtalarm_sigmask); |  | 
| 1241     PR_ASSERT(0 == rv); |  | 
| 1242         rv = sigaddset(&javagc_vtalarm_sigmask, SIGVTALRM); |  | 
| 1243     PR_ASSERT(0 == rv); |  | 
| 1244 #else  /* defined(_PR_DCETHREADS) */ |  | 
| 1245         { |  | 
| 1246             struct sigaction sigact_usr2; |  | 
| 1247 |  | 
| 1248             sigact_usr2.sa_handler = suspend_signal_handler; |  | 
| 1249             sigact_usr2.sa_flags = SA_RESTART; |  | 
| 1250             sigemptyset (&sigact_usr2.sa_mask); |  | 
| 1251 |  | 
| 1252         rv = sigaction (SIGUSR2, &sigact_usr2, NULL); |  | 
| 1253         PR_ASSERT(0 == rv); |  | 
| 1254 |  | 
| 1255         sigemptyset (&sigwait_set); |  | 
| 1256 #if defined(PT_NO_SIGTIMEDWAIT) |  | 
| 1257         sigaddset (&sigwait_set, SIGUSR1); |  | 
| 1258 #else |  | 
| 1259         sigaddset (&sigwait_set, SIGUSR2); |  | 
| 1260 #endif  /* defined(PT_NO_SIGTIMEDWAIT) */ |  | 
| 1261         } |  | 
| 1262 #if defined(PT_NO_SIGTIMEDWAIT) |  | 
| 1263         { |  | 
| 1264             struct sigaction sigact_null; |  | 
| 1265             sigact_null.sa_handler = null_signal_handler; |  | 
| 1266             sigact_null.sa_flags = SA_RESTART; |  | 
| 1267             sigemptyset (&sigact_null.sa_mask); |  | 
| 1268         rv = sigaction (SIGUSR1, &sigact_null, NULL); |  | 
| 1269             PR_ASSERT(0 ==rv); |  | 
| 1270     } |  | 
| 1271 #endif  /* defined(PT_NO_SIGTIMEDWAIT) */ |  | 
| 1272 #endif /* defined(_PR_DCETHREADS) */ |  | 
| 1273 #endif /* SYMBIAN */ |  | 
| 1274 } |  | 
| 1275 |  | 
| 1276 PR_IMPLEMENT(void) PR_SetThreadGCAble(void) |  | 
| 1277 { |  | 
| 1278     PR_Lock(pt_book.ml); |  | 
| 1279         PR_GetCurrentThread()->state |= PT_THREAD_GCABLE; |  | 
| 1280     PR_Unlock(pt_book.ml); |  | 
| 1281 } |  | 
| 1282 |  | 
| 1283 PR_IMPLEMENT(void) PR_ClearThreadGCAble(void) |  | 
| 1284 { |  | 
| 1285     PR_Lock(pt_book.ml); |  | 
| 1286         PR_GetCurrentThread()->state &= (~PT_THREAD_GCABLE); |  | 
| 1287     PR_Unlock(pt_book.ml); |  | 
| 1288 } |  | 
| 1289 |  | 
| 1290 #if defined(DEBUG) |  | 
| 1291 static PRBool suspendAllOn = PR_FALSE; |  | 
| 1292 #endif |  | 
| 1293 |  | 
| 1294 static PRBool suspendAllSuspended = PR_FALSE; |  | 
| 1295 |  | 
| 1296 PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg) |  | 
| 1297 { |  | 
| 1298     PRIntn count = 0; |  | 
| 1299     PRStatus rv = PR_SUCCESS; |  | 
| 1300     PRThread* thred = pt_book.first; |  | 
| 1301 |  | 
| 1302 #if defined(DEBUG) || defined(FORCE_PR_ASSERT) |  | 
| 1303 #if !defined(_PR_DCETHREADS) |  | 
| 1304     PRThread *me = PR_GetCurrentThread(); |  | 
| 1305 #endif |  | 
| 1306 #endif |  | 
| 1307 |  | 
| 1308     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_EnumerateThreads\n")); |  | 
| 1309     /* |  | 
| 1310      * $$$ |  | 
| 1311      * Need to suspend all threads other than me before doing this. |  | 
| 1312      * This is really a gross and disgusting thing to do. The only |  | 
| 1313      * good thing is that since all other threads are suspended, holding |  | 
| 1314      * the lock during a callback seems like child's play. |  | 
| 1315      * $$$ |  | 
| 1316      */ |  | 
| 1317     PR_ASSERT(suspendAllOn); |  | 
| 1318 |  | 
| 1319     while (thred != NULL) |  | 
| 1320     { |  | 
| 1321         /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking |  | 
| 1322          * qp->next after applying the function "func".  In particular, "func" |  | 
| 1323          * might remove the thread from the queue and put it into another one in |  | 
| 1324          * which case qp->next no longer points to the next entry in the origina
      l |  | 
| 1325          * queue. |  | 
| 1326          * |  | 
| 1327          * To get around this problem, we save qp->next in qp_next before applyi
      ng |  | 
| 1328          * "func" and use that saved value as the next value after applying "fun
      c". |  | 
| 1329          */ |  | 
| 1330         PRThread* next = thred->next; |  | 
| 1331 |  | 
| 1332         if (_PT_IS_GCABLE_THREAD(thred)) |  | 
| 1333         { |  | 
| 1334 #if !defined(_PR_DCETHREADS) |  | 
| 1335             PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED)); |  | 
| 1336 #endif |  | 
| 1337             PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1338                    ("In PR_EnumerateThreads callback thread %p thid = %X\n", |  | 
| 1339                     thred, thred->id)); |  | 
| 1340 |  | 
| 1341             rv = func(thred, count++, arg); |  | 
| 1342             if (rv != PR_SUCCESS) |  | 
| 1343                 return rv; |  | 
| 1344         } |  | 
| 1345         thred = next; |  | 
| 1346     } |  | 
| 1347     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1348            ("End PR_EnumerateThreads count = %d \n", count)); |  | 
| 1349     return rv; |  | 
| 1350 }  /* PR_EnumerateThreads */ |  | 
| 1351 |  | 
| 1352 /* |  | 
| 1353  * PR_SuspendAll and PR_ResumeAll are called during garbage collection.  The str
      ategy |  | 
| 1354  * we use is to send a SIGUSR2 signal to every gc able thread that we intend to 
      suspend. |  | 
| 1355  * The signal handler will record the stack pointer and will block until resumed
       by |  | 
| 1356  * the resume call.  Since the signal handler is the last routine called for the |  | 
| 1357  * suspended thread, the stack pointer will also serve as a place where all the |  | 
| 1358  * registers have been saved on the stack for the previously executing routines. |  | 
| 1359  * |  | 
| 1360  * Through global variables, we also make sure that PR_Suspend and PR_Resume doe
      s not |  | 
| 1361  * proceed until the thread is suspended or resumed. |  | 
| 1362  */ |  | 
| 1363 |  | 
| 1364 #if !defined(_PR_DCETHREADS) |  | 
| 1365 |  | 
| 1366 /* |  | 
| 1367  * In the signal handler, we can not use condition variable notify or wait. |  | 
| 1368  * This does not work consistently across all pthread platforms.  We also can no
      t |  | 
| 1369  * use locking since that does not seem to work reliably across platforms. |  | 
| 1370  * Only thing we can do is yielding while testing for a global condition |  | 
| 1371  * to change.  This does work on pthread supported platforms.  We may have |  | 
| 1372  * to play with priortities if there are any problems detected. |  | 
| 1373  */ |  | 
| 1374 |  | 
| 1375  /* |  | 
| 1376   * In AIX, you cannot use ANY pthread calls in the signal handler except perhap
      s |  | 
| 1377   * pthread_yield. But that is horribly inefficient. Hence we use only sigwait, 
      no |  | 
| 1378   * sigtimedwait is available. We need to use another user signal, SIGUSR1. Actu
      ally |  | 
| 1379   * SIGUSR1 is also used by exec in Java. So our usage here breaks the exec in J
      ava, |  | 
| 1380   * for AIX. You cannot use pthread_cond_wait or pthread_delay_np in the signal |  | 
| 1381   * handler as all synchronization mechanisms just break down. |  | 
| 1382   */ |  | 
| 1383 |  | 
| 1384 #if defined(PT_NO_SIGTIMEDWAIT) |  | 
| 1385 static void null_signal_handler(PRIntn sig) |  | 
| 1386 { |  | 
| 1387         return; |  | 
| 1388 } |  | 
| 1389 #endif |  | 
| 1390 |  | 
| 1391 static void suspend_signal_handler(PRIntn sig) |  | 
| 1392 { |  | 
| 1393         PRThread *me = PR_GetCurrentThread(); |  | 
| 1394 |  | 
| 1395         PR_ASSERT(me != NULL); |  | 
| 1396         PR_ASSERT(_PT_IS_GCABLE_THREAD(me)); |  | 
| 1397         PR_ASSERT((me->suspend & PT_THREAD_SUSPENDED) == 0); |  | 
| 1398 |  | 
| 1399         PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1400         ("Begin suspend_signal_handler thred %p thread id = %X\n", |  | 
| 1401                 me, me->id)); |  | 
| 1402 |  | 
| 1403         /* |  | 
| 1404          * save stack pointer |  | 
| 1405          */ |  | 
| 1406         me->sp = &me; |  | 
| 1407 |  | 
| 1408         /* |  | 
| 1409            At this point, the thread's stack pointer has been saved, |  | 
| 1410            And it is going to enter a wait loop until it is resumed. |  | 
| 1411            So it is _really_ suspended |  | 
| 1412         */ |  | 
| 1413 |  | 
| 1414         me->suspend |= PT_THREAD_SUSPENDED; |  | 
| 1415 |  | 
| 1416         /* |  | 
| 1417          * now, block current thread |  | 
| 1418          */ |  | 
| 1419 #if defined(PT_NO_SIGTIMEDWAIT) |  | 
| 1420         pthread_cond_signal(&me->suspendResumeCV); |  | 
| 1421         while (me->suspend & PT_THREAD_SUSPENDED) |  | 
| 1422         { |  | 
| 1423 #if !defined(FREEBSD) && !defined(NETBSD) && !defined(OPENBSD) \ |  | 
| 1424     && !defined(BSDI) && !defined(UNIXWARE) \ |  | 
| 1425     && !defined(DARWIN) && !defined(RISCOS) \ |  | 
| 1426     && !defined(SYMBIAN) /*XXX*/ |  | 
| 1427         PRIntn rv; |  | 
| 1428             sigwait(&sigwait_set, &rv); |  | 
| 1429 #endif |  | 
| 1430         } |  | 
| 1431         me->suspend |= PT_THREAD_RESUMED; |  | 
| 1432         pthread_cond_signal(&me->suspendResumeCV); |  | 
| 1433 #else /* defined(PT_NO_SIGTIMEDWAIT) */ |  | 
| 1434         while (me->suspend & PT_THREAD_SUSPENDED) |  | 
| 1435         { |  | 
| 1436                 PRIntn rv = sigtimedwait(&sigwait_set, NULL, &hundredmillisec); |  | 
| 1437         PR_ASSERT(-1 == rv); |  | 
| 1438         } |  | 
| 1439         me->suspend |= PT_THREAD_RESUMED; |  | 
| 1440 #endif |  | 
| 1441 |  | 
| 1442     /* |  | 
| 1443      * At this point, thread has been resumed, so set a global condition. |  | 
| 1444      * The ResumeAll needs to know that this has really been resumed. |  | 
| 1445      * So the signal handler sets a flag which PR_ResumeAll will reset. |  | 
| 1446      * The PR_ResumeAll must reset this flag ... |  | 
| 1447      */ |  | 
| 1448 |  | 
| 1449     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1450         ("End suspend_signal_handler thred = %p tid = %X\n", me, me->id)); |  | 
| 1451 }  /* suspend_signal_handler */ |  | 
| 1452 |  | 
| 1453 static void pt_SuspendSet(PRThread *thred) |  | 
| 1454 { |  | 
| 1455     PRIntn rv; |  | 
| 1456 |  | 
| 1457     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1458            ("pt_SuspendSet thred %p thread id = %X\n", thred, thred->id)); |  | 
| 1459 |  | 
| 1460 |  | 
| 1461     /* |  | 
| 1462      * Check the thread state and signal the thread to suspend |  | 
| 1463      */ |  | 
| 1464 |  | 
| 1465     PR_ASSERT((thred->suspend & PT_THREAD_SUSPENDED) == 0); |  | 
| 1466 |  | 
| 1467     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1468            ("doing pthread_kill in pt_SuspendSet thred %p tid = %X\n", |  | 
| 1469            thred, thred->id)); |  | 
| 1470 #if defined(SYMBIAN) |  | 
| 1471     /* All signal group functions are not implemented in Symbian OS */ |  | 
| 1472     rv = 0; |  | 
| 1473 #else |  | 
| 1474     rv = pthread_kill (thred->id, SIGUSR2); |  | 
| 1475 #endif |  | 
| 1476     PR_ASSERT(0 == rv); |  | 
| 1477 } |  | 
| 1478 |  | 
| 1479 static void pt_SuspendTest(PRThread *thred) |  | 
| 1480 { |  | 
| 1481     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1482            ("Begin pt_SuspendTest thred %p thread id = %X\n", thred, thred->id))
      ; |  | 
| 1483 |  | 
| 1484 |  | 
| 1485     /* |  | 
| 1486      * Wait for the thread to be really suspended. This happens when the |  | 
| 1487      * suspend signal handler stores the stack pointer and sets the state |  | 
| 1488      * to suspended. |  | 
| 1489      */ |  | 
| 1490 |  | 
| 1491 #if defined(PT_NO_SIGTIMEDWAIT) |  | 
| 1492     pthread_mutex_lock(&thred->suspendResumeMutex); |  | 
| 1493     while ((thred->suspend & PT_THREAD_SUSPENDED) == 0) |  | 
| 1494     { |  | 
| 1495             pthread_cond_timedwait( |  | 
| 1496                 &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillise
      c); |  | 
| 1497         } |  | 
| 1498         pthread_mutex_unlock(&thred->suspendResumeMutex); |  | 
| 1499 #else |  | 
| 1500     while ((thred->suspend & PT_THREAD_SUSPENDED) == 0) |  | 
| 1501     { |  | 
| 1502                 PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec); |  | 
| 1503         PR_ASSERT(-1 == rv); |  | 
| 1504         } |  | 
| 1505 #endif |  | 
| 1506 |  | 
| 1507     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1508         ("End pt_SuspendTest thred %p tid %X\n", thred, thred->id)); |  | 
| 1509 }  /* pt_SuspendTest */ |  | 
| 1510 |  | 
| 1511 static void pt_ResumeSet(PRThread *thred) |  | 
| 1512 { |  | 
| 1513     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1514            ("pt_ResumeSet thred %p thread id = %X\n", thred, thred->id)); |  | 
| 1515 |  | 
| 1516     /* |  | 
| 1517      * Clear the global state and set the thread state so that it will |  | 
| 1518      * continue past yield loop in the suspend signal handler |  | 
| 1519      */ |  | 
| 1520 |  | 
| 1521     PR_ASSERT(thred->suspend & PT_THREAD_SUSPENDED); |  | 
| 1522 |  | 
| 1523 |  | 
| 1524     thred->suspend &= ~PT_THREAD_SUSPENDED; |  | 
| 1525 |  | 
| 1526 #if defined(PT_NO_SIGTIMEDWAIT) |  | 
| 1527 #if defined(SYMBIAN) |  | 
| 1528         /* All signal group functions are not implemented in Symbian OS */ |  | 
| 1529 #else |  | 
| 1530         pthread_kill(thred->id, SIGUSR1); |  | 
| 1531 #endif |  | 
| 1532 #endif |  | 
| 1533 |  | 
| 1534 }  /* pt_ResumeSet */ |  | 
| 1535 |  | 
| 1536 static void pt_ResumeTest(PRThread *thred) |  | 
| 1537 { |  | 
| 1538     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1539            ("Begin pt_ResumeTest thred %p thread id = %X\n", thred, thred->id)); |  | 
| 1540 |  | 
| 1541     /* |  | 
| 1542      * Wait for the threads resume state to change |  | 
| 1543      * to indicate it is really resumed |  | 
| 1544      */ |  | 
| 1545 #if defined(PT_NO_SIGTIMEDWAIT) |  | 
| 1546     pthread_mutex_lock(&thred->suspendResumeMutex); |  | 
| 1547     while ((thred->suspend & PT_THREAD_RESUMED) == 0) |  | 
| 1548     { |  | 
| 1549             pthread_cond_timedwait( |  | 
| 1550                 &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillise
      c); |  | 
| 1551     } |  | 
| 1552     pthread_mutex_unlock(&thred->suspendResumeMutex); |  | 
| 1553 #else |  | 
| 1554     while ((thred->suspend & PT_THREAD_RESUMED) == 0) { |  | 
| 1555                 PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec); |  | 
| 1556         PR_ASSERT(-1 == rv); |  | 
| 1557         } |  | 
| 1558 #endif |  | 
| 1559 |  | 
| 1560     thred->suspend &= ~PT_THREAD_RESUMED; |  | 
| 1561 |  | 
| 1562     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ( |  | 
| 1563         "End pt_ResumeTest thred %p tid %X\n", thred, thred->id)); |  | 
| 1564 }  /* pt_ResumeTest */ |  | 
| 1565 |  | 
| 1566 static pthread_once_t pt_gc_support_control = PTHREAD_ONCE_INIT; |  | 
| 1567 |  | 
| 1568 PR_IMPLEMENT(void) PR_SuspendAll(void) |  | 
| 1569 { |  | 
| 1570 #ifdef DEBUG |  | 
| 1571     PRIntervalTime stime, etime; |  | 
| 1572 #endif |  | 
| 1573     PRThread* thred = pt_book.first; |  | 
| 1574     PRThread *me = PR_GetCurrentThread(); |  | 
| 1575     int rv; |  | 
| 1576 |  | 
| 1577     rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support); |  | 
| 1578     PR_ASSERT(0 == rv); |  | 
| 1579     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n")); |  | 
| 1580     /* |  | 
| 1581      * Stop all threads which are marked GC able. |  | 
| 1582      */ |  | 
| 1583     PR_Lock(pt_book.ml); |  | 
| 1584 #ifdef DEBUG |  | 
| 1585     suspendAllOn = PR_TRUE; |  | 
| 1586     stime = PR_IntervalNow(); |  | 
| 1587 #endif |  | 
| 1588     while (thred != NULL) |  | 
| 1589     { |  | 
| 1590             if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) |  | 
| 1591                 pt_SuspendSet(thred); |  | 
| 1592         thred = thred->next; |  | 
| 1593     } |  | 
| 1594 |  | 
| 1595     /* Wait till they are really suspended */ |  | 
| 1596     thred = pt_book.first; |  | 
| 1597     while (thred != NULL) |  | 
| 1598     { |  | 
| 1599             if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) |  | 
| 1600             pt_SuspendTest(thred); |  | 
| 1601         thred = thred->next; |  | 
| 1602     } |  | 
| 1603 |  | 
| 1604     suspendAllSuspended = PR_TRUE; |  | 
| 1605 |  | 
| 1606 #ifdef DEBUG |  | 
| 1607     etime = PR_IntervalNow(); |  | 
| 1608     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,\ |  | 
| 1609         ("End PR_SuspendAll (time %dms)\n", |  | 
| 1610         PR_IntervalToMilliseconds(etime - stime))); |  | 
| 1611 #endif |  | 
| 1612 }  /* PR_SuspendAll */ |  | 
| 1613 |  | 
| 1614 PR_IMPLEMENT(void) PR_ResumeAll(void) |  | 
| 1615 { |  | 
| 1616 #ifdef DEBUG |  | 
| 1617     PRIntervalTime stime, etime; |  | 
| 1618 #endif |  | 
| 1619     PRThread* thred = pt_book.first; |  | 
| 1620     PRThread *me = PR_GetCurrentThread(); |  | 
| 1621     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n")); |  | 
| 1622     /* |  | 
| 1623      * Resume all previously suspended GC able threads. |  | 
| 1624      */ |  | 
| 1625     suspendAllSuspended = PR_FALSE; |  | 
| 1626 #ifdef DEBUG |  | 
| 1627     stime = PR_IntervalNow(); |  | 
| 1628 #endif |  | 
| 1629 |  | 
| 1630     while (thred != NULL) |  | 
| 1631     { |  | 
| 1632             if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) |  | 
| 1633             pt_ResumeSet(thred); |  | 
| 1634         thred = thred->next; |  | 
| 1635     } |  | 
| 1636 |  | 
| 1637     thred = pt_book.first; |  | 
| 1638     while (thred != NULL) |  | 
| 1639     { |  | 
| 1640             if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) |  | 
| 1641             pt_ResumeTest(thred); |  | 
| 1642         thred = thred->next; |  | 
| 1643     } |  | 
| 1644 |  | 
| 1645     PR_Unlock(pt_book.ml); |  | 
| 1646 #ifdef DEBUG |  | 
| 1647     suspendAllOn = PR_FALSE; |  | 
| 1648     etime = PR_IntervalNow(); |  | 
| 1649     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1650         ("End PR_ResumeAll (time %dms)\n", |  | 
| 1651         PR_IntervalToMilliseconds(etime - stime))); |  | 
| 1652 #endif |  | 
| 1653 }  /* PR_ResumeAll */ |  | 
| 1654 |  | 
| 1655 /* Return the stack pointer for the given thread- used by the GC */ |  | 
| 1656 PR_IMPLEMENT(void *)PR_GetSP(PRThread *thred) |  | 
| 1657 { |  | 
| 1658     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, |  | 
| 1659             ("in PR_GetSP thred %p thid = %X, sp = %p\n", |  | 
| 1660             thred, thred->id, thred->sp)); |  | 
| 1661     return thred->sp; |  | 
| 1662 }  /* PR_GetSP */ |  | 
| 1663 |  | 
| 1664 #else /* !defined(_PR_DCETHREADS) */ |  | 
| 1665 |  | 
| 1666 static pthread_once_t pt_gc_support_control = pthread_once_init; |  | 
| 1667 |  | 
| 1668 /* |  | 
| 1669  * For DCE threads, there is no pthread_kill or a way of suspending or resuming 
      a |  | 
| 1670  * particular thread.  We will just disable the preemption (virtual timer alarm)
       and |  | 
| 1671  * let the executing thread finish the garbage collection.  This stops all other
       threads |  | 
| 1672  * (GC able or not) and is very inefficient but there is no other choice. |  | 
| 1673  */ |  | 
| 1674 PR_IMPLEMENT(void) PR_SuspendAll() |  | 
| 1675 { |  | 
| 1676     PRIntn rv; |  | 
| 1677 |  | 
| 1678     rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support); |  | 
| 1679     PR_ASSERT(0 == rv);  /* returns -1 on failure */ |  | 
| 1680 #ifdef DEBUG |  | 
| 1681     suspendAllOn = PR_TRUE; |  | 
| 1682 #endif |  | 
| 1683     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n")); |  | 
| 1684     /* |  | 
| 1685      * turn off preemption - i.e add virtual alarm signal to the set of |  | 
| 1686      * blocking signals |  | 
| 1687      */ |  | 
| 1688     rv = sigprocmask( |  | 
| 1689         SIG_BLOCK, &javagc_vtalarm_sigmask, &javagc_intsoff_sigmask); |  | 
| 1690     PR_ASSERT(0 == rv); |  | 
| 1691     suspendAllSuspended = PR_TRUE; |  | 
| 1692     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_SuspendAll\n")); |  | 
| 1693 }  /* PR_SuspendAll */ |  | 
| 1694 |  | 
| 1695 PR_IMPLEMENT(void) PR_ResumeAll() |  | 
| 1696 { |  | 
| 1697     PRIntn rv; |  | 
| 1698 |  | 
| 1699     suspendAllSuspended = PR_FALSE; |  | 
| 1700     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n")); |  | 
| 1701     /* turn on preemption - i.e re-enable virtual alarm signal */ |  | 
| 1702 |  | 
| 1703     rv = sigprocmask(SIG_SETMASK, &javagc_intsoff_sigmask, (sigset_t *)NULL); |  | 
| 1704     PR_ASSERT(0 == rv); |  | 
| 1705 #ifdef DEBUG |  | 
| 1706     suspendAllOn = PR_FALSE; |  | 
| 1707 #endif |  | 
| 1708 |  | 
| 1709     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_ResumeAll\n")); |  | 
| 1710 }  /* PR_ResumeAll */ |  | 
| 1711 |  | 
| 1712 /* Return the stack pointer for the given thread- used by the GC */ |  | 
| 1713 PR_IMPLEMENT(void*)PR_GetSP(PRThread *thred) |  | 
| 1714 { |  | 
| 1715         pthread_t tid = thred->id; |  | 
| 1716         char *thread_tcb, *top_sp; |  | 
| 1717 |  | 
| 1718         /* |  | 
| 1719          * For HPUX DCE threads, pthread_t is a struct with the |  | 
| 1720          * following three fields (see pthread.h, dce/cma.h): |  | 
| 1721          *     cma_t_address       field1; |  | 
| 1722          *     short int           field2; |  | 
| 1723          *     short int           field3; |  | 
| 1724          * where cma_t_address is typedef'd to be either void* |  | 
| 1725          * or char*. |  | 
| 1726          */ |  | 
| 1727         PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_GetSP\n")); |  | 
| 1728         thread_tcb = (char*)tid.field1; |  | 
| 1729         top_sp = *(char**)(thread_tcb + 128); |  | 
| 1730         PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_GetSP %p \n", top_sp)); |  | 
| 1731         return top_sp; |  | 
| 1732 }  /* PR_GetSP */ |  | 
| 1733 |  | 
| 1734 #endif /* !defined(_PR_DCETHREADS) */ |  | 
| 1735 |  | 
| 1736 PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name) |  | 
| 1737 { |  | 
| 1738     PRThread *thread; |  | 
| 1739     size_t nameLen; |  | 
| 1740     int result = 0; |  | 
| 1741 |  | 
| 1742     if (!name) { |  | 
| 1743         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |  | 
| 1744         return PR_FAILURE; |  | 
| 1745     } |  | 
| 1746 |  | 
| 1747     thread = PR_GetCurrentThread(); |  | 
| 1748     if (!thread) |  | 
| 1749         return PR_FAILURE; |  | 
| 1750 |  | 
| 1751     PR_Free(thread->name); |  | 
| 1752     nameLen = strlen(name); |  | 
| 1753     thread->name = (char *)PR_Malloc(nameLen + 1); |  | 
| 1754     if (!thread->name) |  | 
| 1755         return PR_FAILURE; |  | 
| 1756     memcpy(thread->name, name, nameLen + 1); |  | 
| 1757 |  | 
| 1758 #if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY) |  | 
| 1759     pthread_set_name_np(thread->id, name); |  | 
| 1760 #elif defined(NETBSD) |  | 
| 1761     result = pthread_setname_np(thread->id, "%s", (void *)name); |  | 
| 1762 #else /* not BSD */ |  | 
| 1763     /* |  | 
| 1764      * On OSX, pthread_setname_np is only available in 10.6 or later, so test |  | 
| 1765      * for it at runtime.  It also may not be available on all linux distros. |  | 
| 1766      */ |  | 
| 1767 #if defined(DARWIN) |  | 
| 1768     int (*dynamic_pthread_setname_np)(const char*); |  | 
| 1769 #else |  | 
| 1770     int (*dynamic_pthread_setname_np)(pthread_t, const char*); |  | 
| 1771 #endif |  | 
| 1772 |  | 
| 1773     *(void**)(&dynamic_pthread_setname_np) = |  | 
| 1774         dlsym(RTLD_DEFAULT, "pthread_setname_np"); |  | 
| 1775     if (!dynamic_pthread_setname_np) |  | 
| 1776         return PR_SUCCESS; |  | 
| 1777 |  | 
| 1778     /* |  | 
| 1779      * The 15-character name length limit is an experimentally determined |  | 
| 1780      * length of a null-terminated string that most linux distros and OS X |  | 
| 1781      * accept as an argument to pthread_setname_np.  Otherwise the E2BIG |  | 
| 1782      * error is returned by the function. |  | 
| 1783      */ |  | 
| 1784 #define SETNAME_LENGTH_CONSTRAINT 15 |  | 
| 1785 #define SETNAME_FRAGMENT1_LENGTH (SETNAME_LENGTH_CONSTRAINT >> 1) |  | 
| 1786 #define SETNAME_FRAGMENT2_LENGTH \ |  | 
| 1787     (SETNAME_LENGTH_CONSTRAINT - SETNAME_FRAGMENT1_LENGTH - 1) |  | 
| 1788     char name_dup[SETNAME_LENGTH_CONSTRAINT + 1]; |  | 
| 1789     if (nameLen > SETNAME_LENGTH_CONSTRAINT) { |  | 
| 1790         memcpy(name_dup, name, SETNAME_FRAGMENT1_LENGTH); |  | 
| 1791         name_dup[SETNAME_FRAGMENT1_LENGTH] = '~'; |  | 
| 1792         /* Note that this also copies the null terminator. */ |  | 
| 1793         memcpy(name_dup + SETNAME_FRAGMENT1_LENGTH + 1, |  | 
| 1794                name + nameLen - SETNAME_FRAGMENT2_LENGTH, |  | 
| 1795                SETNAME_FRAGMENT2_LENGTH + 1); |  | 
| 1796         name = name_dup; |  | 
| 1797     } |  | 
| 1798 |  | 
| 1799 #if defined(DARWIN) |  | 
| 1800     result = dynamic_pthread_setname_np(name); |  | 
| 1801 #else |  | 
| 1802     result = dynamic_pthread_setname_np(thread->id, name); |  | 
| 1803 #endif |  | 
| 1804 #endif /* not BSD */ |  | 
| 1805 |  | 
| 1806     if (result) { |  | 
| 1807         PR_SetError(PR_UNKNOWN_ERROR, result); |  | 
| 1808         return PR_FAILURE; |  | 
| 1809     } |  | 
| 1810     return PR_SUCCESS; |  | 
| 1811 } |  | 
| 1812 |  | 
| 1813 PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread) |  | 
| 1814 { |  | 
| 1815     if (!thread) |  | 
| 1816         return NULL; |  | 
| 1817     return thread->name; |  | 
| 1818 } |  | 
| 1819 |  | 
| 1820 #endif  /* defined(_PR_PTHREADS) || defined(_PR_DCETHREADS) */ |  | 
| 1821 |  | 
| 1822 /* ptthread.c */ |  | 
| OLD | NEW | 
|---|