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