michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "prlog.h" michael@0: #include "primpl.h" michael@0: #include "prcvar.h" michael@0: #include "prpdce.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: /* values for PRThread.state */ michael@0: #define BT_THREAD_PRIMORD 0x01 /* this is the primordial thread */ michael@0: #define BT_THREAD_SYSTEM 0x02 /* this is a system thread */ michael@0: #define BT_THREAD_JOINABLE 0x04 /* this is a joinable thread */ michael@0: michael@0: struct _BT_Bookeeping michael@0: { michael@0: PRLock *ml; /* a lock to protect ourselves */ michael@0: sem_id cleanUpSem; /* the primoridal thread will block on this michael@0: sem while waiting for the user threads */ michael@0: PRInt32 threadCount; /* user thred count */ michael@0: michael@0: } bt_book = { NULL, B_ERROR, 0 }; michael@0: michael@0: michael@0: #define BT_TPD_LIMIT 128 /* number of TPD slots we'll provide (arbitrary) */ michael@0: michael@0: /* these will be used to map an index returned by PR_NewThreadPrivateIndex() michael@0: to the corresponding beos native TLS slot number, and to the destructor michael@0: for that slot - note that, because it is allocated globally, this data michael@0: will be automatically zeroed for us when the program begins */ michael@0: static int32 tpd_beosTLSSlots[BT_TPD_LIMIT]; michael@0: static PRThreadPrivateDTOR tpd_dtors[BT_TPD_LIMIT]; michael@0: michael@0: static vint32 tpd_slotsUsed=0; /* number of currently-allocated TPD slots */ michael@0: static int32 tls_prThreadSlot; /* TLS slot in which PRThread will be stored */ michael@0: michael@0: /* this mutex will be used to synchronize access to every michael@0: PRThread.md.joinSem and PRThread.md.is_joining (we could michael@0: actually allocate one per thread, but that seems a bit excessive, michael@0: especially considering that there will probably be little michael@0: contention, PR_JoinThread() is allowed to block anyway, and the code michael@0: protected by the mutex is short/fast) */ michael@0: static PRLock *joinSemLock; michael@0: michael@0: static PRUint32 _bt_MapNSPRToNativePriority( PRThreadPriority priority ); michael@0: static PRThreadPriority _bt_MapNativeToNSPRPriority( PRUint32 priority ); michael@0: static void _bt_CleanupThread(void *arg); michael@0: static PRThread *_bt_AttachThread(); michael@0: michael@0: void michael@0: _PR_InitThreads (PRThreadType type, PRThreadPriority priority, michael@0: PRUintn maxPTDs) michael@0: { michael@0: PRThread *primordialThread; michael@0: PRUint32 beThreadPriority; michael@0: michael@0: /* allocate joinSem mutex */ michael@0: joinSemLock = PR_NewLock(); michael@0: if (joinSemLock == NULL) michael@0: { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: return; michael@0: } michael@0: michael@0: /* michael@0: ** Create and initialize NSPR structure for our primordial thread. michael@0: */ michael@0: michael@0: primordialThread = PR_NEWZAP(PRThread); michael@0: if( NULL == primordialThread ) michael@0: { michael@0: PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 ); michael@0: return; michael@0: } michael@0: michael@0: primordialThread->md.joinSem = B_ERROR; michael@0: michael@0: /* michael@0: ** Set the priority to the desired level. michael@0: */ michael@0: michael@0: beThreadPriority = _bt_MapNSPRToNativePriority( priority ); michael@0: michael@0: set_thread_priority( find_thread( NULL ), beThreadPriority ); michael@0: michael@0: primordialThread->priority = priority; michael@0: michael@0: michael@0: /* set the thread's state - note that the thread is not joinable */ michael@0: primordialThread->state |= BT_THREAD_PRIMORD; michael@0: if (type == PR_SYSTEM_THREAD) michael@0: primordialThread->state |= BT_THREAD_SYSTEM; michael@0: michael@0: /* michael@0: ** Allocate a TLS slot for the PRThread structure (just using michael@0: ** native TLS, as opposed to NSPR TPD, will make PR_GetCurrentThread() michael@0: ** somewhat faster, and will leave one more TPD slot for our client) michael@0: */ michael@0: michael@0: tls_prThreadSlot = tls_allocate(); michael@0: michael@0: /* michael@0: ** Stuff our new PRThread structure into our thread specific michael@0: ** slot. michael@0: */ michael@0: michael@0: tls_set(tls_prThreadSlot, primordialThread); michael@0: michael@0: /* allocate lock for bt_book */ michael@0: bt_book.ml = PR_NewLock(); michael@0: if( NULL == bt_book.ml ) michael@0: { michael@0: PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 ); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: PRUint32 michael@0: _bt_MapNSPRToNativePriority( PRThreadPriority priority ) michael@0: { michael@0: switch( priority ) michael@0: { michael@0: case PR_PRIORITY_LOW: return( B_LOW_PRIORITY ); michael@0: case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY ); michael@0: case PR_PRIORITY_HIGH: return( B_DISPLAY_PRIORITY ); michael@0: case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY ); michael@0: default: return( B_NORMAL_PRIORITY ); michael@0: } michael@0: } michael@0: michael@0: PRThreadPriority michael@0: _bt_MapNativeToNSPRPriority(PRUint32 priority) michael@0: { michael@0: if (priority < B_NORMAL_PRIORITY) michael@0: return PR_PRIORITY_LOW; michael@0: if (priority < B_DISPLAY_PRIORITY) michael@0: return PR_PRIORITY_NORMAL; michael@0: if (priority < B_URGENT_DISPLAY_PRIORITY) michael@0: return PR_PRIORITY_HIGH; michael@0: return PR_PRIORITY_URGENT; michael@0: } michael@0: michael@0: PRUint32 michael@0: _bt_mapNativeToNSPRPriority( int32 priority ) michael@0: { michael@0: switch( priority ) michael@0: { michael@0: case PR_PRIORITY_LOW: return( B_LOW_PRIORITY ); michael@0: case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY ); michael@0: case PR_PRIORITY_HIGH: return( B_DISPLAY_PRIORITY ); michael@0: case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY ); michael@0: default: return( B_NORMAL_PRIORITY ); michael@0: } michael@0: } michael@0: michael@0: /* This method is called by all NSPR threads as they exit */ michael@0: void _bt_CleanupThread(void *arg) michael@0: { michael@0: PRThread *me = PR_GetCurrentThread(); michael@0: int32 i; michael@0: michael@0: /* first, clean up all thread-private data */ michael@0: for (i = 0; i < tpd_slotsUsed; i++) michael@0: { michael@0: void *oldValue = tls_get(tpd_beosTLSSlots[i]); michael@0: if ( oldValue != NULL && tpd_dtors[i] != NULL ) michael@0: (*tpd_dtors[i])(oldValue); michael@0: } michael@0: michael@0: /* if this thread is joinable, wait for someone to join it */ michael@0: if (me->state & BT_THREAD_JOINABLE) michael@0: { michael@0: /* protect access to our joinSem */ michael@0: PR_Lock(joinSemLock); michael@0: michael@0: if (me->md.is_joining) michael@0: { michael@0: /* someone is already waiting to join us (they've michael@0: allocated a joinSem for us) - let them know we're michael@0: ready */ michael@0: delete_sem(me->md.joinSem); michael@0: michael@0: PR_Unlock(joinSemLock); michael@0: michael@0: } michael@0: else michael@0: { michael@0: /* noone is currently waiting for our demise - it michael@0: is our responsibility to allocate the joinSem michael@0: and block on it */ michael@0: me->md.joinSem = create_sem(0, "join sem"); michael@0: michael@0: /* we're done accessing our joinSem */ michael@0: PR_Unlock(joinSemLock); michael@0: michael@0: /* wait for someone to join us */ michael@0: while (acquire_sem(me->md.joinSem) == B_INTERRUPTED); michael@0: } michael@0: } michael@0: michael@0: /* if this is a user thread, we must update our books */ michael@0: if ((me->state & BT_THREAD_SYSTEM) == 0) michael@0: { michael@0: /* synchronize access to bt_book */ michael@0: PR_Lock( bt_book.ml ); michael@0: michael@0: /* decrement the number of currently-alive user threads */ michael@0: bt_book.threadCount--; michael@0: michael@0: if (bt_book.threadCount == 0 && bt_book.cleanUpSem != B_ERROR) { michael@0: /* we are the last user thread, and the primordial thread is michael@0: blocked in PR_Cleanup() waiting for us to finish - notify michael@0: it */ michael@0: delete_sem(bt_book.cleanUpSem); michael@0: } michael@0: michael@0: PR_Unlock( bt_book.ml ); michael@0: } michael@0: michael@0: /* finally, delete this thread's PRThread */ michael@0: PR_DELETE(me); michael@0: } michael@0: michael@0: /** michael@0: * This is a wrapper that all threads invoke that allows us to set some michael@0: * things up prior to a thread's invocation and clean up after a thread has michael@0: * exited. michael@0: */ michael@0: static void* michael@0: _bt_root (void* arg) michael@0: { michael@0: PRThread *thred = (PRThread*)arg; michael@0: PRIntn rv; michael@0: void *privData; michael@0: status_t result; michael@0: int i; michael@0: michael@0: /* save our PRThread object into our TLS */ michael@0: tls_set(tls_prThreadSlot, thred); michael@0: michael@0: thred->startFunc(thred->arg); /* run the dang thing */ michael@0: michael@0: /* clean up */ michael@0: _bt_CleanupThread(NULL); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThread*) michael@0: PR_CreateThread (PRThreadType type, void (*start)(void* arg), void* arg, michael@0: PRThreadPriority priority, PRThreadScope scope, michael@0: PRThreadState state, PRUint32 stackSize) michael@0: { michael@0: PRUint32 bePriority; michael@0: michael@0: PRThread* thred; michael@0: michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: michael@0: thred = PR_NEWZAP(PRThread); michael@0: if (thred == NULL) michael@0: { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: return NULL; michael@0: } michael@0: michael@0: thred->md.joinSem = B_ERROR; michael@0: michael@0: thred->arg = arg; michael@0: thred->startFunc = start; michael@0: thred->priority = priority; michael@0: michael@0: if( state == PR_JOINABLE_THREAD ) michael@0: { michael@0: thred->state |= BT_THREAD_JOINABLE; michael@0: } michael@0: michael@0: /* keep some books */ michael@0: michael@0: PR_Lock( bt_book.ml ); michael@0: michael@0: if (type == PR_USER_THREAD) michael@0: { michael@0: bt_book.threadCount++; michael@0: } michael@0: michael@0: PR_Unlock( bt_book.ml ); michael@0: michael@0: bePriority = _bt_MapNSPRToNativePriority( priority ); michael@0: michael@0: thred->md.tid = spawn_thread((thread_func)_bt_root, "moz-thread", michael@0: bePriority, thred); michael@0: if (thred->md.tid < B_OK) { michael@0: PR_SetError(PR_UNKNOWN_ERROR, thred->md.tid); michael@0: PR_DELETE(thred); michael@0: return NULL; michael@0: } michael@0: michael@0: if (resume_thread(thred->md.tid) < B_OK) { michael@0: PR_SetError(PR_UNKNOWN_ERROR, 0); michael@0: PR_DELETE(thred); michael@0: return NULL; michael@0: } michael@0: michael@0: return thred; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThread*) michael@0: PR_AttachThread(PRThreadType type, PRThreadPriority priority, michael@0: PRThreadStack *stack) michael@0: { michael@0: /* PR_GetCurrentThread() will attach a thread if necessary */ michael@0: return PR_GetCurrentThread(); michael@0: } michael@0: michael@0: PR_IMPLEMENT(void) michael@0: PR_DetachThread() michael@0: { michael@0: /* we don't support detaching */ michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) michael@0: PR_JoinThread (PRThread* thred) michael@0: { michael@0: status_t eval, status; michael@0: michael@0: PR_ASSERT(thred != NULL); michael@0: michael@0: if ((thred->state & BT_THREAD_JOINABLE) == 0) michael@0: { michael@0: PR_SetError( PR_INVALID_ARGUMENT_ERROR, 0 ); michael@0: return( PR_FAILURE ); michael@0: } michael@0: michael@0: /* synchronize access to the thread's joinSem */ michael@0: PR_Lock(joinSemLock); michael@0: michael@0: if (thred->md.is_joining) michael@0: { michael@0: /* another thread is already waiting to join the specified michael@0: thread - we must fail */ michael@0: PR_Unlock(joinSemLock); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: /* let others know we are waiting to join */ michael@0: thred->md.is_joining = PR_TRUE; michael@0: michael@0: if (thred->md.joinSem == B_ERROR) michael@0: { michael@0: /* the thread hasn't finished yet - it is our responsibility to michael@0: allocate a joinSem and wait on it */ michael@0: thred->md.joinSem = create_sem(0, "join sem"); michael@0: michael@0: /* we're done changing the joinSem now */ michael@0: PR_Unlock(joinSemLock); michael@0: michael@0: /* wait for the thread to finish */ michael@0: while (acquire_sem(thred->md.joinSem) == B_INTERRUPTED); michael@0: michael@0: } michael@0: else michael@0: { michael@0: /* the thread has already finished, and has allocated the michael@0: joinSem itself - let it know it can finally die */ michael@0: delete_sem(thred->md.joinSem); michael@0: michael@0: PR_Unlock(joinSemLock); michael@0: } michael@0: michael@0: /* make sure the thread is dead */ michael@0: wait_for_thread(thred->md.tid, &eval); michael@0: michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThread*) michael@0: PR_GetCurrentThread () michael@0: { michael@0: PRThread* thred; michael@0: michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: michael@0: thred = (PRThread *)tls_get( tls_prThreadSlot); michael@0: if (thred == NULL) michael@0: { michael@0: /* this thread doesn't have a PRThread structure (it must be michael@0: a native thread not created by the NSPR) - assimilate it */ michael@0: thred = _bt_AttachThread(); michael@0: } michael@0: PR_ASSERT(NULL != thred); michael@0: michael@0: return thred; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThreadScope) michael@0: PR_GetThreadScope (const PRThread* thred) michael@0: { michael@0: PR_ASSERT(thred != NULL); michael@0: return PR_GLOBAL_THREAD; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThreadType) michael@0: PR_GetThreadType (const PRThread* thred) michael@0: { michael@0: PR_ASSERT(thred != NULL); michael@0: return (thred->state & BT_THREAD_SYSTEM) ? michael@0: PR_SYSTEM_THREAD : PR_USER_THREAD; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThreadState) michael@0: PR_GetThreadState (const PRThread* thred) michael@0: { michael@0: PR_ASSERT(thred != NULL); michael@0: return (thred->state & BT_THREAD_JOINABLE)? michael@0: PR_JOINABLE_THREAD: PR_UNJOINABLE_THREAD; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRThreadPriority) michael@0: PR_GetThreadPriority (const PRThread* thred) michael@0: { michael@0: PR_ASSERT(thred != NULL); michael@0: return thred->priority; michael@0: } /* PR_GetThreadPriority */ michael@0: michael@0: PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred, michael@0: PRThreadPriority newPri) michael@0: { michael@0: PRUint32 bePriority; michael@0: michael@0: PR_ASSERT( thred != NULL ); michael@0: michael@0: thred->priority = newPri; michael@0: bePriority = _bt_MapNSPRToNativePriority( newPri ); michael@0: set_thread_priority( thred->md.tid, bePriority ); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) michael@0: PR_NewThreadPrivateIndex (PRUintn* newIndex, michael@0: PRThreadPrivateDTOR destructor) michael@0: { michael@0: int32 index; michael@0: michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: michael@0: /* reserve the next available tpd slot */ michael@0: index = atomic_add( &tpd_slotsUsed, 1 ); michael@0: if (index >= BT_TPD_LIMIT) michael@0: { michael@0: /* no slots left - decrement value, then fail */ michael@0: atomic_add( &tpd_slotsUsed, -1 ); michael@0: PR_SetError( PR_TPD_RANGE_ERROR, 0 ); michael@0: return( PR_FAILURE ); michael@0: } michael@0: michael@0: /* allocate a beos-native TLS slot for this index (the new slot michael@0: automatically contains NULL) */ michael@0: tpd_beosTLSSlots[index] = tls_allocate(); michael@0: michael@0: /* remember the destructor */ michael@0: tpd_dtors[index] = destructor; michael@0: michael@0: *newIndex = (PRUintn)index; michael@0: michael@0: return( PR_SUCCESS ); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) michael@0: PR_SetThreadPrivate (PRUintn index, void* priv) michael@0: { michael@0: void *oldValue; michael@0: michael@0: /* michael@0: ** Sanity checking michael@0: */ michael@0: michael@0: if(index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT) michael@0: { michael@0: PR_SetError( PR_TPD_RANGE_ERROR, 0 ); michael@0: return( PR_FAILURE ); michael@0: } michael@0: michael@0: /* if the old value isn't NULL, and the dtor for this slot isn't michael@0: NULL, we must destroy the data */ michael@0: oldValue = tls_get(tpd_beosTLSSlots[index]); michael@0: if (oldValue != NULL && tpd_dtors[index] != NULL) michael@0: (*tpd_dtors[index])(oldValue); michael@0: michael@0: /* save new value */ michael@0: tls_set(tpd_beosTLSSlots[index], priv); michael@0: michael@0: return( PR_SUCCESS ); michael@0: } michael@0: michael@0: PR_IMPLEMENT(void*) michael@0: PR_GetThreadPrivate (PRUintn index) michael@0: { michael@0: /* make sure the index is valid */ michael@0: if (index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT) michael@0: { michael@0: PR_SetError( PR_TPD_RANGE_ERROR, 0 ); michael@0: return NULL; michael@0: } michael@0: michael@0: /* return the value */ michael@0: return tls_get( tpd_beosTLSSlots[index] ); michael@0: } michael@0: michael@0: michael@0: PR_IMPLEMENT(PRStatus) michael@0: PR_Interrupt (PRThread* thred) michael@0: { michael@0: PRIntn rv; michael@0: michael@0: PR_ASSERT(thred != NULL); michael@0: michael@0: /* michael@0: ** there seems to be a bug in beos R5 in which calling michael@0: ** resume_thread() on a blocked thread returns B_OK instead michael@0: ** of B_BAD_THREAD_STATE (beos bug #20000422-19095). as such, michael@0: ** to interrupt a thread, we will simply suspend then resume it michael@0: ** (no longer call resume_thread(), check for B_BAD_THREAD_STATE, michael@0: ** the suspend/resume to wake up a blocked thread). this wakes michael@0: ** up blocked threads properly, and doesn't hurt unblocked threads michael@0: ** (they simply get stopped then re-started immediately) michael@0: */ michael@0: michael@0: rv = suspend_thread( thred->md.tid ); michael@0: if( rv != B_NO_ERROR ) michael@0: { michael@0: /* this doesn't appear to be a valid thread_id */ michael@0: PR_SetError( PR_UNKNOWN_ERROR, rv ); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: rv = resume_thread( thred->md.tid ); michael@0: if( rv != B_NO_ERROR ) michael@0: { michael@0: PR_SetError( PR_UNKNOWN_ERROR, rv ); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PR_IMPLEMENT(void) michael@0: PR_ClearInterrupt () michael@0: { michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) michael@0: PR_Yield () michael@0: { michael@0: /* we just sleep for long enough to cause a reschedule (100 michael@0: microseconds) */ michael@0: snooze(100); michael@0: } michael@0: michael@0: #define BT_MILLION 1000000UL michael@0: michael@0: PR_IMPLEMENT(PRStatus) michael@0: PR_Sleep (PRIntervalTime ticks) michael@0: { michael@0: bigtime_t tps; michael@0: status_t status; michael@0: michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: michael@0: tps = PR_IntervalToMicroseconds( ticks ); michael@0: michael@0: status = snooze(tps); michael@0: if (status == B_NO_ERROR) return PR_SUCCESS; michael@0: michael@0: PR_SetError(PR_NOT_IMPLEMENTED_ERROR, status); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) michael@0: PR_Cleanup () michael@0: { michael@0: PRThread *me = PR_GetCurrentThread(); michael@0: michael@0: PR_ASSERT(me->state & BT_THREAD_PRIMORD); michael@0: if ((me->state & BT_THREAD_PRIMORD) == 0) { michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: PR_Lock( bt_book.ml ); michael@0: michael@0: if (bt_book.threadCount != 0) michael@0: { michael@0: /* we'll have to wait for some threads to finish - create a michael@0: sem to block on */ michael@0: bt_book.cleanUpSem = create_sem(0, "cleanup sem"); michael@0: } michael@0: michael@0: PR_Unlock( bt_book.ml ); michael@0: michael@0: /* note that, if all the user threads were already dead, we michael@0: wouldn't have created a sem above, so this acquire_sem() michael@0: will fail immediately */ michael@0: while (acquire_sem(bt_book.cleanUpSem) == B_INTERRUPTED); michael@0: michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PR_IMPLEMENT(void) michael@0: PR_ProcessExit (PRIntn status) michael@0: { michael@0: exit(status); michael@0: } michael@0: michael@0: PRThread *_bt_AttachThread() michael@0: { michael@0: PRThread *thread; michael@0: thread_info tInfo; michael@0: michael@0: /* make sure this thread doesn't already have a PRThread structure */ michael@0: PR_ASSERT(tls_get(tls_prThreadSlot) == NULL); michael@0: michael@0: /* allocate a PRThread structure for this thread */ michael@0: thread = PR_NEWZAP(PRThread); michael@0: if (thread == NULL) michael@0: { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: return NULL; michael@0: } michael@0: michael@0: /* get the native thread's current state */ michael@0: get_thread_info(find_thread(NULL), &tInfo); michael@0: michael@0: /* initialize new PRThread */ michael@0: thread->md.tid = tInfo.thread; michael@0: thread->md.joinSem = B_ERROR; michael@0: thread->priority = _bt_MapNativeToNSPRPriority(tInfo.priority); michael@0: michael@0: /* attached threads are always non-joinable user threads */ michael@0: thread->state = 0; michael@0: michael@0: /* increment user thread count */ michael@0: PR_Lock(bt_book.ml); michael@0: bt_book.threadCount++; michael@0: PR_Unlock(bt_book.ml); michael@0: michael@0: /* store this thread's PRThread */ michael@0: tls_set(tls_prThreadSlot, thread); michael@0: michael@0: /* the thread must call _bt_CleanupThread() before it dies, in order michael@0: to clean up its PRThread, synchronize with the primordial thread, michael@0: etc. */ michael@0: on_exit_thread(_bt_CleanupThread, NULL); michael@0: michael@0: return thread; michael@0: }