Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 | #include "primpl.h" |
michael@0 | 7 | |
michael@0 | 8 | #if defined(WIN95) |
michael@0 | 9 | /* |
michael@0 | 10 | ** Some local variables report warnings on Win95 because the code paths |
michael@0 | 11 | ** using them are conditioned on HAVE_CUSTOME_USER_THREADS. |
michael@0 | 12 | ** The pragma suppresses the warning. |
michael@0 | 13 | ** |
michael@0 | 14 | */ |
michael@0 | 15 | #pragma warning(disable : 4101) |
michael@0 | 16 | #endif |
michael@0 | 17 | |
michael@0 | 18 | |
michael@0 | 19 | extern PRLock *_pr_sleeplock; /* allocated and initialized in prinit */ |
michael@0 | 20 | /* |
michael@0 | 21 | ** Routines common to both native and user threads. |
michael@0 | 22 | ** |
michael@0 | 23 | ** |
michael@0 | 24 | ** Clean up a thread object, releasing all of the attached data. Do not |
michael@0 | 25 | ** free the object itself (it may not have been malloc'd) |
michael@0 | 26 | */ |
michael@0 | 27 | void _PR_CleanupThread(PRThread *thread) |
michael@0 | 28 | { |
michael@0 | 29 | /* Free up per-thread-data */ |
michael@0 | 30 | _PR_DestroyThreadPrivate(thread); |
michael@0 | 31 | |
michael@0 | 32 | /* Free any thread dump procs */ |
michael@0 | 33 | if (thread->dumpArg) { |
michael@0 | 34 | PR_DELETE(thread->dumpArg); |
michael@0 | 35 | } |
michael@0 | 36 | thread->dump = 0; |
michael@0 | 37 | |
michael@0 | 38 | PR_DELETE(thread->name); |
michael@0 | 39 | PR_DELETE(thread->errorString); |
michael@0 | 40 | thread->errorStringSize = 0; |
michael@0 | 41 | thread->errorStringLength = 0; |
michael@0 | 42 | thread->environment = NULL; |
michael@0 | 43 | } |
michael@0 | 44 | |
michael@0 | 45 | PR_IMPLEMENT(PRStatus) PR_Yield() |
michael@0 | 46 | { |
michael@0 | 47 | static PRBool warning = PR_TRUE; |
michael@0 | 48 | if (warning) warning = _PR_Obsolete( |
michael@0 | 49 | "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)"); |
michael@0 | 50 | return (PR_Sleep(PR_INTERVAL_NO_WAIT)); |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | /* |
michael@0 | 54 | ** Make the current thread sleep until "timeout" ticks amount of time |
michael@0 | 55 | ** has expired. If "timeout" is PR_INTERVAL_NO_WAIT then the call is |
michael@0 | 56 | ** equivalent to a yield. Waiting for an infinite amount of time is |
michael@0 | 57 | ** allowed in the expectation that another thread will interrupt(). |
michael@0 | 58 | ** |
michael@0 | 59 | ** A single lock is used for all threads calling sleep. Each caller |
michael@0 | 60 | ** does get its own condition variable since each is expected to have |
michael@0 | 61 | ** a unique 'timeout'. |
michael@0 | 62 | */ |
michael@0 | 63 | PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime timeout) |
michael@0 | 64 | { |
michael@0 | 65 | PRStatus rv = PR_SUCCESS; |
michael@0 | 66 | |
michael@0 | 67 | if (!_pr_initialized) _PR_ImplicitInitialization(); |
michael@0 | 68 | |
michael@0 | 69 | if (PR_INTERVAL_NO_WAIT == timeout) |
michael@0 | 70 | { |
michael@0 | 71 | /* |
michael@0 | 72 | ** This is a simple yield, nothing more, nothing less. |
michael@0 | 73 | */ |
michael@0 | 74 | PRIntn is; |
michael@0 | 75 | PRThread *me = PR_GetCurrentThread(); |
michael@0 | 76 | PRUintn pri = me->priority; |
michael@0 | 77 | _PRCPU *cpu = _PR_MD_CURRENT_CPU(); |
michael@0 | 78 | |
michael@0 | 79 | if ( _PR_IS_NATIVE_THREAD(me) ) _PR_MD_YIELD(); |
michael@0 | 80 | else |
michael@0 | 81 | { |
michael@0 | 82 | _PR_INTSOFF(is); |
michael@0 | 83 | _PR_RUNQ_LOCK(cpu); |
michael@0 | 84 | if (_PR_RUNQREADYMASK(cpu) >> pri) { |
michael@0 | 85 | me->cpu = cpu; |
michael@0 | 86 | me->state = _PR_RUNNABLE; |
michael@0 | 87 | _PR_ADD_RUNQ(me, cpu, pri); |
michael@0 | 88 | _PR_RUNQ_UNLOCK(cpu); |
michael@0 | 89 | |
michael@0 | 90 | PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: yielding")); |
michael@0 | 91 | _PR_MD_SWITCH_CONTEXT(me); |
michael@0 | 92 | PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: done")); |
michael@0 | 93 | |
michael@0 | 94 | _PR_FAST_INTSON(is); |
michael@0 | 95 | } |
michael@0 | 96 | else |
michael@0 | 97 | { |
michael@0 | 98 | _PR_RUNQ_UNLOCK(cpu); |
michael@0 | 99 | _PR_INTSON(is); |
michael@0 | 100 | } |
michael@0 | 101 | } |
michael@0 | 102 | } |
michael@0 | 103 | else |
michael@0 | 104 | { |
michael@0 | 105 | /* |
michael@0 | 106 | ** This is waiting for some finite period of time. |
michael@0 | 107 | ** A thread in this state is interruptible (PR_Interrupt()), |
michael@0 | 108 | ** but the lock and cvar used are local to the implementation |
michael@0 | 109 | ** and not visible to the caller, therefore not notifiable. |
michael@0 | 110 | */ |
michael@0 | 111 | PRCondVar *cv; |
michael@0 | 112 | PRIntervalTime timein; |
michael@0 | 113 | |
michael@0 | 114 | timein = PR_IntervalNow(); |
michael@0 | 115 | cv = PR_NewCondVar(_pr_sleeplock); |
michael@0 | 116 | PR_ASSERT(cv != NULL); |
michael@0 | 117 | PR_Lock(_pr_sleeplock); |
michael@0 | 118 | do |
michael@0 | 119 | { |
michael@0 | 120 | PRIntervalTime delta = PR_IntervalNow() - timein; |
michael@0 | 121 | if (delta > timeout) break; |
michael@0 | 122 | rv = PR_WaitCondVar(cv, timeout - delta); |
michael@0 | 123 | } while (rv == PR_SUCCESS); |
michael@0 | 124 | PR_Unlock(_pr_sleeplock); |
michael@0 | 125 | PR_DestroyCondVar(cv); |
michael@0 | 126 | } |
michael@0 | 127 | return rv; |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thread) |
michael@0 | 131 | { |
michael@0 | 132 | return thread->id; |
michael@0 | 133 | } |
michael@0 | 134 | |
michael@0 | 135 | PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thread) |
michael@0 | 136 | { |
michael@0 | 137 | return (PRThreadPriority) thread->priority; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | PR_IMPLEMENT(PRThread *) PR_GetCurrentThread() |
michael@0 | 141 | { |
michael@0 | 142 | if (!_pr_initialized) _PR_ImplicitInitialization(); |
michael@0 | 143 | return _PR_MD_CURRENT_THREAD(); |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | /* |
michael@0 | 147 | ** Set the interrupt flag for a thread. The thread will be unable to |
michael@0 | 148 | ** block in i/o functions when this happens. Also, any PR_Wait's in |
michael@0 | 149 | ** progress will be undone. The interrupt remains in force until |
michael@0 | 150 | ** PR_ClearInterrupt is called. |
michael@0 | 151 | */ |
michael@0 | 152 | PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thread) |
michael@0 | 153 | { |
michael@0 | 154 | #ifdef _PR_GLOBAL_THREADS_ONLY |
michael@0 | 155 | PRCondVar *victim; |
michael@0 | 156 | |
michael@0 | 157 | _PR_THREAD_LOCK(thread); |
michael@0 | 158 | thread->flags |= _PR_INTERRUPT; |
michael@0 | 159 | victim = thread->wait.cvar; |
michael@0 | 160 | _PR_THREAD_UNLOCK(thread); |
michael@0 | 161 | if ((NULL != victim) && (!(thread->flags & _PR_INTERRUPT_BLOCKED))) { |
michael@0 | 162 | int haveLock = (victim->lock->owner == _PR_MD_CURRENT_THREAD()); |
michael@0 | 163 | |
michael@0 | 164 | if (!haveLock) PR_Lock(victim->lock); |
michael@0 | 165 | PR_NotifyAllCondVar(victim); |
michael@0 | 166 | if (!haveLock) PR_Unlock(victim->lock); |
michael@0 | 167 | } |
michael@0 | 168 | return PR_SUCCESS; |
michael@0 | 169 | #else /* ! _PR_GLOBAL_THREADS_ONLY */ |
michael@0 | 170 | PRIntn is; |
michael@0 | 171 | PRThread *me = _PR_MD_CURRENT_THREAD(); |
michael@0 | 172 | |
michael@0 | 173 | if (!_PR_IS_NATIVE_THREAD(me)) |
michael@0 | 174 | _PR_INTSOFF(is); |
michael@0 | 175 | |
michael@0 | 176 | _PR_THREAD_LOCK(thread); |
michael@0 | 177 | thread->flags |= _PR_INTERRUPT; |
michael@0 | 178 | switch (thread->state) { |
michael@0 | 179 | case _PR_COND_WAIT: |
michael@0 | 180 | /* |
michael@0 | 181 | * call is made with thread locked; |
michael@0 | 182 | * on return lock is released |
michael@0 | 183 | */ |
michael@0 | 184 | if (!(thread->flags & _PR_INTERRUPT_BLOCKED)) |
michael@0 | 185 | _PR_NotifyLockedThread(thread); |
michael@0 | 186 | break; |
michael@0 | 187 | case _PR_IO_WAIT: |
michael@0 | 188 | /* |
michael@0 | 189 | * Need to hold the thread lock when calling |
michael@0 | 190 | * _PR_Unblock_IO_Wait(). On return lock is |
michael@0 | 191 | * released. |
michael@0 | 192 | */ |
michael@0 | 193 | #if defined(XP_UNIX) || defined(WINNT) || defined(WIN16) |
michael@0 | 194 | if (!(thread->flags & _PR_INTERRUPT_BLOCKED)) |
michael@0 | 195 | _PR_Unblock_IO_Wait(thread); |
michael@0 | 196 | #else |
michael@0 | 197 | _PR_THREAD_UNLOCK(thread); |
michael@0 | 198 | #endif |
michael@0 | 199 | break; |
michael@0 | 200 | case _PR_RUNNING: |
michael@0 | 201 | case _PR_RUNNABLE: |
michael@0 | 202 | case _PR_LOCK_WAIT: |
michael@0 | 203 | default: |
michael@0 | 204 | _PR_THREAD_UNLOCK(thread); |
michael@0 | 205 | break; |
michael@0 | 206 | } |
michael@0 | 207 | if (!_PR_IS_NATIVE_THREAD(me)) |
michael@0 | 208 | _PR_INTSON(is); |
michael@0 | 209 | return PR_SUCCESS; |
michael@0 | 210 | #endif /* _PR_GLOBAL_THREADS_ONLY */ |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | /* |
michael@0 | 214 | ** Clear the interrupt flag for self. |
michael@0 | 215 | */ |
michael@0 | 216 | PR_IMPLEMENT(void) PR_ClearInterrupt() |
michael@0 | 217 | { |
michael@0 | 218 | PRIntn is; |
michael@0 | 219 | PRThread *me = _PR_MD_CURRENT_THREAD(); |
michael@0 | 220 | |
michael@0 | 221 | if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); |
michael@0 | 222 | _PR_THREAD_LOCK(me); |
michael@0 | 223 | me->flags &= ~_PR_INTERRUPT; |
michael@0 | 224 | _PR_THREAD_UNLOCK(me); |
michael@0 | 225 | if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | PR_IMPLEMENT(void) PR_BlockInterrupt() |
michael@0 | 229 | { |
michael@0 | 230 | PRIntn is; |
michael@0 | 231 | PRThread *me = _PR_MD_CURRENT_THREAD(); |
michael@0 | 232 | |
michael@0 | 233 | if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); |
michael@0 | 234 | _PR_THREAD_LOCK(me); |
michael@0 | 235 | _PR_THREAD_BLOCK_INTERRUPT(me); |
michael@0 | 236 | _PR_THREAD_UNLOCK(me); |
michael@0 | 237 | if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); |
michael@0 | 238 | } /* PR_BlockInterrupt */ |
michael@0 | 239 | |
michael@0 | 240 | PR_IMPLEMENT(void) PR_UnblockInterrupt() |
michael@0 | 241 | { |
michael@0 | 242 | PRIntn is; |
michael@0 | 243 | PRThread *me = _PR_MD_CURRENT_THREAD(); |
michael@0 | 244 | |
michael@0 | 245 | if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); |
michael@0 | 246 | _PR_THREAD_LOCK(me); |
michael@0 | 247 | _PR_THREAD_UNBLOCK_INTERRUPT(me); |
michael@0 | 248 | _PR_THREAD_UNLOCK(me); |
michael@0 | 249 | if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); |
michael@0 | 250 | } /* PR_UnblockInterrupt */ |
michael@0 | 251 | |
michael@0 | 252 | /* |
michael@0 | 253 | ** Return the thread stack pointer of the given thread. |
michael@0 | 254 | */ |
michael@0 | 255 | PR_IMPLEMENT(void *) PR_GetSP(PRThread *thread) |
michael@0 | 256 | { |
michael@0 | 257 | return (void *)_PR_MD_GET_SP(thread); |
michael@0 | 258 | } |
michael@0 | 259 | |
michael@0 | 260 | PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thread) |
michael@0 | 261 | { |
michael@0 | 262 | return thread->environment; |
michael@0 | 263 | } |
michael@0 | 264 | |
michael@0 | 265 | PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thread, void *env) |
michael@0 | 266 | { |
michael@0 | 267 | thread->environment = env; |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | |
michael@0 | 271 | PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask) |
michael@0 | 272 | { |
michael@0 | 273 | #ifdef HAVE_THREAD_AFFINITY |
michael@0 | 274 | return _PR_MD_GETTHREADAFFINITYMASK(thread, mask); |
michael@0 | 275 | #else |
michael@0 | 276 | return 0; |
michael@0 | 277 | #endif |
michael@0 | 278 | } |
michael@0 | 279 | |
michael@0 | 280 | PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask ) |
michael@0 | 281 | { |
michael@0 | 282 | #ifdef HAVE_THREAD_AFFINITY |
michael@0 | 283 | #ifndef IRIX |
michael@0 | 284 | return _PR_MD_SETTHREADAFFINITYMASK(thread, mask); |
michael@0 | 285 | #else |
michael@0 | 286 | return 0; |
michael@0 | 287 | #endif |
michael@0 | 288 | #else |
michael@0 | 289 | return 0; |
michael@0 | 290 | #endif |
michael@0 | 291 | } |
michael@0 | 292 | |
michael@0 | 293 | /* This call is thread unsafe if another thread is calling SetConcurrency() |
michael@0 | 294 | */ |
michael@0 | 295 | PR_IMPLEMENT(PRInt32) PR_SetCPUAffinityMask(PRUint32 mask) |
michael@0 | 296 | { |
michael@0 | 297 | #ifdef HAVE_THREAD_AFFINITY |
michael@0 | 298 | PRCList *qp; |
michael@0 | 299 | extern PRUint32 _pr_cpu_affinity_mask; |
michael@0 | 300 | |
michael@0 | 301 | if (!_pr_initialized) _PR_ImplicitInitialization(); |
michael@0 | 302 | |
michael@0 | 303 | _pr_cpu_affinity_mask = mask; |
michael@0 | 304 | |
michael@0 | 305 | qp = _PR_CPUQ().next; |
michael@0 | 306 | while(qp != &_PR_CPUQ()) { |
michael@0 | 307 | _PRCPU *cpu; |
michael@0 | 308 | |
michael@0 | 309 | cpu = _PR_CPU_PTR(qp); |
michael@0 | 310 | PR_SetThreadAffinityMask(cpu->thread, mask); |
michael@0 | 311 | |
michael@0 | 312 | qp = qp->next; |
michael@0 | 313 | } |
michael@0 | 314 | #endif |
michael@0 | 315 | |
michael@0 | 316 | return 0; |
michael@0 | 317 | } |
michael@0 | 318 | |
michael@0 | 319 | PRUint32 _pr_recycleThreads = 0; |
michael@0 | 320 | PR_IMPLEMENT(void) PR_SetThreadRecycleMode(PRUint32 count) |
michael@0 | 321 | { |
michael@0 | 322 | _pr_recycleThreads = count; |
michael@0 | 323 | } |
michael@0 | 324 | |
michael@0 | 325 | PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble(PRThreadType type, |
michael@0 | 326 | void (*start)(void *arg), |
michael@0 | 327 | void *arg, |
michael@0 | 328 | PRThreadPriority priority, |
michael@0 | 329 | PRThreadScope scope, |
michael@0 | 330 | PRThreadState state, |
michael@0 | 331 | PRUint32 stackSize) |
michael@0 | 332 | { |
michael@0 | 333 | return _PR_CreateThread(type, start, arg, priority, scope, state, |
michael@0 | 334 | stackSize, _PR_GCABLE_THREAD); |
michael@0 | 335 | } |
michael@0 | 336 | |
michael@0 | 337 | #ifdef SOLARIS |
michael@0 | 338 | PR_IMPLEMENT(PRThread*) PR_CreateThreadBound(PRThreadType type, |
michael@0 | 339 | void (*start)(void *arg), |
michael@0 | 340 | void *arg, |
michael@0 | 341 | PRUintn priority, |
michael@0 | 342 | PRThreadScope scope, |
michael@0 | 343 | PRThreadState state, |
michael@0 | 344 | PRUint32 stackSize) |
michael@0 | 345 | { |
michael@0 | 346 | return _PR_CreateThread(type, start, arg, priority, scope, state, |
michael@0 | 347 | stackSize, _PR_BOUND_THREAD); |
michael@0 | 348 | } |
michael@0 | 349 | #endif |
michael@0 | 350 | |
michael@0 | 351 | |
michael@0 | 352 | PR_IMPLEMENT(PRThread*) PR_AttachThreadGCAble( |
michael@0 | 353 | PRThreadType type, PRThreadPriority priority, PRThreadStack *stack) |
michael@0 | 354 | { |
michael@0 | 355 | /* $$$$ not sure how to finese this one */ |
michael@0 | 356 | PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
michael@0 | 357 | return NULL; |
michael@0 | 358 | } |
michael@0 | 359 | |
michael@0 | 360 | PR_IMPLEMENT(void) PR_SetThreadGCAble() |
michael@0 | 361 | { |
michael@0 | 362 | if (!_pr_initialized) _PR_ImplicitInitialization(); |
michael@0 | 363 | PR_Lock(_pr_activeLock); |
michael@0 | 364 | _PR_MD_CURRENT_THREAD()->flags |= _PR_GCABLE_THREAD; |
michael@0 | 365 | PR_Unlock(_pr_activeLock); |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | PR_IMPLEMENT(void) PR_ClearThreadGCAble() |
michael@0 | 369 | { |
michael@0 | 370 | if (!_pr_initialized) _PR_ImplicitInitialization(); |
michael@0 | 371 | PR_Lock(_pr_activeLock); |
michael@0 | 372 | _PR_MD_CURRENT_THREAD()->flags &= (~_PR_GCABLE_THREAD); |
michael@0 | 373 | PR_Unlock(_pr_activeLock); |
michael@0 | 374 | } |
michael@0 | 375 | |
michael@0 | 376 | PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thread) |
michael@0 | 377 | { |
michael@0 | 378 | if (!_pr_initialized) _PR_ImplicitInitialization(); |
michael@0 | 379 | |
michael@0 | 380 | if (_PR_IS_NATIVE_THREAD(thread)) { |
michael@0 | 381 | return (thread->flags & _PR_BOUND_THREAD) ? PR_GLOBAL_BOUND_THREAD : |
michael@0 | 382 | PR_GLOBAL_THREAD; |
michael@0 | 383 | } else |
michael@0 | 384 | return PR_LOCAL_THREAD; |
michael@0 | 385 | } |
michael@0 | 386 | |
michael@0 | 387 | PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thread) |
michael@0 | 388 | { |
michael@0 | 389 | return (thread->flags & _PR_SYSTEM) ? PR_SYSTEM_THREAD : PR_USER_THREAD; |
michael@0 | 390 | } |
michael@0 | 391 | |
michael@0 | 392 | PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thread) |
michael@0 | 393 | { |
michael@0 | 394 | return (NULL == thread->term) ? PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD; |
michael@0 | 395 | } /* PR_GetThreadState */ |