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