1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/src/threads/combined/pruthr.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1889 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "primpl.h" 1.10 +#include <signal.h> 1.11 +#include <string.h> 1.12 + 1.13 +#if defined(WIN95) 1.14 +/* 1.15 +** Some local variables report warnings on Win95 because the code paths 1.16 +** using them are conditioned on HAVE_CUSTOME_USER_THREADS. 1.17 +** The pragma suppresses the warning. 1.18 +** 1.19 +*/ 1.20 +#pragma warning(disable : 4101) 1.21 +#endif 1.22 + 1.23 +/* _pr_activeLock protects the following global variables */ 1.24 +PRLock *_pr_activeLock; 1.25 +PRInt32 _pr_primordialExitCount; /* In PR_Cleanup(), the primordial thread 1.26 + * waits until all other user (non-system) 1.27 + * threads have terminated before it exits. 1.28 + * So whenever we decrement _pr_userActive, 1.29 + * it is compared with 1.30 + * _pr_primordialExitCount. 1.31 + * If the primordial thread is a system 1.32 + * thread, then _pr_primordialExitCount 1.33 + * is 0. If the primordial thread is 1.34 + * itself a user thread, then 1.35 + * _pr_primordialThread is 1. 1.36 + */ 1.37 +PRCondVar *_pr_primordialExitCVar; /* When _pr_userActive is decremented to 1.38 + * _pr_primordialExitCount, this condition 1.39 + * variable is notified. 1.40 + */ 1.41 + 1.42 +PRLock *_pr_deadQLock; 1.43 +PRUint32 _pr_numNativeDead; 1.44 +PRUint32 _pr_numUserDead; 1.45 +PRCList _pr_deadNativeQ; 1.46 +PRCList _pr_deadUserQ; 1.47 + 1.48 +PRUint32 _pr_join_counter; 1.49 + 1.50 +PRUint32 _pr_local_threads; 1.51 +PRUint32 _pr_global_threads; 1.52 + 1.53 +PRBool suspendAllOn = PR_FALSE; 1.54 +PRThread *suspendAllThread = NULL; 1.55 + 1.56 +extern PRCList _pr_active_global_threadQ; 1.57 +extern PRCList _pr_active_local_threadQ; 1.58 + 1.59 +static void _PR_DecrActiveThreadCount(PRThread *thread); 1.60 +static PRThread *_PR_AttachThread(PRThreadType, PRThreadPriority, PRThreadStack *); 1.61 +static void _PR_InitializeNativeStack(PRThreadStack *ts); 1.62 +static void _PR_InitializeRecycledThread(PRThread *thread); 1.63 +static void _PR_UserRunThread(void); 1.64 + 1.65 +void _PR_InitThreads(PRThreadType type, PRThreadPriority priority, 1.66 + PRUintn maxPTDs) 1.67 +{ 1.68 + PRThread *thread; 1.69 + PRThreadStack *stack; 1.70 + 1.71 + PR_ASSERT(priority == PR_PRIORITY_NORMAL); 1.72 + 1.73 + _pr_terminationCVLock = PR_NewLock(); 1.74 + _pr_activeLock = PR_NewLock(); 1.75 + 1.76 +#ifndef HAVE_CUSTOM_USER_THREADS 1.77 + stack = PR_NEWZAP(PRThreadStack); 1.78 +#ifdef HAVE_STACK_GROWING_UP 1.79 + stack->stackTop = (char*) ((((long)&type) >> _pr_pageShift) 1.80 + << _pr_pageShift); 1.81 +#else 1.82 +#if defined(SOLARIS) || defined (UNIXWARE) && defined (USR_SVR4_THREADS) 1.83 + stack->stackTop = (char*) &thread; 1.84 +#else 1.85 + stack->stackTop = (char*) ((((long)&type + _pr_pageSize - 1) 1.86 + >> _pr_pageShift) << _pr_pageShift); 1.87 +#endif 1.88 +#endif 1.89 +#else 1.90 + /* If stack is NULL, we're using custom user threads like NT fibers. */ 1.91 + stack = PR_NEWZAP(PRThreadStack); 1.92 + if (stack) { 1.93 + stack->stackSize = 0; 1.94 + _PR_InitializeNativeStack(stack); 1.95 + } 1.96 +#endif /* HAVE_CUSTOM_USER_THREADS */ 1.97 + 1.98 + thread = _PR_AttachThread(type, priority, stack); 1.99 + if (thread) { 1.100 + _PR_MD_SET_CURRENT_THREAD(thread); 1.101 + 1.102 + if (type == PR_SYSTEM_THREAD) { 1.103 + thread->flags = _PR_SYSTEM; 1.104 + _pr_systemActive++; 1.105 + _pr_primordialExitCount = 0; 1.106 + } else { 1.107 + _pr_userActive++; 1.108 + _pr_primordialExitCount = 1; 1.109 + } 1.110 + thread->no_sched = 1; 1.111 + _pr_primordialExitCVar = PR_NewCondVar(_pr_activeLock); 1.112 + } 1.113 + 1.114 + if (!thread) PR_Abort(); 1.115 +#ifdef _PR_LOCAL_THREADS_ONLY 1.116 + thread->flags |= _PR_PRIMORDIAL; 1.117 +#else 1.118 + thread->flags |= _PR_PRIMORDIAL | _PR_GLOBAL_SCOPE; 1.119 +#endif 1.120 + 1.121 + /* 1.122 + * Needs _PR_PRIMORDIAL flag set before calling 1.123 + * _PR_MD_INIT_THREAD() 1.124 + */ 1.125 + if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { 1.126 + /* 1.127 + * XXX do what? 1.128 + */ 1.129 + } 1.130 + 1.131 + if (_PR_IS_NATIVE_THREAD(thread)) { 1.132 + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ()); 1.133 + _pr_global_threads++; 1.134 + } else { 1.135 + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ()); 1.136 + _pr_local_threads++; 1.137 + } 1.138 + 1.139 + _pr_recycleThreads = 0; 1.140 + _pr_deadQLock = PR_NewLock(); 1.141 + _pr_numNativeDead = 0; 1.142 + _pr_numUserDead = 0; 1.143 + PR_INIT_CLIST(&_pr_deadNativeQ); 1.144 + PR_INIT_CLIST(&_pr_deadUserQ); 1.145 +} 1.146 + 1.147 +void _PR_CleanupThreads(void) 1.148 +{ 1.149 + if (_pr_terminationCVLock) { 1.150 + PR_DestroyLock(_pr_terminationCVLock); 1.151 + _pr_terminationCVLock = NULL; 1.152 + } 1.153 + if (_pr_activeLock) { 1.154 + PR_DestroyLock(_pr_activeLock); 1.155 + _pr_activeLock = NULL; 1.156 + } 1.157 + if (_pr_primordialExitCVar) { 1.158 + PR_DestroyCondVar(_pr_primordialExitCVar); 1.159 + _pr_primordialExitCVar = NULL; 1.160 + } 1.161 + /* TODO _pr_dead{Native,User}Q need to be deleted */ 1.162 + if (_pr_deadQLock) { 1.163 + PR_DestroyLock(_pr_deadQLock); 1.164 + _pr_deadQLock = NULL; 1.165 + } 1.166 +} 1.167 + 1.168 +/* 1.169 +** Initialize a stack for a native thread 1.170 +*/ 1.171 +static void _PR_InitializeNativeStack(PRThreadStack *ts) 1.172 +{ 1.173 + if( ts && (ts->stackTop == 0) ) { 1.174 + ts->allocSize = ts->stackSize; 1.175 + 1.176 + /* 1.177 + ** Setup stackTop and stackBottom values. 1.178 + */ 1.179 +#ifdef HAVE_STACK_GROWING_UP 1.180 + ts->allocBase = (char*) ((((long)&ts) >> _pr_pageShift) 1.181 + << _pr_pageShift); 1.182 + ts->stackBottom = ts->allocBase + ts->stackSize; 1.183 + ts->stackTop = ts->allocBase; 1.184 +#else 1.185 + ts->allocBase = (char*) ((((long)&ts + _pr_pageSize - 1) 1.186 + >> _pr_pageShift) << _pr_pageShift); 1.187 + ts->stackTop = ts->allocBase; 1.188 + ts->stackBottom = ts->allocBase - ts->stackSize; 1.189 +#endif 1.190 + } 1.191 +} 1.192 + 1.193 +void _PR_NotifyJoinWaiters(PRThread *thread) 1.194 +{ 1.195 + /* 1.196 + ** Handle joinable threads. Change the state to waiting for join. 1.197 + ** Remove from our run Q and put it on global waiting to join Q. 1.198 + ** Notify on our "termination" condition variable so that joining 1.199 + ** thread will know about our termination. Switch our context and 1.200 + ** come back later on to continue the cleanup. 1.201 + */ 1.202 + PR_ASSERT(thread == _PR_MD_CURRENT_THREAD()); 1.203 + if (thread->term != NULL) { 1.204 + PR_Lock(_pr_terminationCVLock); 1.205 + _PR_THREAD_LOCK(thread); 1.206 + thread->state = _PR_JOIN_WAIT; 1.207 + if ( !_PR_IS_NATIVE_THREAD(thread) ) { 1.208 + _PR_MISCQ_LOCK(thread->cpu); 1.209 + _PR_ADD_JOINQ(thread, thread->cpu); 1.210 + _PR_MISCQ_UNLOCK(thread->cpu); 1.211 + } 1.212 + _PR_THREAD_UNLOCK(thread); 1.213 + PR_NotifyCondVar(thread->term); 1.214 + PR_Unlock(_pr_terminationCVLock); 1.215 + _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT); 1.216 + PR_ASSERT(thread->state != _PR_JOIN_WAIT); 1.217 + } 1.218 + 1.219 +} 1.220 + 1.221 +/* 1.222 + * Zero some of the data members of a recycled thread. 1.223 + * 1.224 + * Note that we can do this either when a dead thread is added to 1.225 + * the dead thread queue or when it is reused. Here, we are doing 1.226 + * this lazily, when the thread is reused in _PR_CreateThread(). 1.227 + */ 1.228 +static void _PR_InitializeRecycledThread(PRThread *thread) 1.229 +{ 1.230 + /* 1.231 + * Assert that the following data members are already zeroed 1.232 + * by _PR_CleanupThread(). 1.233 + */ 1.234 +#ifdef DEBUG 1.235 + if (thread->privateData) { 1.236 + unsigned int i; 1.237 + for (i = 0; i < thread->tpdLength; i++) { 1.238 + PR_ASSERT(thread->privateData[i] == NULL); 1.239 + } 1.240 + } 1.241 +#endif 1.242 + PR_ASSERT(thread->dumpArg == 0 && thread->dump == 0); 1.243 + PR_ASSERT(thread->errorString == 0 && thread->errorStringSize == 0); 1.244 + PR_ASSERT(thread->errorStringLength == 0); 1.245 + PR_ASSERT(thread->name == 0); 1.246 + 1.247 + /* Reset data members in thread structure */ 1.248 + thread->errorCode = thread->osErrorCode = 0; 1.249 + thread->io_pending = thread->io_suspended = PR_FALSE; 1.250 + thread->environment = 0; 1.251 + PR_INIT_CLIST(&thread->lockList); 1.252 +} 1.253 + 1.254 +PRStatus _PR_RecycleThread(PRThread *thread) 1.255 +{ 1.256 + if ( _PR_IS_NATIVE_THREAD(thread) && 1.257 + _PR_NUM_DEADNATIVE < _pr_recycleThreads) { 1.258 + _PR_DEADQ_LOCK; 1.259 + PR_APPEND_LINK(&thread->links, &_PR_DEADNATIVEQ); 1.260 + _PR_INC_DEADNATIVE; 1.261 + _PR_DEADQ_UNLOCK; 1.262 + return (PR_SUCCESS); 1.263 + } else if ( !_PR_IS_NATIVE_THREAD(thread) && 1.264 + _PR_NUM_DEADUSER < _pr_recycleThreads) { 1.265 + _PR_DEADQ_LOCK; 1.266 + PR_APPEND_LINK(&thread->links, &_PR_DEADUSERQ); 1.267 + _PR_INC_DEADUSER; 1.268 + _PR_DEADQ_UNLOCK; 1.269 + return (PR_SUCCESS); 1.270 + } 1.271 + return (PR_FAILURE); 1.272 +} 1.273 + 1.274 +/* 1.275 + * Decrement the active thread count, either _pr_systemActive or 1.276 + * _pr_userActive, depending on whether the thread is a system thread 1.277 + * or a user thread. If all the user threads, except possibly 1.278 + * the primordial thread, have terminated, we notify the primordial 1.279 + * thread of this condition. 1.280 + * 1.281 + * Since this function will lock _pr_activeLock, do not call this 1.282 + * function while holding the _pr_activeLock lock, as this will result 1.283 + * in a deadlock. 1.284 + */ 1.285 + 1.286 +static void 1.287 +_PR_DecrActiveThreadCount(PRThread *thread) 1.288 +{ 1.289 + PR_Lock(_pr_activeLock); 1.290 + if (thread->flags & _PR_SYSTEM) { 1.291 + _pr_systemActive--; 1.292 + } else { 1.293 + _pr_userActive--; 1.294 + if (_pr_userActive == _pr_primordialExitCount) { 1.295 + PR_NotifyCondVar(_pr_primordialExitCVar); 1.296 + } 1.297 + } 1.298 + PR_Unlock(_pr_activeLock); 1.299 +} 1.300 + 1.301 +/* 1.302 +** Detach thread structure 1.303 +*/ 1.304 +static void 1.305 +_PR_DestroyThread(PRThread *thread) 1.306 +{ 1.307 + _PR_MD_FREE_LOCK(&thread->threadLock); 1.308 + PR_DELETE(thread); 1.309 +} 1.310 + 1.311 +void 1.312 +_PR_NativeDestroyThread(PRThread *thread) 1.313 +{ 1.314 + if(thread->term) { 1.315 + PR_DestroyCondVar(thread->term); 1.316 + thread->term = 0; 1.317 + } 1.318 + if (NULL != thread->privateData) { 1.319 + PR_ASSERT(0 != thread->tpdLength); 1.320 + PR_DELETE(thread->privateData); 1.321 + thread->tpdLength = 0; 1.322 + } 1.323 + PR_DELETE(thread->stack); 1.324 + _PR_DestroyThread(thread); 1.325 +} 1.326 + 1.327 +void 1.328 +_PR_UserDestroyThread(PRThread *thread) 1.329 +{ 1.330 + if(thread->term) { 1.331 + PR_DestroyCondVar(thread->term); 1.332 + thread->term = 0; 1.333 + } 1.334 + if (NULL != thread->privateData) { 1.335 + PR_ASSERT(0 != thread->tpdLength); 1.336 + PR_DELETE(thread->privateData); 1.337 + thread->tpdLength = 0; 1.338 + } 1.339 + _PR_MD_FREE_LOCK(&thread->threadLock); 1.340 + if (thread->threadAllocatedOnStack == 1) { 1.341 + _PR_MD_CLEAN_THREAD(thread); 1.342 + /* 1.343 + * Because the no_sched field is set, this thread/stack will 1.344 + * will not be re-used until the flag is cleared by the thread 1.345 + * we will context switch to. 1.346 + */ 1.347 + _PR_FreeStack(thread->stack); 1.348 + } else { 1.349 +#ifdef WINNT 1.350 + _PR_MD_CLEAN_THREAD(thread); 1.351 +#else 1.352 + /* 1.353 + * This assertion does not apply to NT. On NT, every fiber 1.354 + * has its threadAllocatedOnStack equal to 0. Elsewhere, 1.355 + * only the primordial thread has its threadAllocatedOnStack 1.356 + * equal to 0. 1.357 + */ 1.358 + PR_ASSERT(thread->flags & _PR_PRIMORDIAL); 1.359 +#endif 1.360 + } 1.361 +} 1.362 + 1.363 + 1.364 +/* 1.365 +** Run a thread's start function. When the start function returns the 1.366 +** thread is done executing and no longer needs the CPU. If there are no 1.367 +** more user threads running then we can exit the program. 1.368 +*/ 1.369 +void _PR_NativeRunThread(void *arg) 1.370 +{ 1.371 + PRThread *thread = (PRThread *)arg; 1.372 + 1.373 + _PR_MD_SET_CURRENT_THREAD(thread); 1.374 + 1.375 + _PR_MD_SET_CURRENT_CPU(NULL); 1.376 + 1.377 + /* Set up the thread stack information */ 1.378 + _PR_InitializeNativeStack(thread->stack); 1.379 + 1.380 + /* Set up the thread md information */ 1.381 + if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { 1.382 + /* 1.383 + * thread failed to initialize itself, possibly due to 1.384 + * failure to allocate per-thread resources 1.385 + */ 1.386 + return; 1.387 + } 1.388 + 1.389 + while(1) { 1.390 + thread->state = _PR_RUNNING; 1.391 + 1.392 + /* 1.393 + * Add to list of active threads 1.394 + */ 1.395 + PR_Lock(_pr_activeLock); 1.396 + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ()); 1.397 + _pr_global_threads++; 1.398 + PR_Unlock(_pr_activeLock); 1.399 + 1.400 + (*thread->startFunc)(thread->arg); 1.401 + 1.402 + /* 1.403 + * The following two assertions are meant for NT asynch io. 1.404 + * 1.405 + * The thread should have no asynch io in progress when it 1.406 + * exits, otherwise the overlapped buffer, which is part of 1.407 + * the thread structure, would become invalid. 1.408 + */ 1.409 + PR_ASSERT(thread->io_pending == PR_FALSE); 1.410 + /* 1.411 + * This assertion enforces the programming guideline that 1.412 + * if an io function times out or is interrupted, the thread 1.413 + * should close the fd to force the asynch io to abort 1.414 + * before it exits. Right now, closing the fd is the only 1.415 + * way to clear the io_suspended flag. 1.416 + */ 1.417 + PR_ASSERT(thread->io_suspended == PR_FALSE); 1.418 + 1.419 + /* 1.420 + * remove thread from list of active threads 1.421 + */ 1.422 + PR_Lock(_pr_activeLock); 1.423 + PR_REMOVE_LINK(&thread->active); 1.424 + _pr_global_threads--; 1.425 + PR_Unlock(_pr_activeLock); 1.426 + 1.427 + PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting")); 1.428 + 1.429 + /* All done, time to go away */ 1.430 + _PR_CleanupThread(thread); 1.431 + 1.432 + _PR_NotifyJoinWaiters(thread); 1.433 + 1.434 + _PR_DecrActiveThreadCount(thread); 1.435 + 1.436 + thread->state = _PR_DEAD_STATE; 1.437 + 1.438 + if (!_pr_recycleThreads || (_PR_RecycleThread(thread) == 1.439 + PR_FAILURE)) { 1.440 + /* 1.441 + * thread not recycled 1.442 + * platform-specific thread exit processing 1.443 + * - for stuff like releasing native-thread resources, etc. 1.444 + */ 1.445 + _PR_MD_EXIT_THREAD(thread); 1.446 + /* 1.447 + * Free memory allocated for the thread 1.448 + */ 1.449 + _PR_NativeDestroyThread(thread); 1.450 + /* 1.451 + * thread gone, cannot de-reference thread now 1.452 + */ 1.453 + return; 1.454 + } 1.455 + 1.456 + /* Now wait for someone to activate us again... */ 1.457 + _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT); 1.458 + } 1.459 +} 1.460 + 1.461 +static void _PR_UserRunThread(void) 1.462 +{ 1.463 + PRThread *thread = _PR_MD_CURRENT_THREAD(); 1.464 + PRIntn is; 1.465 + 1.466 + if (_MD_LAST_THREAD()) 1.467 + _MD_LAST_THREAD()->no_sched = 0; 1.468 + 1.469 +#ifdef HAVE_CUSTOM_USER_THREADS 1.470 + if (thread->stack == NULL) { 1.471 + thread->stack = PR_NEWZAP(PRThreadStack); 1.472 + _PR_InitializeNativeStack(thread->stack); 1.473 + } 1.474 +#endif /* HAVE_CUSTOM_USER_THREADS */ 1.475 + 1.476 + while(1) { 1.477 + /* Run thread main */ 1.478 + if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_MD_SET_INTSOFF(0); 1.479 + 1.480 + /* 1.481 + * Add to list of active threads 1.482 + */ 1.483 + if (!(thread->flags & _PR_IDLE_THREAD)) { 1.484 + PR_Lock(_pr_activeLock); 1.485 + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ()); 1.486 + _pr_local_threads++; 1.487 + PR_Unlock(_pr_activeLock); 1.488 + } 1.489 + 1.490 + (*thread->startFunc)(thread->arg); 1.491 + 1.492 + /* 1.493 + * The following two assertions are meant for NT asynch io. 1.494 + * 1.495 + * The thread should have no asynch io in progress when it 1.496 + * exits, otherwise the overlapped buffer, which is part of 1.497 + * the thread structure, would become invalid. 1.498 + */ 1.499 + PR_ASSERT(thread->io_pending == PR_FALSE); 1.500 + /* 1.501 + * This assertion enforces the programming guideline that 1.502 + * if an io function times out or is interrupted, the thread 1.503 + * should close the fd to force the asynch io to abort 1.504 + * before it exits. Right now, closing the fd is the only 1.505 + * way to clear the io_suspended flag. 1.506 + */ 1.507 + PR_ASSERT(thread->io_suspended == PR_FALSE); 1.508 + 1.509 + PR_Lock(_pr_activeLock); 1.510 + /* 1.511 + * remove thread from list of active threads 1.512 + */ 1.513 + if (!(thread->flags & _PR_IDLE_THREAD)) { 1.514 + PR_REMOVE_LINK(&thread->active); 1.515 + _pr_local_threads--; 1.516 + } 1.517 + PR_Unlock(_pr_activeLock); 1.518 + PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting")); 1.519 + 1.520 + /* All done, time to go away */ 1.521 + _PR_CleanupThread(thread); 1.522 + 1.523 + _PR_INTSOFF(is); 1.524 + 1.525 + _PR_NotifyJoinWaiters(thread); 1.526 + 1.527 + _PR_DecrActiveThreadCount(thread); 1.528 + 1.529 + thread->state = _PR_DEAD_STATE; 1.530 + 1.531 + if (!_pr_recycleThreads || (_PR_RecycleThread(thread) == 1.532 + PR_FAILURE)) { 1.533 + /* 1.534 + ** Destroy the thread resources 1.535 + */ 1.536 + _PR_UserDestroyThread(thread); 1.537 + } 1.538 + 1.539 + /* 1.540 + ** Find another user thread to run. This cpu has finished the 1.541 + ** previous threads main and is now ready to run another thread. 1.542 + */ 1.543 + { 1.544 + PRInt32 is; 1.545 + _PR_INTSOFF(is); 1.546 + _PR_MD_SWITCH_CONTEXT(thread); 1.547 + } 1.548 + 1.549 + /* Will land here when we get scheduled again if we are recycling... */ 1.550 + } 1.551 +} 1.552 + 1.553 +void _PR_SetThreadPriority(PRThread *thread, PRThreadPriority newPri) 1.554 +{ 1.555 + PRThread *me = _PR_MD_CURRENT_THREAD(); 1.556 + PRIntn is; 1.557 + 1.558 + if ( _PR_IS_NATIVE_THREAD(thread) ) { 1.559 + _PR_MD_SET_PRIORITY(&(thread->md), newPri); 1.560 + return; 1.561 + } 1.562 + 1.563 + if (!_PR_IS_NATIVE_THREAD(me)) 1.564 + _PR_INTSOFF(is); 1.565 + _PR_THREAD_LOCK(thread); 1.566 + if (newPri != thread->priority) { 1.567 + _PRCPU *cpu = thread->cpu; 1.568 + 1.569 + switch (thread->state) { 1.570 + case _PR_RUNNING: 1.571 + /* Change my priority */ 1.572 + 1.573 + _PR_RUNQ_LOCK(cpu); 1.574 + thread->priority = newPri; 1.575 + if (_PR_RUNQREADYMASK(cpu) >> (newPri + 1)) { 1.576 + if (!_PR_IS_NATIVE_THREAD(me)) 1.577 + _PR_SET_RESCHED_FLAG(); 1.578 + } 1.579 + _PR_RUNQ_UNLOCK(cpu); 1.580 + break; 1.581 + 1.582 + case _PR_RUNNABLE: 1.583 + 1.584 + _PR_RUNQ_LOCK(cpu); 1.585 + /* Move to different runQ */ 1.586 + _PR_DEL_RUNQ(thread); 1.587 + thread->priority = newPri; 1.588 + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); 1.589 + _PR_ADD_RUNQ(thread, cpu, newPri); 1.590 + _PR_RUNQ_UNLOCK(cpu); 1.591 + 1.592 + if (newPri > me->priority) { 1.593 + if (!_PR_IS_NATIVE_THREAD(me)) 1.594 + _PR_SET_RESCHED_FLAG(); 1.595 + } 1.596 + 1.597 + break; 1.598 + 1.599 + case _PR_LOCK_WAIT: 1.600 + case _PR_COND_WAIT: 1.601 + case _PR_IO_WAIT: 1.602 + case _PR_SUSPENDED: 1.603 + 1.604 + thread->priority = newPri; 1.605 + break; 1.606 + } 1.607 + } 1.608 + _PR_THREAD_UNLOCK(thread); 1.609 + if (!_PR_IS_NATIVE_THREAD(me)) 1.610 + _PR_INTSON(is); 1.611 +} 1.612 + 1.613 +/* 1.614 +** Suspend the named thread and copy its gc registers into regBuf 1.615 +*/ 1.616 +static void _PR_Suspend(PRThread *thread) 1.617 +{ 1.618 + PRIntn is; 1.619 + PRThread *me = _PR_MD_CURRENT_THREAD(); 1.620 + 1.621 + PR_ASSERT(thread != me); 1.622 + PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread) || (!thread->cpu)); 1.623 + 1.624 + if (!_PR_IS_NATIVE_THREAD(me)) 1.625 + _PR_INTSOFF(is); 1.626 + _PR_THREAD_LOCK(thread); 1.627 + switch (thread->state) { 1.628 + case _PR_RUNNABLE: 1.629 + if (!_PR_IS_NATIVE_THREAD(thread)) { 1.630 + _PR_RUNQ_LOCK(thread->cpu); 1.631 + _PR_DEL_RUNQ(thread); 1.632 + _PR_RUNQ_UNLOCK(thread->cpu); 1.633 + 1.634 + _PR_MISCQ_LOCK(thread->cpu); 1.635 + _PR_ADD_SUSPENDQ(thread, thread->cpu); 1.636 + _PR_MISCQ_UNLOCK(thread->cpu); 1.637 + } else { 1.638 + /* 1.639 + * Only LOCAL threads are suspended by _PR_Suspend 1.640 + */ 1.641 + PR_ASSERT(0); 1.642 + } 1.643 + thread->state = _PR_SUSPENDED; 1.644 + break; 1.645 + 1.646 + case _PR_RUNNING: 1.647 + /* 1.648 + * The thread being suspended should be a LOCAL thread with 1.649 + * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state 1.650 + */ 1.651 + PR_ASSERT(0); 1.652 + break; 1.653 + 1.654 + case _PR_LOCK_WAIT: 1.655 + case _PR_IO_WAIT: 1.656 + case _PR_COND_WAIT: 1.657 + if (_PR_IS_NATIVE_THREAD(thread)) { 1.658 + _PR_MD_SUSPEND_THREAD(thread); 1.659 + } 1.660 + thread->flags |= _PR_SUSPENDING; 1.661 + break; 1.662 + 1.663 + default: 1.664 + PR_Abort(); 1.665 + } 1.666 + _PR_THREAD_UNLOCK(thread); 1.667 + if (!_PR_IS_NATIVE_THREAD(me)) 1.668 + _PR_INTSON(is); 1.669 +} 1.670 + 1.671 +static void _PR_Resume(PRThread *thread) 1.672 +{ 1.673 + PRThreadPriority pri; 1.674 + PRIntn is; 1.675 + PRThread *me = _PR_MD_CURRENT_THREAD(); 1.676 + 1.677 + if (!_PR_IS_NATIVE_THREAD(me)) 1.678 + _PR_INTSOFF(is); 1.679 + _PR_THREAD_LOCK(thread); 1.680 + switch (thread->state) { 1.681 + case _PR_SUSPENDED: 1.682 + thread->state = _PR_RUNNABLE; 1.683 + thread->flags &= ~_PR_SUSPENDING; 1.684 + if (!_PR_IS_NATIVE_THREAD(thread)) { 1.685 + _PR_MISCQ_LOCK(thread->cpu); 1.686 + _PR_DEL_SUSPENDQ(thread); 1.687 + _PR_MISCQ_UNLOCK(thread->cpu); 1.688 + 1.689 + pri = thread->priority; 1.690 + 1.691 + _PR_RUNQ_LOCK(thread->cpu); 1.692 + _PR_ADD_RUNQ(thread, thread->cpu, pri); 1.693 + _PR_RUNQ_UNLOCK(thread->cpu); 1.694 + 1.695 + if (pri > _PR_MD_CURRENT_THREAD()->priority) { 1.696 + if (!_PR_IS_NATIVE_THREAD(me)) 1.697 + _PR_SET_RESCHED_FLAG(); 1.698 + } 1.699 + } else { 1.700 + PR_ASSERT(0); 1.701 + } 1.702 + break; 1.703 + 1.704 + case _PR_IO_WAIT: 1.705 + case _PR_COND_WAIT: 1.706 + thread->flags &= ~_PR_SUSPENDING; 1.707 +/* PR_ASSERT(thread->wait.monitor->stickyCount == 0); */ 1.708 + break; 1.709 + 1.710 + case _PR_LOCK_WAIT: 1.711 + { 1.712 + PRLock *wLock = thread->wait.lock; 1.713 + 1.714 + thread->flags &= ~_PR_SUSPENDING; 1.715 + 1.716 + _PR_LOCK_LOCK(wLock); 1.717 + if (thread->wait.lock->owner == 0) { 1.718 + _PR_UnblockLockWaiter(thread->wait.lock); 1.719 + } 1.720 + _PR_LOCK_UNLOCK(wLock); 1.721 + break; 1.722 + } 1.723 + case _PR_RUNNABLE: 1.724 + break; 1.725 + case _PR_RUNNING: 1.726 + /* 1.727 + * The thread being suspended should be a LOCAL thread with 1.728 + * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state 1.729 + */ 1.730 + PR_ASSERT(0); 1.731 + break; 1.732 + 1.733 + default: 1.734 + /* 1.735 + * thread should have been in one of the above-listed blocked states 1.736 + * (_PR_JOIN_WAIT, _PR_IO_WAIT, _PR_UNBORN, _PR_DEAD_STATE) 1.737 + */ 1.738 + PR_Abort(); 1.739 + } 1.740 + _PR_THREAD_UNLOCK(thread); 1.741 + if (!_PR_IS_NATIVE_THREAD(me)) 1.742 + _PR_INTSON(is); 1.743 + 1.744 +} 1.745 + 1.746 +#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) 1.747 +static PRThread *get_thread(_PRCPU *cpu, PRBool *wakeup_cpus) 1.748 +{ 1.749 + PRThread *thread; 1.750 + PRIntn pri; 1.751 + PRUint32 r; 1.752 + PRCList *qp; 1.753 + PRIntn priMin, priMax; 1.754 + 1.755 + _PR_RUNQ_LOCK(cpu); 1.756 + r = _PR_RUNQREADYMASK(cpu); 1.757 + if (r==0) { 1.758 + priMin = priMax = PR_PRIORITY_FIRST; 1.759 + } else if (r == (1<<PR_PRIORITY_NORMAL) ) { 1.760 + priMin = priMax = PR_PRIORITY_NORMAL; 1.761 + } else { 1.762 + priMin = PR_PRIORITY_FIRST; 1.763 + priMax = PR_PRIORITY_LAST; 1.764 + } 1.765 + thread = NULL; 1.766 + for (pri = priMax; pri >= priMin ; pri-- ) { 1.767 + if (r & (1 << pri)) { 1.768 + for (qp = _PR_RUNQ(cpu)[pri].next; 1.769 + qp != &_PR_RUNQ(cpu)[pri]; 1.770 + qp = qp->next) { 1.771 + thread = _PR_THREAD_PTR(qp); 1.772 + /* 1.773 + * skip non-schedulable threads 1.774 + */ 1.775 + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); 1.776 + if (thread->no_sched) { 1.777 + thread = NULL; 1.778 + /* 1.779 + * Need to wakeup cpus to avoid missing a 1.780 + * runnable thread 1.781 + * Waking up all CPU's need happen only once. 1.782 + */ 1.783 + 1.784 + *wakeup_cpus = PR_TRUE; 1.785 + continue; 1.786 + } else if (thread->flags & _PR_BOUND_THREAD) { 1.787 + /* 1.788 + * Thread bound to cpu 0 1.789 + */ 1.790 + 1.791 + thread = NULL; 1.792 +#ifdef IRIX 1.793 + _PR_MD_WAKEUP_PRIMORDIAL_CPU(); 1.794 +#endif 1.795 + continue; 1.796 + } else if (thread->io_pending == PR_TRUE) { 1.797 + /* 1.798 + * A thread that is blocked for I/O needs to run 1.799 + * on the same cpu on which it was blocked. This is because 1.800 + * the cpu's ioq is accessed without lock protection and scheduling 1.801 + * the thread on a different cpu would preclude this optimization. 1.802 + */ 1.803 + thread = NULL; 1.804 + continue; 1.805 + } else { 1.806 + /* Pull thread off of its run queue */ 1.807 + _PR_DEL_RUNQ(thread); 1.808 + _PR_RUNQ_UNLOCK(cpu); 1.809 + return(thread); 1.810 + } 1.811 + } 1.812 + } 1.813 + thread = NULL; 1.814 + } 1.815 + _PR_RUNQ_UNLOCK(cpu); 1.816 + return(thread); 1.817 +} 1.818 +#endif /* !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) */ 1.819 + 1.820 +/* 1.821 +** Schedule this native thread by finding the highest priority nspr 1.822 +** thread that is ready to run. 1.823 +** 1.824 +** Note- everyone really needs to call _PR_MD_SWITCH_CONTEXT (which calls 1.825 +** PR_Schedule() rather than calling PR_Schedule. Otherwise if there 1.826 +** is initialization required for switching from SWITCH_CONTEXT, 1.827 +** it will not get done! 1.828 +*/ 1.829 +void _PR_Schedule(void) 1.830 +{ 1.831 + PRThread *thread, *me = _PR_MD_CURRENT_THREAD(); 1.832 + _PRCPU *cpu = _PR_MD_CURRENT_CPU(); 1.833 + PRIntn pri; 1.834 + PRUint32 r; 1.835 + PRCList *qp; 1.836 + PRIntn priMin, priMax; 1.837 +#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) 1.838 + PRBool wakeup_cpus; 1.839 +#endif 1.840 + 1.841 + /* Interrupts must be disabled */ 1.842 + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); 1.843 + 1.844 + /* Since we are rescheduling, we no longer want to */ 1.845 + _PR_CLEAR_RESCHED_FLAG(); 1.846 + 1.847 + /* 1.848 + ** Find highest priority thread to run. Bigger priority numbers are 1.849 + ** higher priority threads 1.850 + */ 1.851 + _PR_RUNQ_LOCK(cpu); 1.852 + /* 1.853 + * if we are in SuspendAll mode, can schedule only the thread 1.854 + * that called PR_SuspendAll 1.855 + * 1.856 + * The thread may be ready to run now, after completing an I/O 1.857 + * operation, for example 1.858 + */ 1.859 + if ((thread = suspendAllThread) != 0) { 1.860 + if ((!(thread->no_sched)) && (thread->state == _PR_RUNNABLE)) { 1.861 + /* Pull thread off of its run queue */ 1.862 + _PR_DEL_RUNQ(thread); 1.863 + _PR_RUNQ_UNLOCK(cpu); 1.864 + goto found_thread; 1.865 + } else { 1.866 + thread = NULL; 1.867 + _PR_RUNQ_UNLOCK(cpu); 1.868 + goto idle_thread; 1.869 + } 1.870 + } 1.871 + r = _PR_RUNQREADYMASK(cpu); 1.872 + if (r==0) { 1.873 + priMin = priMax = PR_PRIORITY_FIRST; 1.874 + } else if (r == (1<<PR_PRIORITY_NORMAL) ) { 1.875 + priMin = priMax = PR_PRIORITY_NORMAL; 1.876 + } else { 1.877 + priMin = PR_PRIORITY_FIRST; 1.878 + priMax = PR_PRIORITY_LAST; 1.879 + } 1.880 + thread = NULL; 1.881 + for (pri = priMax; pri >= priMin ; pri-- ) { 1.882 + if (r & (1 << pri)) { 1.883 + for (qp = _PR_RUNQ(cpu)[pri].next; 1.884 + qp != &_PR_RUNQ(cpu)[pri]; 1.885 + qp = qp->next) { 1.886 + thread = _PR_THREAD_PTR(qp); 1.887 + /* 1.888 + * skip non-schedulable threads 1.889 + */ 1.890 + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); 1.891 + if ((thread->no_sched) && (me != thread)){ 1.892 + thread = NULL; 1.893 + continue; 1.894 + } else { 1.895 + /* Pull thread off of its run queue */ 1.896 + _PR_DEL_RUNQ(thread); 1.897 + _PR_RUNQ_UNLOCK(cpu); 1.898 + goto found_thread; 1.899 + } 1.900 + } 1.901 + } 1.902 + thread = NULL; 1.903 + } 1.904 + _PR_RUNQ_UNLOCK(cpu); 1.905 + 1.906 +#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) 1.907 + 1.908 + wakeup_cpus = PR_FALSE; 1.909 + _PR_CPU_LIST_LOCK(); 1.910 + for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) { 1.911 + if (cpu != _PR_CPU_PTR(qp)) { 1.912 + if ((thread = get_thread(_PR_CPU_PTR(qp), &wakeup_cpus)) 1.913 + != NULL) { 1.914 + thread->cpu = cpu; 1.915 + _PR_CPU_LIST_UNLOCK(); 1.916 + if (wakeup_cpus == PR_TRUE) 1.917 + _PR_MD_WAKEUP_CPUS(); 1.918 + goto found_thread; 1.919 + } 1.920 + } 1.921 + } 1.922 + _PR_CPU_LIST_UNLOCK(); 1.923 + if (wakeup_cpus == PR_TRUE) 1.924 + _PR_MD_WAKEUP_CPUS(); 1.925 + 1.926 +#endif /* _PR_LOCAL_THREADS_ONLY */ 1.927 + 1.928 +idle_thread: 1.929 + /* 1.930 + ** There are no threads to run. Switch to the idle thread 1.931 + */ 1.932 + PR_LOG(_pr_sched_lm, PR_LOG_MAX, ("pausing")); 1.933 + thread = _PR_MD_CURRENT_CPU()->idle_thread; 1.934 + 1.935 +found_thread: 1.936 + PR_ASSERT((me == thread) || ((thread->state == _PR_RUNNABLE) && 1.937 + (!(thread->no_sched)))); 1.938 + 1.939 + /* Resume the thread */ 1.940 + PR_LOG(_pr_sched_lm, PR_LOG_MAX, 1.941 + ("switching to %d[%p]", thread->id, thread)); 1.942 + PR_ASSERT(thread->state != _PR_RUNNING); 1.943 + thread->state = _PR_RUNNING; 1.944 + 1.945 + /* If we are on the runq, it just means that we went to sleep on some 1.946 + * resource, and by the time we got here another real native thread had 1.947 + * already given us the resource and put us back on the runqueue 1.948 + */ 1.949 + PR_ASSERT(thread->cpu == _PR_MD_CURRENT_CPU()); 1.950 + if (thread != me) 1.951 + _PR_MD_RESTORE_CONTEXT(thread); 1.952 +#if 0 1.953 + /* XXXMB; with setjmp/longjmp it is impossible to land here, but 1.954 + * it is not with fibers... Is this a bad thing? I believe it is 1.955 + * still safe. 1.956 + */ 1.957 + PR_NOT_REACHED("impossible return from schedule"); 1.958 +#endif 1.959 +} 1.960 + 1.961 +/* 1.962 +** Attaches a thread. 1.963 +** Does not set the _PR_MD_CURRENT_THREAD. 1.964 +** Does not specify the scope of the thread. 1.965 +*/ 1.966 +static PRThread * 1.967 +_PR_AttachThread(PRThreadType type, PRThreadPriority priority, 1.968 + PRThreadStack *stack) 1.969 +{ 1.970 + PRThread *thread; 1.971 + char *mem; 1.972 + 1.973 + if (priority > PR_PRIORITY_LAST) { 1.974 + priority = PR_PRIORITY_LAST; 1.975 + } else if (priority < PR_PRIORITY_FIRST) { 1.976 + priority = PR_PRIORITY_FIRST; 1.977 + } 1.978 + 1.979 + mem = (char*) PR_CALLOC(sizeof(PRThread)); 1.980 + if (mem) { 1.981 + thread = (PRThread*) mem; 1.982 + thread->priority = priority; 1.983 + thread->stack = stack; 1.984 + thread->state = _PR_RUNNING; 1.985 + PR_INIT_CLIST(&thread->lockList); 1.986 + if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) { 1.987 + PR_DELETE(thread); 1.988 + return 0; 1.989 + } 1.990 + 1.991 + return thread; 1.992 + } 1.993 + return 0; 1.994 +} 1.995 + 1.996 + 1.997 + 1.998 +PR_IMPLEMENT(PRThread*) 1.999 +_PR_NativeCreateThread(PRThreadType type, 1.1000 + void (*start)(void *arg), 1.1001 + void *arg, 1.1002 + PRThreadPriority priority, 1.1003 + PRThreadScope scope, 1.1004 + PRThreadState state, 1.1005 + PRUint32 stackSize, 1.1006 + PRUint32 flags) 1.1007 +{ 1.1008 + PRThread *thread; 1.1009 + 1.1010 + thread = _PR_AttachThread(type, priority, NULL); 1.1011 + 1.1012 + if (thread) { 1.1013 + PR_Lock(_pr_activeLock); 1.1014 + thread->flags = (flags | _PR_GLOBAL_SCOPE); 1.1015 + thread->id = ++_pr_utid; 1.1016 + if (type == PR_SYSTEM_THREAD) { 1.1017 + thread->flags |= _PR_SYSTEM; 1.1018 + _pr_systemActive++; 1.1019 + } else { 1.1020 + _pr_userActive++; 1.1021 + } 1.1022 + PR_Unlock(_pr_activeLock); 1.1023 + 1.1024 + thread->stack = PR_NEWZAP(PRThreadStack); 1.1025 + if (!thread->stack) { 1.1026 + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 1.1027 + goto done; 1.1028 + } 1.1029 + thread->stack->stackSize = stackSize?stackSize:_MD_DEFAULT_STACK_SIZE; 1.1030 + thread->stack->thr = thread; 1.1031 + thread->startFunc = start; 1.1032 + thread->arg = arg; 1.1033 + 1.1034 + /* 1.1035 + Set thread flags related to scope and joinable state. If joinable 1.1036 + thread, allocate a "termination" conidition variable. 1.1037 + */ 1.1038 + if (state == PR_JOINABLE_THREAD) { 1.1039 + thread->term = PR_NewCondVar(_pr_terminationCVLock); 1.1040 + if (thread->term == NULL) { 1.1041 + PR_DELETE(thread->stack); 1.1042 + goto done; 1.1043 + } 1.1044 + } 1.1045 + 1.1046 + thread->state = _PR_RUNNING; 1.1047 + if (_PR_MD_CREATE_THREAD(thread, _PR_NativeRunThread, priority, 1.1048 + scope,state,stackSize) == PR_SUCCESS) { 1.1049 + return thread; 1.1050 + } 1.1051 + if (thread->term) { 1.1052 + PR_DestroyCondVar(thread->term); 1.1053 + thread->term = NULL; 1.1054 + } 1.1055 + PR_DELETE(thread->stack); 1.1056 + } 1.1057 + 1.1058 +done: 1.1059 + if (thread) { 1.1060 + _PR_DecrActiveThreadCount(thread); 1.1061 + _PR_DestroyThread(thread); 1.1062 + } 1.1063 + return NULL; 1.1064 +} 1.1065 + 1.1066 +/************************************************************************/ 1.1067 + 1.1068 +PR_IMPLEMENT(PRThread*) _PR_CreateThread(PRThreadType type, 1.1069 + void (*start)(void *arg), 1.1070 + void *arg, 1.1071 + PRThreadPriority priority, 1.1072 + PRThreadScope scope, 1.1073 + PRThreadState state, 1.1074 + PRUint32 stackSize, 1.1075 + PRUint32 flags) 1.1076 +{ 1.1077 + PRThread *me; 1.1078 + PRThread *thread = NULL; 1.1079 + PRThreadStack *stack; 1.1080 + char *top; 1.1081 + PRIntn is; 1.1082 + PRIntn native = 0; 1.1083 + PRIntn useRecycled = 0; 1.1084 + PRBool status; 1.1085 + 1.1086 + /* 1.1087 + First, pin down the priority. Not all compilers catch passing out of 1.1088 + range enum here. If we let bad values thru, priority queues won't work. 1.1089 + */ 1.1090 + if (priority > PR_PRIORITY_LAST) { 1.1091 + priority = PR_PRIORITY_LAST; 1.1092 + } else if (priority < PR_PRIORITY_FIRST) { 1.1093 + priority = PR_PRIORITY_FIRST; 1.1094 + } 1.1095 + 1.1096 + if (!_pr_initialized) _PR_ImplicitInitialization(); 1.1097 + 1.1098 + if (! (flags & _PR_IDLE_THREAD)) 1.1099 + me = _PR_MD_CURRENT_THREAD(); 1.1100 + 1.1101 +#if defined(_PR_GLOBAL_THREADS_ONLY) 1.1102 + /* 1.1103 + * can create global threads only 1.1104 + */ 1.1105 + if (scope == PR_LOCAL_THREAD) 1.1106 + scope = PR_GLOBAL_THREAD; 1.1107 +#endif 1.1108 + 1.1109 + if (_native_threads_only) 1.1110 + scope = PR_GLOBAL_THREAD; 1.1111 + 1.1112 + native = (((scope == PR_GLOBAL_THREAD)|| (scope == PR_GLOBAL_BOUND_THREAD)) 1.1113 + && _PR_IS_NATIVE_THREAD_SUPPORTED()); 1.1114 + 1.1115 + _PR_ADJUST_STACKSIZE(stackSize); 1.1116 + 1.1117 + if (native) { 1.1118 + /* 1.1119 + * clear the IDLE_THREAD flag which applies to LOCAL 1.1120 + * threads only 1.1121 + */ 1.1122 + flags &= ~_PR_IDLE_THREAD; 1.1123 + flags |= _PR_GLOBAL_SCOPE; 1.1124 + if (_PR_NUM_DEADNATIVE > 0) { 1.1125 + _PR_DEADQ_LOCK; 1.1126 + 1.1127 + if (_PR_NUM_DEADNATIVE == 0) { /* Thread safe check */ 1.1128 + _PR_DEADQ_UNLOCK; 1.1129 + } else { 1.1130 + thread = _PR_THREAD_PTR(_PR_DEADNATIVEQ.next); 1.1131 + PR_REMOVE_LINK(&thread->links); 1.1132 + _PR_DEC_DEADNATIVE; 1.1133 + _PR_DEADQ_UNLOCK; 1.1134 + 1.1135 + _PR_InitializeRecycledThread(thread); 1.1136 + thread->startFunc = start; 1.1137 + thread->arg = arg; 1.1138 + thread->flags = (flags | _PR_GLOBAL_SCOPE); 1.1139 + if (type == PR_SYSTEM_THREAD) 1.1140 + { 1.1141 + thread->flags |= _PR_SYSTEM; 1.1142 + PR_ATOMIC_INCREMENT(&_pr_systemActive); 1.1143 + } 1.1144 + else PR_ATOMIC_INCREMENT(&_pr_userActive); 1.1145 + 1.1146 + if (state == PR_JOINABLE_THREAD) { 1.1147 + if (!thread->term) 1.1148 + thread->term = PR_NewCondVar(_pr_terminationCVLock); 1.1149 + } 1.1150 + else { 1.1151 + if(thread->term) { 1.1152 + PR_DestroyCondVar(thread->term); 1.1153 + thread->term = 0; 1.1154 + } 1.1155 + } 1.1156 + 1.1157 + thread->priority = priority; 1.1158 + _PR_MD_SET_PRIORITY(&(thread->md), priority); 1.1159 + /* XXX what about stackSize? */ 1.1160 + thread->state = _PR_RUNNING; 1.1161 + _PR_MD_WAKEUP_WAITER(thread); 1.1162 + return thread; 1.1163 + } 1.1164 + } 1.1165 + thread = _PR_NativeCreateThread(type, start, arg, priority, 1.1166 + scope, state, stackSize, flags); 1.1167 + } else { 1.1168 + if (_PR_NUM_DEADUSER > 0) { 1.1169 + _PR_DEADQ_LOCK; 1.1170 + 1.1171 + if (_PR_NUM_DEADUSER == 0) { /* thread safe check */ 1.1172 + _PR_DEADQ_UNLOCK; 1.1173 + } else { 1.1174 + PRCList *ptr; 1.1175 + 1.1176 + /* Go down list checking for a recycled thread with a 1.1177 + * large enough stack. XXXMB - this has a bad degenerate case. 1.1178 + */ 1.1179 + ptr = _PR_DEADUSERQ.next; 1.1180 + while( ptr != &_PR_DEADUSERQ ) { 1.1181 + thread = _PR_THREAD_PTR(ptr); 1.1182 + if ((thread->stack->stackSize >= stackSize) && 1.1183 + (!thread->no_sched)) { 1.1184 + PR_REMOVE_LINK(&thread->links); 1.1185 + _PR_DEC_DEADUSER; 1.1186 + break; 1.1187 + } else { 1.1188 + ptr = ptr->next; 1.1189 + thread = NULL; 1.1190 + } 1.1191 + } 1.1192 + 1.1193 + _PR_DEADQ_UNLOCK; 1.1194 + 1.1195 + if (thread) { 1.1196 + _PR_InitializeRecycledThread(thread); 1.1197 + thread->startFunc = start; 1.1198 + thread->arg = arg; 1.1199 + thread->priority = priority; 1.1200 + if (state == PR_JOINABLE_THREAD) { 1.1201 + if (!thread->term) 1.1202 + thread->term = PR_NewCondVar(_pr_terminationCVLock); 1.1203 + } else { 1.1204 + if(thread->term) { 1.1205 + PR_DestroyCondVar(thread->term); 1.1206 + thread->term = 0; 1.1207 + } 1.1208 + } 1.1209 + useRecycled++; 1.1210 + } 1.1211 + } 1.1212 + } 1.1213 + if (thread == NULL) { 1.1214 +#ifndef HAVE_CUSTOM_USER_THREADS 1.1215 + stack = _PR_NewStack(stackSize); 1.1216 + if (!stack) { 1.1217 + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 1.1218 + return NULL; 1.1219 + } 1.1220 + 1.1221 + /* Allocate thread object and per-thread data off the top of the stack*/ 1.1222 + top = stack->stackTop; 1.1223 +#ifdef HAVE_STACK_GROWING_UP 1.1224 + thread = (PRThread*) top; 1.1225 + top = top + sizeof(PRThread); 1.1226 + /* 1.1227 + * Make stack 64-byte aligned 1.1228 + */ 1.1229 + if ((PRUptrdiff)top & 0x3f) { 1.1230 + top = (char*)(((PRUptrdiff)top + 0x40) & ~0x3f); 1.1231 + } 1.1232 +#else 1.1233 + top = top - sizeof(PRThread); 1.1234 + thread = (PRThread*) top; 1.1235 + /* 1.1236 + * Make stack 64-byte aligned 1.1237 + */ 1.1238 + if ((PRUptrdiff)top & 0x3f) { 1.1239 + top = (char*)((PRUptrdiff)top & ~0x3f); 1.1240 + } 1.1241 +#endif 1.1242 + stack->thr = thread; 1.1243 + memset(thread, 0, sizeof(PRThread)); 1.1244 + thread->threadAllocatedOnStack = 1; 1.1245 +#else 1.1246 + thread = _PR_MD_CREATE_USER_THREAD(stackSize, start, arg); 1.1247 + if (!thread) { 1.1248 + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 1.1249 + return NULL; 1.1250 + } 1.1251 + thread->threadAllocatedOnStack = 0; 1.1252 + stack = NULL; 1.1253 + top = NULL; 1.1254 +#endif 1.1255 + 1.1256 + /* Initialize thread */ 1.1257 + thread->tpdLength = 0; 1.1258 + thread->privateData = NULL; 1.1259 + thread->stack = stack; 1.1260 + thread->priority = priority; 1.1261 + thread->startFunc = start; 1.1262 + thread->arg = arg; 1.1263 + PR_INIT_CLIST(&thread->lockList); 1.1264 + 1.1265 + if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { 1.1266 + if (thread->threadAllocatedOnStack == 1) 1.1267 + _PR_FreeStack(thread->stack); 1.1268 + else { 1.1269 + PR_DELETE(thread); 1.1270 + } 1.1271 + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); 1.1272 + return NULL; 1.1273 + } 1.1274 + 1.1275 + if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) { 1.1276 + if (thread->threadAllocatedOnStack == 1) 1.1277 + _PR_FreeStack(thread->stack); 1.1278 + else { 1.1279 + PR_DELETE(thread->privateData); 1.1280 + PR_DELETE(thread); 1.1281 + } 1.1282 + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); 1.1283 + return NULL; 1.1284 + } 1.1285 + 1.1286 + _PR_MD_INIT_CONTEXT(thread, top, _PR_UserRunThread, &status); 1.1287 + 1.1288 + if (status == PR_FALSE) { 1.1289 + _PR_MD_FREE_LOCK(&thread->threadLock); 1.1290 + if (thread->threadAllocatedOnStack == 1) 1.1291 + _PR_FreeStack(thread->stack); 1.1292 + else { 1.1293 + PR_DELETE(thread->privateData); 1.1294 + PR_DELETE(thread); 1.1295 + } 1.1296 + return NULL; 1.1297 + } 1.1298 + 1.1299 + /* 1.1300 + Set thread flags related to scope and joinable state. If joinable 1.1301 + thread, allocate a "termination" condition variable. 1.1302 + */ 1.1303 + if (state == PR_JOINABLE_THREAD) { 1.1304 + thread->term = PR_NewCondVar(_pr_terminationCVLock); 1.1305 + if (thread->term == NULL) { 1.1306 + _PR_MD_FREE_LOCK(&thread->threadLock); 1.1307 + if (thread->threadAllocatedOnStack == 1) 1.1308 + _PR_FreeStack(thread->stack); 1.1309 + else { 1.1310 + PR_DELETE(thread->privateData); 1.1311 + PR_DELETE(thread); 1.1312 + } 1.1313 + return NULL; 1.1314 + } 1.1315 + } 1.1316 + 1.1317 + } 1.1318 + 1.1319 + /* Update thread type counter */ 1.1320 + PR_Lock(_pr_activeLock); 1.1321 + thread->flags = flags; 1.1322 + thread->id = ++_pr_utid; 1.1323 + if (type == PR_SYSTEM_THREAD) { 1.1324 + thread->flags |= _PR_SYSTEM; 1.1325 + _pr_systemActive++; 1.1326 + } else { 1.1327 + _pr_userActive++; 1.1328 + } 1.1329 + 1.1330 + /* Make thread runnable */ 1.1331 + thread->state = _PR_RUNNABLE; 1.1332 + /* 1.1333 + * Add to list of active threads 1.1334 + */ 1.1335 + PR_Unlock(_pr_activeLock); 1.1336 + 1.1337 + if ((! (thread->flags & _PR_IDLE_THREAD)) && _PR_IS_NATIVE_THREAD(me) ) 1.1338 + thread->cpu = _PR_GetPrimordialCPU(); 1.1339 + else 1.1340 + thread->cpu = _PR_MD_CURRENT_CPU(); 1.1341 + 1.1342 + PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread)); 1.1343 + 1.1344 + if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me)) { 1.1345 + _PR_INTSOFF(is); 1.1346 + _PR_RUNQ_LOCK(thread->cpu); 1.1347 + _PR_ADD_RUNQ(thread, thread->cpu, priority); 1.1348 + _PR_RUNQ_UNLOCK(thread->cpu); 1.1349 + } 1.1350 + 1.1351 + if (thread->flags & _PR_IDLE_THREAD) { 1.1352 + /* 1.1353 + ** If the creating thread is a kernel thread, we need to 1.1354 + ** awaken the user thread idle thread somehow; potentially 1.1355 + ** it could be sleeping in its idle loop, and we need to poke 1.1356 + ** it. To do so, wake the idle thread... 1.1357 + */ 1.1358 + _PR_MD_WAKEUP_WAITER(NULL); 1.1359 + } else if (_PR_IS_NATIVE_THREAD(me)) { 1.1360 + _PR_MD_WAKEUP_WAITER(thread); 1.1361 + } 1.1362 + if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me) ) 1.1363 + _PR_INTSON(is); 1.1364 + } 1.1365 + 1.1366 + return thread; 1.1367 +} 1.1368 + 1.1369 +PR_IMPLEMENT(PRThread*) PR_CreateThread(PRThreadType type, 1.1370 + void (*start)(void *arg), 1.1371 + void *arg, 1.1372 + PRThreadPriority priority, 1.1373 + PRThreadScope scope, 1.1374 + PRThreadState state, 1.1375 + PRUint32 stackSize) 1.1376 +{ 1.1377 + return _PR_CreateThread(type, start, arg, priority, scope, state, 1.1378 + stackSize, 0); 1.1379 +} 1.1380 + 1.1381 +/* 1.1382 +** Associate a thread object with an existing native thread. 1.1383 +** "type" is the type of thread object to attach 1.1384 +** "priority" is the priority to assign to the thread 1.1385 +** "stack" defines the shape of the threads stack 1.1386 +** 1.1387 +** This can return NULL if some kind of error occurs, or if memory is 1.1388 +** tight. 1.1389 +** 1.1390 +** This call is not normally needed unless you create your own native 1.1391 +** thread. PR_Init does this automatically for the primordial thread. 1.1392 +*/ 1.1393 +PRThread* _PRI_AttachThread(PRThreadType type, 1.1394 + PRThreadPriority priority, PRThreadStack *stack, PRUint32 flags) 1.1395 +{ 1.1396 + PRThread *thread; 1.1397 + 1.1398 + if ((thread = _PR_MD_GET_ATTACHED_THREAD()) != NULL) { 1.1399 + return thread; 1.1400 + } 1.1401 + _PR_MD_SET_CURRENT_THREAD(NULL); 1.1402 + 1.1403 + /* Clear out any state if this thread was attached before */ 1.1404 + _PR_MD_SET_CURRENT_CPU(NULL); 1.1405 + 1.1406 + thread = _PR_AttachThread(type, priority, stack); 1.1407 + if (thread) { 1.1408 + PRIntn is; 1.1409 + 1.1410 + _PR_MD_SET_CURRENT_THREAD(thread); 1.1411 + 1.1412 + thread->flags = flags | _PR_GLOBAL_SCOPE | _PR_ATTACHED; 1.1413 + 1.1414 + if (!stack) { 1.1415 + thread->stack = PR_NEWZAP(PRThreadStack); 1.1416 + if (!thread->stack) { 1.1417 + _PR_DestroyThread(thread); 1.1418 + return NULL; 1.1419 + } 1.1420 + thread->stack->stackSize = _MD_DEFAULT_STACK_SIZE; 1.1421 + } 1.1422 + PR_INIT_CLIST(&thread->links); 1.1423 + 1.1424 + if (_PR_MD_INIT_ATTACHED_THREAD(thread) == PR_FAILURE) { 1.1425 + PR_DELETE(thread->stack); 1.1426 + _PR_DestroyThread(thread); 1.1427 + return NULL; 1.1428 + } 1.1429 + 1.1430 + _PR_MD_SET_CURRENT_CPU(NULL); 1.1431 + 1.1432 + if (_PR_MD_CURRENT_CPU()) { 1.1433 + _PR_INTSOFF(is); 1.1434 + PR_Lock(_pr_activeLock); 1.1435 + } 1.1436 + if (type == PR_SYSTEM_THREAD) { 1.1437 + thread->flags |= _PR_SYSTEM; 1.1438 + _pr_systemActive++; 1.1439 + } else { 1.1440 + _pr_userActive++; 1.1441 + } 1.1442 + if (_PR_MD_CURRENT_CPU()) { 1.1443 + PR_Unlock(_pr_activeLock); 1.1444 + _PR_INTSON(is); 1.1445 + } 1.1446 + } 1.1447 + return thread; 1.1448 +} 1.1449 + 1.1450 +PR_IMPLEMENT(PRThread*) PR_AttachThread(PRThreadType type, 1.1451 + PRThreadPriority priority, PRThreadStack *stack) 1.1452 +{ 1.1453 + return PR_GetCurrentThread(); 1.1454 +} 1.1455 + 1.1456 +PR_IMPLEMENT(void) PR_DetachThread(void) 1.1457 +{ 1.1458 + /* 1.1459 + * On IRIX, Solaris, and Windows, foreign threads are detached when 1.1460 + * they terminate. 1.1461 + */ 1.1462 +#if !defined(IRIX) && !defined(WIN32) \ 1.1463 + && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)) 1.1464 + PRThread *me; 1.1465 + if (_pr_initialized) { 1.1466 + me = _PR_MD_GET_ATTACHED_THREAD(); 1.1467 + if ((me != NULL) && (me->flags & _PR_ATTACHED)) 1.1468 + _PRI_DetachThread(); 1.1469 + } 1.1470 +#endif 1.1471 +} 1.1472 + 1.1473 +void _PRI_DetachThread(void) 1.1474 +{ 1.1475 + PRThread *me = _PR_MD_CURRENT_THREAD(); 1.1476 + 1.1477 + if (me->flags & _PR_PRIMORDIAL) { 1.1478 + /* 1.1479 + * ignore, if primordial thread 1.1480 + */ 1.1481 + return; 1.1482 + } 1.1483 + PR_ASSERT(me->flags & _PR_ATTACHED); 1.1484 + PR_ASSERT(_PR_IS_NATIVE_THREAD(me)); 1.1485 + _PR_CleanupThread(me); 1.1486 + PR_DELETE(me->privateData); 1.1487 + 1.1488 + _PR_DecrActiveThreadCount(me); 1.1489 + 1.1490 + _PR_MD_CLEAN_THREAD(me); 1.1491 + _PR_MD_SET_CURRENT_THREAD(NULL); 1.1492 + if (!me->threadAllocatedOnStack) 1.1493 + PR_DELETE(me->stack); 1.1494 + _PR_MD_FREE_LOCK(&me->threadLock); 1.1495 + PR_DELETE(me); 1.1496 +} 1.1497 + 1.1498 +/* 1.1499 +** Wait for thread termination: 1.1500 +** "thread" is the target thread 1.1501 +** 1.1502 +** This can return PR_FAILURE if no joinable thread could be found 1.1503 +** corresponding to the specified target thread. 1.1504 +** 1.1505 +** The calling thread is suspended until the target thread completes. 1.1506 +** Several threads cannot wait for the same thread to complete; one thread 1.1507 +** will complete successfully and others will terminate with an error PR_FAILURE. 1.1508 +** The calling thread will not be blocked if the target thread has already 1.1509 +** terminated. 1.1510 +*/ 1.1511 +PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thread) 1.1512 +{ 1.1513 + PRIntn is; 1.1514 + PRCondVar *term; 1.1515 + PRThread *me = _PR_MD_CURRENT_THREAD(); 1.1516 + 1.1517 + if (!_PR_IS_NATIVE_THREAD(me)) 1.1518 + _PR_INTSOFF(is); 1.1519 + term = thread->term; 1.1520 + /* can't join a non-joinable thread */ 1.1521 + if (term == NULL) { 1.1522 + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 1.1523 + goto ErrorExit; 1.1524 + } 1.1525 + 1.1526 + /* multiple threads can't wait on the same joinable thread */ 1.1527 + if (term->condQ.next != &term->condQ) { 1.1528 + goto ErrorExit; 1.1529 + } 1.1530 + if (!_PR_IS_NATIVE_THREAD(me)) 1.1531 + _PR_INTSON(is); 1.1532 + 1.1533 + /* wait for the target thread's termination cv invariant */ 1.1534 + PR_Lock (_pr_terminationCVLock); 1.1535 + while (thread->state != _PR_JOIN_WAIT) { 1.1536 + (void) PR_WaitCondVar(term, PR_INTERVAL_NO_TIMEOUT); 1.1537 + } 1.1538 + (void) PR_Unlock (_pr_terminationCVLock); 1.1539 + 1.1540 + /* 1.1541 + Remove target thread from global waiting to join Q; make it runnable 1.1542 + again and put it back on its run Q. When it gets scheduled later in 1.1543 + _PR_RunThread code, it will clean up its stack. 1.1544 + */ 1.1545 + if (!_PR_IS_NATIVE_THREAD(me)) 1.1546 + _PR_INTSOFF(is); 1.1547 + thread->state = _PR_RUNNABLE; 1.1548 + if ( !_PR_IS_NATIVE_THREAD(thread) ) { 1.1549 + _PR_THREAD_LOCK(thread); 1.1550 + 1.1551 + _PR_MISCQ_LOCK(thread->cpu); 1.1552 + _PR_DEL_JOINQ(thread); 1.1553 + _PR_MISCQ_UNLOCK(thread->cpu); 1.1554 + 1.1555 + _PR_AddThreadToRunQ(me, thread); 1.1556 + _PR_THREAD_UNLOCK(thread); 1.1557 + } 1.1558 + if (!_PR_IS_NATIVE_THREAD(me)) 1.1559 + _PR_INTSON(is); 1.1560 + 1.1561 + _PR_MD_WAKEUP_WAITER(thread); 1.1562 + 1.1563 + return PR_SUCCESS; 1.1564 + 1.1565 +ErrorExit: 1.1566 + if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); 1.1567 + return PR_FAILURE; 1.1568 +} 1.1569 + 1.1570 +PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thread, 1.1571 + PRThreadPriority newPri) 1.1572 +{ 1.1573 + 1.1574 + /* 1.1575 + First, pin down the priority. Not all compilers catch passing out of 1.1576 + range enum here. If we let bad values thru, priority queues won't work. 1.1577 + */ 1.1578 + if ((PRIntn)newPri > (PRIntn)PR_PRIORITY_LAST) { 1.1579 + newPri = PR_PRIORITY_LAST; 1.1580 + } else if ((PRIntn)newPri < (PRIntn)PR_PRIORITY_FIRST) { 1.1581 + newPri = PR_PRIORITY_FIRST; 1.1582 + } 1.1583 + 1.1584 + if ( _PR_IS_NATIVE_THREAD(thread) ) { 1.1585 + thread->priority = newPri; 1.1586 + _PR_MD_SET_PRIORITY(&(thread->md), newPri); 1.1587 + } else _PR_SetThreadPriority(thread, newPri); 1.1588 +} 1.1589 + 1.1590 +PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name) 1.1591 +{ 1.1592 + PRThread *thread; 1.1593 + size_t nameLen; 1.1594 + 1.1595 + if (!name) { 1.1596 + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 1.1597 + return PR_FAILURE; 1.1598 + } 1.1599 + 1.1600 + thread = PR_GetCurrentThread(); 1.1601 + if (!thread) 1.1602 + return PR_FAILURE; 1.1603 + 1.1604 + PR_Free(thread->name); 1.1605 + nameLen = strlen(name); 1.1606 + thread->name = (char *)PR_Malloc(nameLen + 1); 1.1607 + if (!thread->name) 1.1608 + return PR_FAILURE; 1.1609 + memcpy(thread->name, name, nameLen + 1); 1.1610 + _PR_MD_SET_CURRENT_THREAD_NAME(thread->name); 1.1611 + return PR_SUCCESS; 1.1612 +} 1.1613 + 1.1614 +PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread) 1.1615 +{ 1.1616 + if (!thread) 1.1617 + return NULL; 1.1618 + return thread->name; 1.1619 +} 1.1620 + 1.1621 + 1.1622 +/* 1.1623 +** This routine prevents all other threads from running. This call is needed by 1.1624 +** the garbage collector. 1.1625 +*/ 1.1626 +PR_IMPLEMENT(void) PR_SuspendAll(void) 1.1627 +{ 1.1628 + PRThread *me = _PR_MD_CURRENT_THREAD(); 1.1629 + PRCList *qp; 1.1630 + 1.1631 + /* 1.1632 + * Stop all user and native threads which are marked GC able. 1.1633 + */ 1.1634 + PR_Lock(_pr_activeLock); 1.1635 + suspendAllOn = PR_TRUE; 1.1636 + suspendAllThread = _PR_MD_CURRENT_THREAD(); 1.1637 + _PR_MD_BEGIN_SUSPEND_ALL(); 1.1638 + for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; 1.1639 + qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) { 1.1640 + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && 1.1641 + _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) { 1.1642 + _PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); 1.1643 + PR_ASSERT((_PR_ACTIVE_THREAD_PTR(qp))->state != _PR_RUNNING); 1.1644 + } 1.1645 + } 1.1646 + for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; 1.1647 + qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) { 1.1648 + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && 1.1649 + _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) 1.1650 + /* PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); */ 1.1651 + _PR_MD_SUSPEND_THREAD(_PR_ACTIVE_THREAD_PTR(qp)); 1.1652 + } 1.1653 + _PR_MD_END_SUSPEND_ALL(); 1.1654 +} 1.1655 + 1.1656 +/* 1.1657 +** This routine unblocks all other threads that were suspended from running by 1.1658 +** PR_SuspendAll(). This call is needed by the garbage collector. 1.1659 +*/ 1.1660 +PR_IMPLEMENT(void) PR_ResumeAll(void) 1.1661 +{ 1.1662 + PRThread *me = _PR_MD_CURRENT_THREAD(); 1.1663 + PRCList *qp; 1.1664 + 1.1665 + /* 1.1666 + * Resume all user and native threads which are marked GC able. 1.1667 + */ 1.1668 + _PR_MD_BEGIN_RESUME_ALL(); 1.1669 + for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; 1.1670 + qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) { 1.1671 + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && 1.1672 + _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) 1.1673 + _PR_Resume(_PR_ACTIVE_THREAD_PTR(qp)); 1.1674 + } 1.1675 + for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; 1.1676 + qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) { 1.1677 + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && 1.1678 + _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) 1.1679 + _PR_MD_RESUME_THREAD(_PR_ACTIVE_THREAD_PTR(qp)); 1.1680 + } 1.1681 + _PR_MD_END_RESUME_ALL(); 1.1682 + suspendAllThread = NULL; 1.1683 + suspendAllOn = PR_FALSE; 1.1684 + PR_Unlock(_pr_activeLock); 1.1685 +} 1.1686 + 1.1687 +PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg) 1.1688 +{ 1.1689 + PRCList *qp, *qp_next; 1.1690 + PRIntn i = 0; 1.1691 + PRStatus rv = PR_SUCCESS; 1.1692 + PRThread* t; 1.1693 + 1.1694 + /* 1.1695 + ** Currently Enumerate threads happen only with suspension and 1.1696 + ** pr_activeLock held 1.1697 + */ 1.1698 + PR_ASSERT(suspendAllOn); 1.1699 + 1.1700 + /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking 1.1701 + * qp->next after applying the function "func". In particular, "func" 1.1702 + * might remove the thread from the queue and put it into another one in 1.1703 + * which case qp->next no longer points to the next entry in the original 1.1704 + * queue. 1.1705 + * 1.1706 + * To get around this problem, we save qp->next in qp_next before applying 1.1707 + * "func" and use that saved value as the next value after applying "func". 1.1708 + */ 1.1709 + 1.1710 + /* 1.1711 + * Traverse the list of local and global threads 1.1712 + */ 1.1713 + for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; 1.1714 + qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp_next) 1.1715 + { 1.1716 + qp_next = qp->next; 1.1717 + t = _PR_ACTIVE_THREAD_PTR(qp); 1.1718 + if (_PR_IS_GCABLE_THREAD(t)) 1.1719 + { 1.1720 + rv = (*func)(t, i, arg); 1.1721 + if (rv != PR_SUCCESS) 1.1722 + return rv; 1.1723 + i++; 1.1724 + } 1.1725 + } 1.1726 + for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; 1.1727 + qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp_next) 1.1728 + { 1.1729 + qp_next = qp->next; 1.1730 + t = _PR_ACTIVE_THREAD_PTR(qp); 1.1731 + if (_PR_IS_GCABLE_THREAD(t)) 1.1732 + { 1.1733 + rv = (*func)(t, i, arg); 1.1734 + if (rv != PR_SUCCESS) 1.1735 + return rv; 1.1736 + i++; 1.1737 + } 1.1738 + } 1.1739 + return rv; 1.1740 +} 1.1741 + 1.1742 +/* FUNCTION: _PR_AddSleepQ 1.1743 +** DESCRIPTION: 1.1744 +** Adds a thread to the sleep/pauseQ. 1.1745 +** RESTRICTIONS: 1.1746 +** Caller must have the RUNQ lock. 1.1747 +** Caller must be a user level thread 1.1748 +*/ 1.1749 +PR_IMPLEMENT(void) 1.1750 +_PR_AddSleepQ(PRThread *thread, PRIntervalTime timeout) 1.1751 +{ 1.1752 + _PRCPU *cpu = thread->cpu; 1.1753 + 1.1754 + if (timeout == PR_INTERVAL_NO_TIMEOUT) { 1.1755 + /* append the thread to the global pause Q */ 1.1756 + PR_APPEND_LINK(&thread->links, &_PR_PAUSEQ(thread->cpu)); 1.1757 + thread->flags |= _PR_ON_PAUSEQ; 1.1758 + } else { 1.1759 + PRIntervalTime sleep; 1.1760 + PRCList *q; 1.1761 + PRThread *t; 1.1762 + 1.1763 + /* sort onto global sleepQ */ 1.1764 + sleep = timeout; 1.1765 + 1.1766 + /* Check if we are longest timeout */ 1.1767 + if (timeout >= _PR_SLEEPQMAX(cpu)) { 1.1768 + PR_INSERT_BEFORE(&thread->links, &_PR_SLEEPQ(cpu)); 1.1769 + thread->sleep = timeout - _PR_SLEEPQMAX(cpu); 1.1770 + _PR_SLEEPQMAX(cpu) = timeout; 1.1771 + } else { 1.1772 + /* Sort thread into global sleepQ at appropriate point */ 1.1773 + q = _PR_SLEEPQ(cpu).next; 1.1774 + 1.1775 + /* Now scan the list for where to insert this entry */ 1.1776 + while (q != &_PR_SLEEPQ(cpu)) { 1.1777 + t = _PR_THREAD_PTR(q); 1.1778 + if (sleep < t->sleep) { 1.1779 + /* Found sleeper to insert in front of */ 1.1780 + break; 1.1781 + } 1.1782 + sleep -= t->sleep; 1.1783 + q = q->next; 1.1784 + } 1.1785 + thread->sleep = sleep; 1.1786 + PR_INSERT_BEFORE(&thread->links, q); 1.1787 + 1.1788 + /* 1.1789 + ** Subtract our sleep time from the sleeper that follows us (there 1.1790 + ** must be one) so that they remain relative to us. 1.1791 + */ 1.1792 + PR_ASSERT (thread->links.next != &_PR_SLEEPQ(cpu)); 1.1793 + 1.1794 + t = _PR_THREAD_PTR(thread->links.next); 1.1795 + PR_ASSERT(_PR_THREAD_PTR(t->links.prev) == thread); 1.1796 + t->sleep -= sleep; 1.1797 + } 1.1798 + 1.1799 + thread->flags |= _PR_ON_SLEEPQ; 1.1800 + } 1.1801 +} 1.1802 + 1.1803 +/* FUNCTION: _PR_DelSleepQ 1.1804 +** DESCRIPTION: 1.1805 +** Removes a thread from the sleep/pauseQ. 1.1806 +** INPUTS: 1.1807 +** If propogate_time is true, then the thread following the deleted 1.1808 +** thread will be get the time from the deleted thread. This is used 1.1809 +** when deleting a sleeper that has not timed out. 1.1810 +** RESTRICTIONS: 1.1811 +** Caller must have the RUNQ lock. 1.1812 +** Caller must be a user level thread 1.1813 +*/ 1.1814 +PR_IMPLEMENT(void) 1.1815 +_PR_DelSleepQ(PRThread *thread, PRBool propogate_time) 1.1816 +{ 1.1817 + _PRCPU *cpu = thread->cpu; 1.1818 + 1.1819 + /* Remove from pauseQ/sleepQ */ 1.1820 + if (thread->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) { 1.1821 + if (thread->flags & _PR_ON_SLEEPQ) { 1.1822 + PRCList *q = thread->links.next; 1.1823 + if (q != &_PR_SLEEPQ(cpu)) { 1.1824 + if (propogate_time == PR_TRUE) { 1.1825 + PRThread *after = _PR_THREAD_PTR(q); 1.1826 + after->sleep += thread->sleep; 1.1827 + } else 1.1828 + _PR_SLEEPQMAX(cpu) -= thread->sleep; 1.1829 + } else { 1.1830 + /* Check if prev is the beggining of the list; if so, 1.1831 + * we are the only element on the list. 1.1832 + */ 1.1833 + if (thread->links.prev != &_PR_SLEEPQ(cpu)) 1.1834 + _PR_SLEEPQMAX(cpu) -= thread->sleep; 1.1835 + else 1.1836 + _PR_SLEEPQMAX(cpu) = 0; 1.1837 + } 1.1838 + thread->flags &= ~_PR_ON_SLEEPQ; 1.1839 + } else { 1.1840 + thread->flags &= ~_PR_ON_PAUSEQ; 1.1841 + } 1.1842 + PR_REMOVE_LINK(&thread->links); 1.1843 + } else 1.1844 + PR_ASSERT(0); 1.1845 +} 1.1846 + 1.1847 +void 1.1848 +_PR_AddThreadToRunQ( 1.1849 + PRThread *me, /* the current thread */ 1.1850 + PRThread *thread) /* the local thread to be added to a run queue */ 1.1851 +{ 1.1852 + PRThreadPriority pri = thread->priority; 1.1853 + _PRCPU *cpu = thread->cpu; 1.1854 + 1.1855 + PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread)); 1.1856 + 1.1857 +#if defined(WINNT) 1.1858 + /* 1.1859 + * On NT, we can only reliably know that the current CPU 1.1860 + * is not idle. We add the awakened thread to the run 1.1861 + * queue of its CPU if its CPU is the current CPU. 1.1862 + * For any other CPU, we don't really know whether it 1.1863 + * is busy or idle. So in all other cases, we just 1.1864 + * "post" the awakened thread to the IO completion port 1.1865 + * for the next idle CPU to execute (this is done in 1.1866 + * _PR_MD_WAKEUP_WAITER). 1.1867 + * Threads with a suspended I/O operation remain bound to 1.1868 + * the same cpu until I/O is cancelled 1.1869 + * 1.1870 + * NOTE: the boolean expression below must be the exact 1.1871 + * opposite of the corresponding boolean expression in 1.1872 + * _PR_MD_WAKEUP_WAITER. 1.1873 + */ 1.1874 + if ((!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) || 1.1875 + (thread->md.thr_bound_cpu)) { 1.1876 + PR_ASSERT(!thread->md.thr_bound_cpu || 1.1877 + (thread->md.thr_bound_cpu == cpu)); 1.1878 + _PR_RUNQ_LOCK(cpu); 1.1879 + _PR_ADD_RUNQ(thread, cpu, pri); 1.1880 + _PR_RUNQ_UNLOCK(cpu); 1.1881 + } 1.1882 +#else 1.1883 + _PR_RUNQ_LOCK(cpu); 1.1884 + _PR_ADD_RUNQ(thread, cpu, pri); 1.1885 + _PR_RUNQ_UNLOCK(cpu); 1.1886 + if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) { 1.1887 + if (pri > me->priority) { 1.1888 + _PR_SET_RESCHED_FLAG(); 1.1889 + } 1.1890 + } 1.1891 +#endif 1.1892 +}