Back to index

lightning-sunbird  0.9+nobinonly
btthread.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include <kernel/OS.h>
00039 #include <support/TLS.h>
00040 
00041 #include "prlog.h"
00042 #include "primpl.h"
00043 #include "prcvar.h"
00044 #include "prpdce.h"
00045 
00046 #include <stdlib.h>
00047 #include <string.h>
00048 #include <signal.h>
00049 
00050 /* values for PRThread.state */
00051 #define BT_THREAD_PRIMORD   0x01    /* this is the primordial thread */
00052 #define BT_THREAD_SYSTEM    0x02    /* this is a system thread */
00053 #define BT_THREAD_JOINABLE  0x04   /* this is a joinable thread */
00054 
00055 struct _BT_Bookeeping
00056 {
00057     PRLock *ml;                 /* a lock to protect ourselves */
00058        sem_id cleanUpSem;          /* the primoridal thread will block on this
00059                                                     sem while waiting for the user threads */
00060     PRInt32 threadCount;    /* user thred count */
00061 
00062 } bt_book = { NULL, B_ERROR, 0 };
00063 
00064 
00065 #define BT_TPD_LIMIT 128    /* number of TPD slots we'll provide (arbitrary) */
00066 
00067 /* these will be used to map an index returned by PR_NewThreadPrivateIndex()
00068    to the corresponding beos native TLS slot number, and to the destructor
00069    for that slot - note that, because it is allocated globally, this data
00070    will be automatically zeroed for us when the program begins */
00071 static int32 tpd_beosTLSSlots[BT_TPD_LIMIT];
00072 static PRThreadPrivateDTOR tpd_dtors[BT_TPD_LIMIT];
00073 
00074 static vint32 tpd_slotsUsed=0;     /* number of currently-allocated TPD slots */
00075 static int32 tls_prThreadSlot;     /* TLS slot in which PRThread will be stored */
00076 
00077 /* this mutex will be used to synchronize access to every
00078    PRThread.md.joinSem and PRThread.md.is_joining (we could
00079    actually allocate one per thread, but that seems a bit excessive,
00080    especially considering that there will probably be little
00081    contention, PR_JoinThread() is allowed to block anyway, and the code
00082    protected by the mutex is short/fast) */
00083 static PRLock *joinSemLock;
00084 
00085 static PRUint32 _bt_MapNSPRToNativePriority( PRThreadPriority priority );
00086 static PRThreadPriority _bt_MapNativeToNSPRPriority( PRUint32 priority );
00087 static void _bt_CleanupThread(void *arg);
00088 static PRThread *_bt_AttachThread();
00089 
00090 void
00091 _PR_InitThreads (PRThreadType type, PRThreadPriority priority,
00092                  PRUintn maxPTDs)
00093 {
00094     PRThread *primordialThread;
00095     PRUint32  beThreadPriority;
00096 
00097        /* allocate joinSem mutex */
00098        joinSemLock = PR_NewLock();
00099        if (joinSemLock == NULL)
00100        {
00101               PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00102               return;
00103     }
00104 
00105     /*
00106     ** Create and initialize NSPR structure for our primordial thread.
00107     */
00108     
00109     primordialThread = PR_NEWZAP(PRThread);
00110     if( NULL == primordialThread )
00111     {
00112         PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
00113         return;
00114     }
00115 
00116        primordialThread->md.joinSem = B_ERROR;
00117 
00118     /*
00119     ** Set the priority to the desired level.
00120     */
00121 
00122     beThreadPriority = _bt_MapNSPRToNativePriority( priority );
00123     
00124     set_thread_priority( find_thread( NULL ), beThreadPriority );
00125     
00126     primordialThread->priority = priority;
00127 
00128 
00129        /* set the thread's state - note that the thread is not joinable */
00130     primordialThread->state |= BT_THREAD_PRIMORD;
00131        if (type == PR_SYSTEM_THREAD)
00132               primordialThread->state |= BT_THREAD_SYSTEM;
00133 
00134     /*
00135     ** Allocate a TLS slot for the PRThread structure (just using
00136     ** native TLS, as opposed to NSPR TPD, will make PR_GetCurrentThread()
00137     ** somewhat faster, and will leave one more TPD slot for our client)
00138     */
00139        
00140        tls_prThreadSlot = tls_allocate();
00141 
00142     /*
00143     ** Stuff our new PRThread structure into our thread specific
00144     ** slot.
00145     */
00146 
00147        tls_set(tls_prThreadSlot, primordialThread);
00148     
00149        /* allocate lock for bt_book */
00150     bt_book.ml = PR_NewLock();
00151     if( NULL == bt_book.ml )
00152     {
00153        PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
00154        return;
00155     }
00156 }
00157 
00158 PRUint32
00159 _bt_MapNSPRToNativePriority( PRThreadPriority priority )
00160     {
00161     switch( priority )
00162     {
00163        case PR_PRIORITY_LOW:        return( B_LOW_PRIORITY );
00164        case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY );
00165        case PR_PRIORITY_HIGH:       return( B_DISPLAY_PRIORITY );
00166        case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY );
00167        default:              return( B_NORMAL_PRIORITY );
00168     }
00169 }
00170 
00171 PRThreadPriority
00172 _bt_MapNativeToNSPRPriority(PRUint32 priority)
00173     {
00174        if (priority < B_NORMAL_PRIORITY)
00175               return PR_PRIORITY_LOW;
00176        if (priority < B_DISPLAY_PRIORITY)
00177               return PR_PRIORITY_NORMAL;
00178        if (priority < B_URGENT_DISPLAY_PRIORITY)
00179               return PR_PRIORITY_HIGH;
00180        return PR_PRIORITY_URGENT;
00181 }
00182 
00183 PRUint32
00184 _bt_mapNativeToNSPRPriority( int32 priority )
00185 {
00186     switch( priority )
00187     {
00188        case PR_PRIORITY_LOW:        return( B_LOW_PRIORITY );
00189        case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY );
00190        case PR_PRIORITY_HIGH:       return( B_DISPLAY_PRIORITY );
00191        case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY );
00192        default:              return( B_NORMAL_PRIORITY );
00193     }
00194 }
00195 
00196 /* This method is called by all NSPR threads as they exit */
00197 void _bt_CleanupThread(void *arg)
00198 {
00199        PRThread *me = PR_GetCurrentThread();
00200        int32 i;
00201 
00202        /* first, clean up all thread-private data */
00203        for (i = 0; i < tpd_slotsUsed; i++)
00204        {
00205               void *oldValue = tls_get(tpd_beosTLSSlots[i]);
00206               if ( oldValue != NULL && tpd_dtors[i] != NULL )
00207                      (*tpd_dtors[i])(oldValue);
00208        }
00209 
00210        /* if this thread is joinable, wait for someone to join it */
00211        if (me->state & BT_THREAD_JOINABLE)
00212        {
00213               /* protect access to our joinSem */
00214               PR_Lock(joinSemLock);
00215 
00216               if (me->md.is_joining)
00217               {
00218                      /* someone is already waiting to join us (they've
00219                         allocated a joinSem for us) - let them know we're
00220                         ready */
00221                      delete_sem(me->md.joinSem);
00222 
00223                      PR_Unlock(joinSemLock);
00224 
00225               }
00226               else
00227     {
00228                      /* noone is currently waiting for our demise - it
00229                         is our responsibility to allocate the joinSem
00230                         and block on it */
00231                      me->md.joinSem = create_sem(0, "join sem");
00232 
00233                      /* we're done accessing our joinSem */
00234                      PR_Unlock(joinSemLock);
00235 
00236                      /* wait for someone to join us */
00237                      while (acquire_sem(me->md.joinSem) == B_INTERRUPTED);
00238            }
00239        }
00240 
00241        /* if this is a user thread, we must update our books */
00242        if ((me->state & BT_THREAD_SYSTEM) == 0)
00243        {
00244               /* synchronize access to bt_book */
00245     PR_Lock( bt_book.ml );
00246 
00247               /* decrement the number of currently-alive user threads */
00248        bt_book.threadCount--;
00249 
00250               if (bt_book.threadCount == 0 && bt_book.cleanUpSem != B_ERROR) {
00251                      /* we are the last user thread, and the primordial thread is
00252                         blocked in PR_Cleanup() waiting for us to finish - notify
00253                         it */
00254                      delete_sem(bt_book.cleanUpSem);
00255        }
00256 
00257     PR_Unlock( bt_book.ml );
00258        }
00259 
00260        /* finally, delete this thread's PRThread */
00261        PR_DELETE(me);
00262 }
00263 
00269 static void*
00270 _bt_root (void* arg)
00271        {
00272     PRThread *thred = (PRThread*)arg;
00273     PRIntn rv;
00274     void *privData;
00275     status_t result;
00276     int i;
00277 
00278        /* save our PRThread object into our TLS */
00279        tls_set(tls_prThreadSlot, thred);
00280 
00281     thred->startFunc(thred->arg);  /* run the dang thing */
00282 
00283        /* clean up */
00284        _bt_CleanupThread(NULL);
00285 
00286        return 0;
00287 }
00288 
00289 PR_IMPLEMENT(PRThread*)
00290     PR_CreateThread (PRThreadType type, void (*start)(void* arg), void* arg,
00291                      PRThreadPriority priority, PRThreadScope scope,
00292                      PRThreadState state, PRUint32 stackSize)
00293 {
00294     PRUint32 bePriority;
00295 
00296     PRThread* thred;
00297 
00298     if (!_pr_initialized) _PR_ImplicitInitialization();
00299 
00300        thred = PR_NEWZAP(PRThread);
00301        if (thred == NULL)
00302        {
00303         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00304         return NULL;
00305     }
00306 
00307     thred->md.joinSem = B_ERROR;
00308 
00309         thred->arg = arg;
00310         thred->startFunc = start;
00311         thred->priority = priority;
00312 
00313        if( state == PR_JOINABLE_THREAD )
00314        {
00315            thred->state |= BT_THREAD_JOINABLE;
00316        }
00317 
00318         /* keep some books */
00319 
00320        PR_Lock( bt_book.ml );
00321 
00322        if (type == PR_USER_THREAD)
00323        {
00324            bt_book.threadCount++;
00325         }
00326 
00327        PR_Unlock( bt_book.ml );
00328 
00329        bePriority = _bt_MapNSPRToNativePriority( priority );
00330 
00331         thred->md.tid = spawn_thread((thread_func)_bt_root, "moz-thread",
00332                                      bePriority, thred);
00333         if (thred->md.tid < B_OK) {
00334             PR_SetError(PR_UNKNOWN_ERROR, thred->md.tid);
00335             PR_DELETE(thred);
00336                      return NULL;
00337         }
00338 
00339         if (resume_thread(thred->md.tid) < B_OK) {
00340             PR_SetError(PR_UNKNOWN_ERROR, 0);
00341             PR_DELETE(thred);
00342                      return NULL;
00343         }
00344 
00345     return thred;
00346     }
00347 
00348 PR_IMPLEMENT(PRThread*)
00349        PR_AttachThread(PRThreadType type, PRThreadPriority priority,
00350                                    PRThreadStack *stack)
00351 {
00352        /* PR_GetCurrentThread() will attach a thread if necessary */
00353        return PR_GetCurrentThread();
00354 }
00355 
00356 PR_IMPLEMENT(void)
00357        PR_DetachThread()
00358 {
00359        /* we don't support detaching */
00360 }
00361 
00362 PR_IMPLEMENT(PRStatus)
00363     PR_JoinThread (PRThread* thred)
00364 {
00365     status_t eval, status;
00366 
00367     PR_ASSERT(thred != NULL);
00368 
00369        if ((thred->state & BT_THREAD_JOINABLE) == 0)
00370     {
00371        PR_SetError( PR_INVALID_ARGUMENT_ERROR, 0 );
00372        return( PR_FAILURE );
00373     }
00374 
00375        /* synchronize access to the thread's joinSem */
00376        PR_Lock(joinSemLock);
00377        
00378        if (thred->md.is_joining)
00379        {
00380               /* another thread is already waiting to join the specified
00381                  thread - we must fail */
00382               PR_Unlock(joinSemLock);
00383               return PR_FAILURE;
00384        }
00385 
00386        /* let others know we are waiting to join */
00387        thred->md.is_joining = PR_TRUE;
00388 
00389        if (thred->md.joinSem == B_ERROR)
00390        {
00391               /* the thread hasn't finished yet - it is our responsibility to
00392                  allocate a joinSem and wait on it */
00393               thred->md.joinSem = create_sem(0, "join sem");
00394 
00395               /* we're done changing the joinSem now */
00396               PR_Unlock(joinSemLock);
00397 
00398               /* wait for the thread to finish */
00399               while (acquire_sem(thred->md.joinSem) == B_INTERRUPTED);
00400 
00401        }
00402        else
00403        {
00404               /* the thread has already finished, and has allocated the
00405                  joinSem itself - let it know it can finally die */
00406               delete_sem(thred->md.joinSem);
00407               
00408               PR_Unlock(joinSemLock);
00409     }
00410 
00411        /* make sure the thread is dead */
00412     wait_for_thread(thred->md.tid, &eval);
00413 
00414     return PR_SUCCESS;
00415 }
00416 
00417 PR_IMPLEMENT(PRThread*)
00418     PR_GetCurrentThread ()
00419 {
00420     PRThread* thred;
00421 
00422     if (!_pr_initialized) _PR_ImplicitInitialization();
00423 
00424     thred = (PRThread *)tls_get( tls_prThreadSlot);
00425        if (thred == NULL)
00426        {
00427               /* this thread doesn't have a PRThread structure (it must be
00428                  a native thread not created by the NSPR) - assimilate it */
00429               thred = _bt_AttachThread();
00430        }
00431     PR_ASSERT(NULL != thred);
00432 
00433     return thred;
00434 }
00435 
00436 PR_IMPLEMENT(PRThreadScope)
00437     PR_GetThreadScope (const PRThread* thred)
00438 {
00439     PR_ASSERT(thred != NULL);
00440     return PR_GLOBAL_THREAD;
00441 }
00442 
00443 PR_IMPLEMENT(PRThreadType)
00444     PR_GetThreadType (const PRThread* thred)
00445 {
00446     PR_ASSERT(thred != NULL);
00447     return (thred->state & BT_THREAD_SYSTEM) ?
00448         PR_SYSTEM_THREAD : PR_USER_THREAD;
00449 }
00450 
00451 PR_IMPLEMENT(PRThreadState)
00452     PR_GetThreadState (const PRThread* thred)
00453 {
00454     PR_ASSERT(thred != NULL);
00455     return (thred->state & BT_THREAD_JOINABLE)?
00456                                    PR_JOINABLE_THREAD: PR_UNJOINABLE_THREAD;
00457 }
00458 
00459 PR_IMPLEMENT(PRThreadPriority)
00460     PR_GetThreadPriority (const PRThread* thred)
00461 {
00462     PR_ASSERT(thred != NULL);
00463     return thred->priority;
00464 }  /* PR_GetThreadPriority */
00465 
00466 PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred,
00467                                         PRThreadPriority newPri)
00468 {
00469     PRUint32 bePriority;
00470 
00471     PR_ASSERT( thred != NULL );
00472 
00473     thred->priority = newPri;
00474     bePriority = _bt_MapNSPRToNativePriority( newPri );
00475     set_thread_priority( thred->md.tid, bePriority );
00476 }
00477 
00478 PR_IMPLEMENT(PRStatus)
00479     PR_NewThreadPrivateIndex (PRUintn* newIndex,
00480                               PRThreadPrivateDTOR destructor)
00481 {
00482        int32    index;
00483 
00484     if (!_pr_initialized) _PR_ImplicitInitialization();
00485 
00486        /* reserve the next available tpd slot */
00487        index = atomic_add( &tpd_slotsUsed, 1 );
00488        if (index >= BT_TPD_LIMIT)
00489        {
00490               /* no slots left - decrement value, then fail */
00491               atomic_add( &tpd_slotsUsed, -1 );
00492               PR_SetError( PR_TPD_RANGE_ERROR, 0 );
00493            return( PR_FAILURE );
00494        }
00495 
00496        /* allocate a beos-native TLS slot for this index (the new slot
00497           automatically contains NULL) */
00498        tpd_beosTLSSlots[index] = tls_allocate();
00499 
00500        /* remember the destructor */
00501        tpd_dtors[index] = destructor;
00502 
00503     *newIndex = (PRUintn)index;
00504 
00505     return( PR_SUCCESS );
00506 }
00507 
00508 PR_IMPLEMENT(PRStatus)
00509     PR_SetThreadPrivate (PRUintn index, void* priv)
00510 {
00511        void *oldValue;
00512 
00513     /*
00514     ** Sanity checking
00515     */
00516 
00517     if(index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
00518     {
00519               PR_SetError( PR_TPD_RANGE_ERROR, 0 );
00520        return( PR_FAILURE );
00521     }
00522 
00523        /* if the old value isn't NULL, and the dtor for this slot isn't
00524           NULL, we must destroy the data */
00525        oldValue = tls_get(tpd_beosTLSSlots[index]);
00526        if (oldValue != NULL && tpd_dtors[index] != NULL)
00527               (*tpd_dtors[index])(oldValue);
00528 
00529        /* save new value */
00530        tls_set(tpd_beosTLSSlots[index], priv);
00531 
00532            return( PR_SUCCESS );
00533        }
00534 
00535 PR_IMPLEMENT(void*)
00536     PR_GetThreadPrivate (PRUintn index)
00537 {
00538        /* make sure the index is valid */
00539        if (index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
00540     {   
00541               PR_SetError( PR_TPD_RANGE_ERROR, 0 );
00542               return NULL;
00543     }
00544 
00545        /* return the value */
00546        return tls_get( tpd_beosTLSSlots[index] );
00547        }
00548 
00549 
00550 PR_IMPLEMENT(PRStatus)
00551     PR_Interrupt (PRThread* thred)
00552 {
00553     PRIntn rv;
00554 
00555     PR_ASSERT(thred != NULL);
00556 
00557     /*
00558     ** there seems to be a bug in beos R5 in which calling
00559     ** resume_thread() on a blocked thread returns B_OK instead
00560     ** of B_BAD_THREAD_STATE (beos bug #20000422-19095).  as such,
00561     ** to interrupt a thread, we will simply suspend then resume it
00562     ** (no longer call resume_thread(), check for B_BAD_THREAD_STATE,
00563     ** the suspend/resume to wake up a blocked thread).  this wakes
00564     ** up blocked threads properly, and doesn't hurt unblocked threads
00565     ** (they simply get stopped then re-started immediately)
00566     */
00567 
00568     rv = suspend_thread( thred->md.tid );
00569     if( rv != B_NO_ERROR )
00570     {
00571         /* this doesn't appear to be a valid thread_id */
00572         PR_SetError( PR_UNKNOWN_ERROR, rv );
00573         return PR_FAILURE;
00574     }
00575 
00576     rv = resume_thread( thred->md.tid );
00577     if( rv != B_NO_ERROR )
00578     {
00579         PR_SetError( PR_UNKNOWN_ERROR, rv );
00580         return PR_FAILURE;
00581     }
00582 
00583     return PR_SUCCESS;
00584 }
00585 
00586 PR_IMPLEMENT(void)
00587     PR_ClearInterrupt ()
00588 {
00589 }
00590 
00591 PR_IMPLEMENT(PRStatus)
00592     PR_Yield ()
00593 {
00594     /* we just sleep for long enough to cause a reschedule (100
00595        microseconds) */
00596     snooze(100);
00597 }
00598 
00599 #define BT_MILLION 1000000UL
00600 
00601 PR_IMPLEMENT(PRStatus)
00602     PR_Sleep (PRIntervalTime ticks)
00603 {
00604     bigtime_t tps;
00605     status_t status;
00606 
00607     if (!_pr_initialized) _PR_ImplicitInitialization();
00608 
00609     tps = PR_IntervalToMicroseconds( ticks );
00610 
00611     status = snooze(tps);
00612     if (status == B_NO_ERROR) return PR_SUCCESS;
00613 
00614     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, status);
00615     return PR_FAILURE;
00616 }
00617 
00618 PR_IMPLEMENT(PRStatus)
00619     PR_Cleanup ()
00620 {
00621     PRThread *me = PR_CurrentThread();
00622 
00623     PR_ASSERT(me->state & BT_THREAD_PRIMORD);
00624     if ((me->state & BT_THREAD_PRIMORD) == 0) {
00625         return PR_FAILURE;
00626     }
00627 
00628     PR_Lock( bt_book.ml );
00629 
00630        if (bt_book.threadCount != 0)
00631     {
00632               /* we'll have to wait for some threads to finish - create a
00633                  sem to block on */
00634               bt_book.cleanUpSem = create_sem(0, "cleanup sem");
00635     }
00636 
00637     PR_Unlock( bt_book.ml );
00638 
00639        /* note that, if all the user threads were already dead, we
00640           wouldn't have created a sem above, so this acquire_sem()
00641           will fail immediately */
00642        while (acquire_sem(bt_book.cleanUpSem) == B_INTERRUPTED);
00643 
00644     return PR_SUCCESS;
00645 }
00646 
00647 PR_IMPLEMENT(void)
00648     PR_ProcessExit (PRIntn status)
00649 {
00650     exit(status);
00651 }
00652 
00653 PRThread *_bt_AttachThread()
00654 {
00655        PRThread *thread;
00656        thread_info tInfo;
00657 
00658        /* make sure this thread doesn't already have a PRThread structure */
00659        PR_ASSERT(tls_get(tls_prThreadSlot) == NULL);
00660 
00661        /* allocate a PRThread structure for this thread */
00662        thread = PR_NEWZAP(PRThread);
00663        if (thread == NULL)
00664        {
00665               PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00666               return NULL;
00667        }
00668 
00669        /* get the native thread's current state */
00670        get_thread_info(find_thread(NULL), &tInfo);
00671 
00672        /* initialize new PRThread */
00673        thread->md.tid = tInfo.thread;
00674        thread->md.joinSem = B_ERROR;
00675        thread->priority = _bt_MapNativeToNSPRPriority(tInfo.priority);
00676 
00677        /* attached threads are always non-joinable user threads */
00678        thread->state = 0;
00679 
00680        /* increment user thread count */
00681        PR_Lock(bt_book.ml);
00682        bt_book.threadCount++;
00683        PR_Unlock(bt_book.ml);
00684 
00685        /* store this thread's PRThread */
00686        tls_set(tls_prThreadSlot, thread);
00687        
00688        /* the thread must call _bt_CleanupThread() before it dies, in order
00689           to clean up its PRThread, synchronize with the primordial thread,
00690           etc. */
00691        on_exit_thread(_bt_CleanupThread, NULL);
00692        
00693        return thread;
00694 }