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