1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is the Netscape Portable Runtime (NSPR).
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include <kernel/OS.h>
39 #include <support/TLS.h>
50 /* values for PRThread.state */
51 #define BT_THREAD_PRIMORD 0x01 /* this is the primordial thread */
52 #define BT_THREAD_SYSTEM 0x02 /* this is a system thread */
53 #define BT_THREAD_JOINABLE 0x04 /* this is a joinable thread */
57 PRLock *ml; /* a lock to protect ourselves */
58 sem_id cleanUpSem; /* the primoridal thread will block on this
59 sem while waiting for the user threads */
60 PRInt32 threadCount; /* user thred count */
62 } bt_book = { NULL, B_ERROR, 0 };
65 #define BT_TPD_LIMIT 128 /* number of TPD slots we'll provide (arbitrary) */
67 /* these will be used to map an index returned by PR_NewThreadPrivateIndex()
68 to the corresponding beos native TLS slot number, and to the destructor
69 for that slot - note that, because it is allocated globally, this data
70 will be automatically zeroed for us when the program begins */
71 static int32 tpd_beosTLSSlots[BT_TPD_LIMIT];
72 static PRThreadPrivateDTOR tpd_dtors[BT_TPD_LIMIT];
74 static vint32 tpd_slotsUsed=0; /* number of currently-allocated TPD slots */
75 static int32 tls_prThreadSlot; /* TLS slot in which PRThread will be stored */
77 /* this mutex will be used to synchronize access to every
78 PRThread.md.joinSem and PRThread.md.is_joining (we could
79 actually allocate one per thread, but that seems a bit excessive,
80 especially considering that there will probably be little
81 contention, PR_JoinThread() is allowed to block anyway, and the code
82 protected by the mutex is short/fast) */
83 static PRLock *joinSemLock;
85 static PRUint32 _bt_MapNSPRToNativePriority( PRThreadPriority priority );
86 static PRThreadPriority _bt_MapNativeToNSPRPriority( PRUint32 priority );
87 static void _bt_CleanupThread(void *arg);
88 static PRThread *_bt_AttachThread();
91 _PR_InitThreads (PRThreadType type, PRThreadPriority priority,
94 PRThread *primordialThread;
95 PRUint32 beThreadPriority;
97 /* allocate joinSem mutex */
98 joinSemLock = PR_NewLock();
99 if (joinSemLock == NULL)
101 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
106 ** Create and initialize NSPR structure for our primordial thread.
109 primordialThread = PR_NEWZAP(PRThread);
110 if( NULL == primordialThread )
112 PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
116 primordialThread->md.joinSem = B_ERROR;
119 ** Set the priority to the desired level.
122 beThreadPriority = _bt_MapNSPRToNativePriority( priority );
124 set_thread_priority( find_thread( NULL ), beThreadPriority );
126 primordialThread->priority = priority;
129 /* set the thread's state - note that the thread is not joinable */
130 primordialThread->state |= BT_THREAD_PRIMORD;
131 if (type == PR_SYSTEM_THREAD)
132 primordialThread->state |= BT_THREAD_SYSTEM;
135 ** Allocate a TLS slot for the PRThread structure (just using
136 ** native TLS, as opposed to NSPR TPD, will make PR_GetCurrentThread()
137 ** somewhat faster, and will leave one more TPD slot for our client)
140 tls_prThreadSlot = tls_allocate();
143 ** Stuff our new PRThread structure into our thread specific
147 tls_set(tls_prThreadSlot, primordialThread);
149 /* allocate lock for bt_book */
150 bt_book.ml = PR_NewLock();
151 if( NULL == bt_book.ml )
153 PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
159 _bt_MapNSPRToNativePriority( PRThreadPriority priority )
163 case PR_PRIORITY_LOW: return( B_LOW_PRIORITY );
164 case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY );
165 case PR_PRIORITY_HIGH: return( B_DISPLAY_PRIORITY );
166 case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY );
167 default: return( B_NORMAL_PRIORITY );
172 _bt_MapNativeToNSPRPriority(PRUint32 priority)
174 if (priority < B_NORMAL_PRIORITY)
175 return PR_PRIORITY_LOW;
176 if (priority < B_DISPLAY_PRIORITY)
177 return PR_PRIORITY_NORMAL;
178 if (priority < B_URGENT_DISPLAY_PRIORITY)
179 return PR_PRIORITY_HIGH;
180 return PR_PRIORITY_URGENT;
184 _bt_mapNativeToNSPRPriority( int32 priority )
188 case PR_PRIORITY_LOW: return( B_LOW_PRIORITY );
189 case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY );
190 case PR_PRIORITY_HIGH: return( B_DISPLAY_PRIORITY );
191 case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY );
192 default: return( B_NORMAL_PRIORITY );
196 /* This method is called by all NSPR threads as they exit */
197 void _bt_CleanupThread(void *arg)
199 PRThread *me = PR_GetCurrentThread();
202 /* first, clean up all thread-private data */
203 for (i = 0; i < tpd_slotsUsed; i++)
205 void *oldValue = tls_get(tpd_beosTLSSlots[i]);
206 if ( oldValue != NULL && tpd_dtors[i] != NULL )
207 (*tpd_dtors[i])(oldValue);
210 /* if this thread is joinable, wait for someone to join it */
211 if (me->state & BT_THREAD_JOINABLE)
213 /* protect access to our joinSem */
214 PR_Lock(joinSemLock);
216 if (me->md.is_joining)
218 /* someone is already waiting to join us (they've
219 allocated a joinSem for us) - let them know we're
221 delete_sem(me->md.joinSem);
223 PR_Unlock(joinSemLock);
228 /* noone is currently waiting for our demise - it
229 is our responsibility to allocate the joinSem
231 me->md.joinSem = create_sem(0, "join sem");
233 /* we're done accessing our joinSem */
234 PR_Unlock(joinSemLock);
236 /* wait for someone to join us */
237 while (acquire_sem(me->md.joinSem) == B_INTERRUPTED);
241 /* if this is a user thread, we must update our books */
242 if ((me->state & BT_THREAD_SYSTEM) == 0)
244 /* synchronize access to bt_book */
245 PR_Lock( bt_book.ml );
247 /* decrement the number of currently-alive user threads */
248 bt_book.threadCount--;
250 if (bt_book.threadCount == 0 && bt_book.cleanUpSem != B_ERROR) {
251 /* we are the last user thread, and the primordial thread is
252 blocked in PR_Cleanup() waiting for us to finish - notify
254 delete_sem(bt_book.cleanUpSem);
257 PR_Unlock( bt_book.ml );
260 /* finally, delete this thread's PRThread */
265 * This is a wrapper that all threads invoke that allows us to set some
266 * things up prior to a thread's invocation and clean up after a thread has
272 PRThread *thred = (PRThread*)arg;
278 /* save our PRThread object into our TLS */
279 tls_set(tls_prThreadSlot, thred);
281 thred->startFunc(thred->arg); /* run the dang thing */
284 _bt_CleanupThread(NULL);
289 PR_IMPLEMENT(PRThread*)
290 PR_CreateThread (PRThreadType type, void (*start)(void* arg), void* arg,
291 PRThreadPriority priority, PRThreadScope scope,
292 PRThreadState state, PRUint32 stackSize)
298 if (!_pr_initialized) _PR_ImplicitInitialization();
300 thred = PR_NEWZAP(PRThread);
303 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
307 thred->md.joinSem = B_ERROR;
310 thred->startFunc = start;
311 thred->priority = priority;
313 if( state == PR_JOINABLE_THREAD )
315 thred->state |= BT_THREAD_JOINABLE;
318 /* keep some books */
320 PR_Lock( bt_book.ml );
322 if (type == PR_USER_THREAD)
324 bt_book.threadCount++;
327 PR_Unlock( bt_book.ml );
329 bePriority = _bt_MapNSPRToNativePriority( priority );
331 thred->md.tid = spawn_thread((thread_func)_bt_root, "moz-thread",
333 if (thred->md.tid < B_OK) {
334 PR_SetError(PR_UNKNOWN_ERROR, thred->md.tid);
339 if (resume_thread(thred->md.tid) < B_OK) {
340 PR_SetError(PR_UNKNOWN_ERROR, 0);
348 PR_IMPLEMENT(PRThread*)
349 PR_AttachThread(PRThreadType type, PRThreadPriority priority,
350 PRThreadStack *stack)
352 /* PR_GetCurrentThread() will attach a thread if necessary */
353 return PR_GetCurrentThread();
359 /* we don't support detaching */
362 PR_IMPLEMENT(PRStatus)
363 PR_JoinThread (PRThread* thred)
365 status_t eval, status;
367 PR_ASSERT(thred != NULL);
369 if ((thred->state & BT_THREAD_JOINABLE) == 0)
371 PR_SetError( PR_INVALID_ARGUMENT_ERROR, 0 );
372 return( PR_FAILURE );
375 /* synchronize access to the thread's joinSem */
376 PR_Lock(joinSemLock);
378 if (thred->md.is_joining)
380 /* another thread is already waiting to join the specified
381 thread - we must fail */
382 PR_Unlock(joinSemLock);
386 /* let others know we are waiting to join */
387 thred->md.is_joining = PR_TRUE;
389 if (thred->md.joinSem == B_ERROR)
391 /* the thread hasn't finished yet - it is our responsibility to
392 allocate a joinSem and wait on it */
393 thred->md.joinSem = create_sem(0, "join sem");
395 /* we're done changing the joinSem now */
396 PR_Unlock(joinSemLock);
398 /* wait for the thread to finish */
399 while (acquire_sem(thred->md.joinSem) == B_INTERRUPTED);
404 /* the thread has already finished, and has allocated the
405 joinSem itself - let it know it can finally die */
406 delete_sem(thred->md.joinSem);
408 PR_Unlock(joinSemLock);
411 /* make sure the thread is dead */
412 wait_for_thread(thred->md.tid, &eval);
417 PR_IMPLEMENT(PRThread*)
418 PR_GetCurrentThread ()
422 if (!_pr_initialized) _PR_ImplicitInitialization();
424 thred = (PRThread *)tls_get( tls_prThreadSlot);
427 /* this thread doesn't have a PRThread structure (it must be
428 a native thread not created by the NSPR) - assimilate it */
429 thred = _bt_AttachThread();
431 PR_ASSERT(NULL != thred);
436 PR_IMPLEMENT(PRThreadScope)
437 PR_GetThreadScope (const PRThread* thred)
439 PR_ASSERT(thred != NULL);
440 return PR_GLOBAL_THREAD;
443 PR_IMPLEMENT(PRThreadType)
444 PR_GetThreadType (const PRThread* thred)
446 PR_ASSERT(thred != NULL);
447 return (thred->state & BT_THREAD_SYSTEM) ?
448 PR_SYSTEM_THREAD : PR_USER_THREAD;
451 PR_IMPLEMENT(PRThreadState)
452 PR_GetThreadState (const PRThread* thred)
454 PR_ASSERT(thred != NULL);
455 return (thred->state & BT_THREAD_JOINABLE)?
456 PR_JOINABLE_THREAD: PR_UNJOINABLE_THREAD;
459 PR_IMPLEMENT(PRThreadPriority)
460 PR_GetThreadPriority (const PRThread* thred)
462 PR_ASSERT(thred != NULL);
463 return thred->priority;
464 } /* PR_GetThreadPriority */
466 PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred,
467 PRThreadPriority newPri)
471 PR_ASSERT( thred != NULL );
473 thred->priority = newPri;
474 bePriority = _bt_MapNSPRToNativePriority( newPri );
475 set_thread_priority( thred->md.tid, bePriority );
478 PR_IMPLEMENT(PRStatus)
479 PR_NewThreadPrivateIndex (PRUintn* newIndex,
480 PRThreadPrivateDTOR destructor)
484 if (!_pr_initialized) _PR_ImplicitInitialization();
486 /* reserve the next available tpd slot */
487 index = atomic_add( &tpd_slotsUsed, 1 );
488 if (index >= BT_TPD_LIMIT)
490 /* no slots left - decrement value, then fail */
491 atomic_add( &tpd_slotsUsed, -1 );
492 PR_SetError( PR_TPD_RANGE_ERROR, 0 );
493 return( PR_FAILURE );
496 /* allocate a beos-native TLS slot for this index (the new slot
497 automatically contains NULL) */
498 tpd_beosTLSSlots[index] = tls_allocate();
500 /* remember the destructor */
501 tpd_dtors[index] = destructor;
503 *newIndex = (PRUintn)index;
505 return( PR_SUCCESS );
508 PR_IMPLEMENT(PRStatus)
509 PR_SetThreadPrivate (PRUintn index, void* priv)
517 if(index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
519 PR_SetError( PR_TPD_RANGE_ERROR, 0 );
520 return( PR_FAILURE );
523 /* if the old value isn't NULL, and the dtor for this slot isn't
524 NULL, we must destroy the data */
525 oldValue = tls_get(tpd_beosTLSSlots[index]);
526 if (oldValue != NULL && tpd_dtors[index] != NULL)
527 (*tpd_dtors[index])(oldValue);
530 tls_set(tpd_beosTLSSlots[index], priv);
532 return( PR_SUCCESS );
536 PR_GetThreadPrivate (PRUintn index)
538 /* make sure the index is valid */
539 if (index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
541 PR_SetError( PR_TPD_RANGE_ERROR, 0 );
545 /* return the value */
546 return tls_get( tpd_beosTLSSlots[index] );
550 PR_IMPLEMENT(PRStatus)
551 PR_Interrupt (PRThread* thred)
555 PR_ASSERT(thred != NULL);
558 ** there seems to be a bug in beos R5 in which calling
559 ** resume_thread() on a blocked thread returns B_OK instead
560 ** of B_BAD_THREAD_STATE (beos bug #20000422-19095). as such,
561 ** to interrupt a thread, we will simply suspend then resume it
562 ** (no longer call resume_thread(), check for B_BAD_THREAD_STATE,
563 ** the suspend/resume to wake up a blocked thread). this wakes
564 ** up blocked threads properly, and doesn't hurt unblocked threads
565 ** (they simply get stopped then re-started immediately)
568 rv = suspend_thread( thred->md.tid );
569 if( rv != B_NO_ERROR )
571 /* this doesn't appear to be a valid thread_id */
572 PR_SetError( PR_UNKNOWN_ERROR, rv );
576 rv = resume_thread( thred->md.tid );
577 if( rv != B_NO_ERROR )
579 PR_SetError( PR_UNKNOWN_ERROR, rv );
591 PR_IMPLEMENT(PRStatus)
594 /* we just sleep for long enough to cause a reschedule (100
599 #define BT_MILLION 1000000UL
601 PR_IMPLEMENT(PRStatus)
602 PR_Sleep (PRIntervalTime ticks)
607 if (!_pr_initialized) _PR_ImplicitInitialization();
609 tps = PR_IntervalToMicroseconds( ticks );
611 status = snooze(tps);
612 if (status == B_NO_ERROR) return PR_SUCCESS;
614 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, status);
618 PR_IMPLEMENT(PRStatus)
621 PRThread *me = PR_GetCurrentThread();
623 PR_ASSERT(me->state & BT_THREAD_PRIMORD);
624 if ((me->state & BT_THREAD_PRIMORD) == 0) {
628 PR_Lock( bt_book.ml );
630 if (bt_book.threadCount != 0)
632 /* we'll have to wait for some threads to finish - create a
634 bt_book.cleanUpSem = create_sem(0, "cleanup sem");
637 PR_Unlock( bt_book.ml );
639 /* note that, if all the user threads were already dead, we
640 wouldn't have created a sem above, so this acquire_sem()
641 will fail immediately */
642 while (acquire_sem(bt_book.cleanUpSem) == B_INTERRUPTED);
648 PR_ProcessExit (PRIntn status)
653 PRThread *_bt_AttachThread()
658 /* make sure this thread doesn't already have a PRThread structure */
659 PR_ASSERT(tls_get(tls_prThreadSlot) == NULL);
661 /* allocate a PRThread structure for this thread */
662 thread = PR_NEWZAP(PRThread);
665 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
669 /* get the native thread's current state */
670 get_thread_info(find_thread(NULL), &tInfo);
672 /* initialize new PRThread */
673 thread->md.tid = tInfo.thread;
674 thread->md.joinSem = B_ERROR;
675 thread->priority = _bt_MapNativeToNSPRPriority(tInfo.priority);
677 /* attached threads are always non-joinable user threads */
680 /* increment user thread count */
682 bt_book.threadCount++;
683 PR_Unlock(bt_book.ml);
685 /* store this thread's PRThread */
686 tls_set(tls_prThreadSlot, thread);
688 /* the thread must call _bt_CleanupThread() before it dies, in order
689 to clean up its PRThread, synchronize with the primordial thread,
691 on_exit_thread(_bt_CleanupThread, NULL);