| 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 |