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