nsprpub/pr/src/pthreads/ptthread.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial