Bugzilla: 55271. BeOS attach thread NSPRPUB_RELEASE_4_0_BRANCH
authorlarryh%netscape.com
Tue, 10 Oct 2000 21:51:54 +0000
branchNSPRPUB_RELEASE_4_0_BRANCH
changeset 1591 4c85d9d3b27c6e27fccfe4b847d18a17c03beadb
parent 1590 8286c4d3cf08a61402f0bd9021109bad33122012
child 1599 3c821f22fb996eaf683440f821cdf13c6b1a9398
push idunknown
push userunknown
push dateunknown
bugs55271
Bugzilla: 55271. BeOS attach thread
pr/include/md/_beos.h
pr/src/bthreads/btthread.c
pr/tests/attach.c
--- a/pr/include/md/_beos.h
+++ b/pr/include/md/_beos.h
@@ -67,18 +67,19 @@
 #define HAVE_CVAR_BUILT_ON_SEM
 #define _PR_GLOBAL_THREADS_ONLY
 #define _PR_BTHREADS
 #define _PR_NEED_FAKE_POLL
 
 /* Define threading functions and objects as native BeOS */
 struct _MDThread {
     thread_id	tid;	/* BeOS thread handle */
-    bool	is_joinable;	/* TRUE if PR_JOINABLE */
-    bool	is_joining;	/* TRUE if we were woken up to join */
+	sem_id		joinSem;	/* sems used to synchronzie joining */
+	PRBool	is_joining;	/* TRUE if someone is currently waiting to
+						   join this thread */
 };
 
 struct _MDThreadStack {
     PRInt8	notused;
 };
 
 /*
  * Lock and Semaphore related definitions
--- a/pr/src/bthreads/btthread.c
+++ b/pr/src/bthreads/btthread.c
@@ -7,378 +7,411 @@
  * 
  * Software distributed under the MPL is distributed on an "AS IS" basis,
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the MPL
  * for the specific language governing rights and limitations under the
  * MPL.
  */
 
 #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 */
-    PRCondVar *cv;              /* used to signal global things */
+	sem_id cleanUpSem;		/* the primoridal thread will block on this
+							   sem while waiting for the user threads */
     PRInt32 threadCount;	/* user thred count */
 
-} bt_book = { 0 };
+} bt_book = { NULL, B_ERROR, 0 };
 
-/*
-** A structure at the root of the thread private data.  Each member of
-** the array keys[] points to a hash table based on the thread's ID.
-*/
+
+#define BT_TPD_LIMIT 128	/* number of TPD slots we'll provide (arbitrary) */
 
-struct _BT_PrivateData
-{
-    PRLock *lock;		/* A lock to coordinate access */
-    struct _BT_PrivateHash *keys[128];	/* Up to 128 keys, pointing to a hash table */
-
-} bt_privateRoot = { 0 };
-
-/*
-** A dynamically allocated structure that contains 256 hash buckets that
-** contain a linked list of thread IDs.  The hash is simply the last 8 bits
-** of the thread_id.  ( current thread_id & 0x000000FF )
-*/
+/* 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];
 
-struct _BT_PrivateHash
-{
-    void (PR_CALLBACK *destructor)(void *arg);	/* The destructor */
-    struct _BT_PrivateEntry *next[256]; /* Pointer to the first element in the list */
-};
+static vint32 tpd_slotsUsed=0;	/* number of currently-allocated TPD slots */
+static int32 tls_prThreadSlot;	/* TLS slot in which PRThread will be stored */
 
-/*
-** A dynamically allocated structure that is a member of a linked list of
-** thread IDs.
-*/
+/* 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;
 
-struct _BT_PrivateEntry
-{
-    struct _BT_PrivateEntry *next;		/* Pointer to the next thread */
-    thread_id threadID;			/* The BeOS thread ID */
-    void *data;				/* The data */
-};
-
-PRUint32 _bt_mapPriority( PRThreadPriority priority );
-PR_IMPLEMENT(void *) _bt_getThreadPrivate(PRUintn index);
+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;
-    PRLock   *tempLock;
-    PRUintn   tempKey;
     PRUint32  beThreadPriority;
 
+	/* allocate joinSem mutex */
+	joinSemLock = PR_NewLock();
+	if (joinSemLock == NULL)
+	{
+		PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+		return;
+    }
+
     /*
-    ** Create a NSPR structure for our primordial thread.
+    ** 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_mapPriority( priority );
+    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;
-    primordialThread->priority = priority;
+	if (type == PR_SYSTEM_THREAD)
+		primordialThread->state |= BT_THREAD_SYSTEM;
 
     /*
-    ** Initialize the thread tracking data structures
+    ** 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)
     */
-
-    bt_privateRoot.lock = PR_NewLock();
-    if( NULL == bt_privateRoot.lock )
-    {
-	PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
-	return;
-    }
-
-    /*
-    ** Grab a key.  We're guaranteed to be key #0, since we are
-    ** always the first one in.
-    */
-
-    if( PR_NewThreadPrivateIndex( &tempKey, NULL ) != PR_SUCCESS )
-    {
-	PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
-	return;
-    }
 	
-    PR_ASSERT( tempKey == 0 );
+	tls_prThreadSlot = tls_allocate();
 
     /*
     ** Stuff our new PRThread structure into our thread specific
     ** slot.
     */
+
+	tls_set(tls_prThreadSlot, primordialThread);
     
-    if( PR_SetThreadPrivate( (PRUint8) 0, (void *) primordialThread ) == PR_FAILURE )
-    {
-    	PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
-    	return;
-    }
-
-    /*
-    ** Allocate some memory to hold our global lock.  We never clean it
-    ** up later, but BeOS automatically frees memory when the thread
-    ** dies.
-    */
-    
+	/* allocate lock for bt_book */
     bt_book.ml = PR_NewLock();
     if( NULL == bt_book.ml )
     {
     	PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
 	return;
     }
+}
 
-    tempLock = PR_NewLock();
-    if( NULL == tempLock )
+PRUint32
+_bt_MapNSPRToNativePriority( PRThreadPriority priority )
     {
-    	PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
-	return;
-    }
-
-    bt_book.cv = PR_NewCondVar( tempLock );
-    if( NULL == bt_book.cv )
+    switch( priority )
     {
-	PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
-	return;
+    	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_mapPriority( PRThreadPriority priority )
+_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;
 
-    struct _BT_PrivateHash *hashTable;
-
-    /* Set within the current thread the pointer to our object. This
-       object will be deleted when the thread termintates.  */
-
-    result = PR_SetThreadPrivate( 0, (void *) thred );
-    PR_ASSERT( result == PR_SUCCESS );
+	/* save our PRThread object into our TLS */
+	tls_set(tls_prThreadSlot, thred);
 
     thred->startFunc(thred->arg);  /* run the dang thing */
 
-    /*
-    ** Call the destructor, if available.
-    */
-
-    PR_Lock( bt_privateRoot.lock );
-
-    for( i = 0; i < 128; i++ )
-    {
-	hashTable = bt_privateRoot.keys[i];
-
-	if( hashTable != NULL )
-	{
-	    if( hashTable->destructor != NULL )
-	    {
-		privData = _bt_getThreadPrivate( i );
-
-		if( privData != NULL )
-		{
-		    PR_Unlock( bt_privateRoot.lock );
-		    hashTable->destructor( privData );
-		    PR_Lock( bt_privateRoot.lock );
-		}
-	    }
-	}
-    }
-
-    PR_Unlock( bt_privateRoot.lock );
-
-    /* decrement our thread counters */
-
-    PR_Lock( bt_book.ml );
+	/* clean up */
+	_bt_CleanupThread(NULL);
 
-    if (thred->state & BT_THREAD_SYSTEM) {
-#if 0
-        bt_book.system -= 1;
-#endif
-    } else 
-    {
-	bt_book.threadCount--;
-
-	if( 0 == bt_book.threadCount )
-	{
-            PR_NotifyAllCondVar(bt_book.cv);
-	}
-    }
-
-    PR_Unlock( bt_book.ml );
-
-    if( thred->md.is_joinable == 1 )
-    {
-	/*
-	** This is a joinable thread.  Keep suspending
-	** until is_joining is set to 1
-	*/
-
-	if( thred->md.is_joining == 0 )
-	{
-	    suspend_thread( thred->md.tid );
-	}
-    }
-
-    /* delete the thread object */
-    PR_DELETE(thred);
-
-    result = PR_SetThreadPrivate( (PRUint8) 0, (void *) NULL );
-    PR_ASSERT( result == PR_SUCCESS );
-    exit_thread( 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 = PR_NEWZAP(PRThread);
+    PRThread* thred;
 
     if (!_pr_initialized) _PR_ImplicitInitialization();
 
-    if (thred != NULL) {
+	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->md.is_joinable = 1;
+	    thred->state |= BT_THREAD_JOINABLE;
 	}
-	else
-	{
-	    thred->md.is_joinable = 0;
-	}
-
-	thred->md.is_joining = 0;
 
         /* keep some books */
 
 	PR_Lock( bt_book.ml );
 
-        if (PR_SYSTEM_THREAD == type) {
-            thred->state |= BT_THREAD_SYSTEM;
-#if 0
-            bt_book.system += 1;
-#endif
-        } else {
+	if (type == PR_USER_THREAD)
+	{
 	    bt_book.threadCount++;
         }
 
 	PR_Unlock( bt_book.ml );
 
-	bePriority = _bt_mapPriority( priority );
+	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);
-            thred = NULL;
+			return NULL;
         }
 
         if (resume_thread(thred->md.tid) < B_OK) {
             PR_SetError(PR_UNKNOWN_ERROR, 0);
             PR_DELETE(thred);
-            thred = NULL;
+			return NULL;
         }
 
-    } else {
-        PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+    return thred;
     }
 
-    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->md.is_joinable != 1 )
+	if ((thred->state & BT_THREAD_JOINABLE) == 0)
     {
-	PR_SetError( PR_UNKNOWN_ERROR, 0 );
+	PR_SetError( PR_INVALID_ARGUMENT_ERROR, 0 );
 	return( PR_FAILURE );
     }
 
-    thred->md.is_joining = 1;
+	/* 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;
+	}
 
-    status = wait_for_thread(thred->md.tid, &eval);
+	/* let others know we are waiting to join */
+	thred->md.is_joining = PR_TRUE;
 
-    if (status < B_NO_ERROR) {
+	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);
 
-        PR_SetError(PR_UNKNOWN_ERROR, status);
-        return PR_FAILURE;
+	}
+	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 ()
 {
-    void* thred;
+    PRThread* thred;
 
     if (!_pr_initialized) _PR_ImplicitInitialization();
 
-    thred = PR_GetThreadPrivate( (PRUint8) 0 );
+    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 (PRThread*)thred;
+    return thred;
 }
 
 PR_IMPLEMENT(PRThreadScope)
     PR_GetThreadScope (const PRThread* thred)
 {
     PR_ASSERT(thred != NULL);
     return PR_GLOBAL_THREAD;
 }
@@ -390,17 +423,18 @@ PR_IMPLEMENT(PRThreadType)
     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 PR_JOINABLE_THREAD;
+    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 */
@@ -408,269 +442,90 @@ PR_IMPLEMENT(PRThreadPriority)
 PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred,
                                         PRThreadPriority newPri)
 {
     PRUint32 bePriority;
 
     PR_ASSERT( thred != NULL );
 
     thred->priority = newPri;
-    bePriority = _bt_mapPriority( newPri );
+    bePriority = _bt_MapNSPRToNativePriority( newPri );
     set_thread_priority( thred->md.tid, bePriority );
 }
 
 PR_IMPLEMENT(PRStatus)
     PR_NewThreadPrivateIndex (PRUintn* newIndex,
                               PRThreadPrivateDTOR destructor)
 {
-    PRUintn  index;
-    struct _BT_PrivateHash *tempPointer;
+	int32    index;
 
     if (!_pr_initialized) _PR_ImplicitInitialization();
 
-    /*
-    ** Grab the lock, or hang until it is free.  This is critical code,
-    ** and only one thread at a time should be going through it.
-    */
-
-    PR_Lock( bt_privateRoot.lock );
-
-    /*
-    ** Run through the array of keys, find the first one that's zero.
-    ** Exit if we hit the top of the array.
-    */
-
-    index = 0;
-
-    while( bt_privateRoot.keys[index] != 0 )
-    {
-	index++;
-
-	if( 128 == index )
+	/* reserve the next available tpd slot */
+	index = atomic_add( &tpd_slotsUsed, 1 );
+	if (index >= BT_TPD_LIMIT)
 	{
-	    PR_Unlock( bt_privateRoot.lock );
+		/* no slots left - decrement value, then fail */
+		atomic_add( &tpd_slotsUsed, -1 );
+		PR_SetError( PR_TPD_RANGE_ERROR, 0 );
 	    return( PR_FAILURE );
 	}
-    }
 
-    /*
-    ** Index has the first available zeroed slot.  Allocate a
-    ** _BT_PrivateHash structure, all zeroed.  Assuming that goes
-    ** well, return the index.
-    */
-
-    tempPointer = PR_NEWZAP( struct _BT_PrivateHash );
+	/* allocate a beos-native TLS slot for this index (the new slot
+	   automatically contains NULL) */
+	tpd_beosTLSSlots[index] = tls_allocate();
 
-    if( 0 == tempPointer ) {
-
-	PR_Unlock( bt_privateRoot.lock );
-	return( PR_FAILURE );
-    }
+	/* remember the destructor */
+	tpd_dtors[index] = destructor;
 
-    bt_privateRoot.keys[index] = tempPointer;
-    tempPointer->destructor = destructor;
-
-    PR_Unlock( bt_privateRoot.lock );
-
-    *newIndex = index;
+    *newIndex = (PRUintn)index;
 
     return( PR_SUCCESS );
 }
 
 PR_IMPLEMENT(PRStatus)
     PR_SetThreadPrivate (PRUintn index, void* priv)
 {
-    thread_id	currentThread;
-    PRUint8	hashBucket;
-    void       *tempPointer;
-
-    struct _BT_PrivateHash  *hashTable;
-    struct _BT_PrivateEntry *currentEntry;
-    struct _BT_PrivateEntry *previousEntry;
+	void *oldValue;
 
     /*
     ** Sanity checking
     */
 
-    if( index < 0 || index > 127 ) return( PR_FAILURE );
-
-    /*
-    ** Grab the thread ID for this thread.  Assign it to a hash bucket.
-    */
-
-    currentThread = find_thread( NULL );
-    hashBucket = currentThread & 0x000000FF;
-
-    /*
-    ** Lock out all other threads then grab the proper hash table based
-    ** on the passed index.
-    */
-
-    PR_Lock( bt_privateRoot.lock );
-
-    hashTable = bt_privateRoot.keys[index];
-
-    if( 0 == hashTable )
-    {
-	PR_Unlock( bt_privateRoot.lock );
-	return( PR_FAILURE );
-    }
-
-    /*
-    ** Search through the linked list the end is reached or an existing
-    ** entry is found.
-    */
-
-    currentEntry = hashTable->next[ hashBucket ];
-    previousEntry = NULL;
-
-    while( currentEntry != 0 )
+    if(index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
     {
-	if( currentEntry->threadID == currentThread )
-	{
-	    /*
-	    ** Found a structure previously created for this thread.
-	    ** Is there a destructor to be called?
-	    */
-
-	    if( hashTable->destructor != NULL )
-	    {
-		if( currentEntry->data != NULL )
-		{
-		    PR_Unlock( bt_privateRoot.lock );
-		    hashTable->destructor( currentEntry->data );
-		    PR_Lock( bt_privateRoot.lock );
-		}
-	    }
-
-	    /*
-	    ** If the data was not NULL, and there was a destructor,
-	    ** it has already been called.  Overwrite the existing
-	    ** data and return with success.
-	    */
-
-	    currentEntry->data = priv;
-	    PR_Unlock( bt_privateRoot.lock );
-	    return( PR_SUCCESS );
-	}
-
-	previousEntry = currentEntry;
-	currentEntry  = previousEntry->next;
-    }
-
-    /*
-    ** If we're here, we didn't find an entry for this thread.  Create
-    ** one and attach it to the end of the list.
-    */
-
-    currentEntry = PR_NEWZAP( struct _BT_PrivateEntry );
-
-    if( 0 == currentEntry )
-    {
-	PR_Unlock( bt_privateRoot.lock );
+		PR_SetError( PR_TPD_RANGE_ERROR, 0 );
 	return( PR_FAILURE );
     }
 
-    currentEntry->threadID = currentThread;
-    currentEntry->data     = priv;
-
-    if( 0 == previousEntry )
-    {
-	/*
-	** This is a special case.  This is the first entry in the list
-	** so set the hash table to point to this entry.
-	*/
-
-	hashTable->next[ hashBucket ] = currentEntry;
-    }
-    else
-    {
-	previousEntry->next = currentEntry;
-    }
-
-    PR_Unlock( bt_privateRoot.lock );
-
-    return( PR_SUCCESS );
-}
-
-PR_IMPLEMENT(void*)
-    _bt_getThreadPrivate(PRUintn index)
-{
-    thread_id	currentThread;
-    PRUint8	hashBucket;
-    void       *tempPointer;
-
-    struct _BT_PrivateHash  *hashTable;
-    struct _BT_PrivateEntry *currentEntry;
-
-    /*
-    ** Sanity checking
-    */
-
-    if( index < 0 || index > 127 ) return( NULL );
-
-    /*
-    ** Grab the thread ID for this thread.  Assign it to a hash bucket.
-    */
+	/* 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);
 
-    currentThread = find_thread( NULL );
-    hashBucket = currentThread & 0x000000FF;
-
-    /*
-    ** Grab the proper hash table based on the passed index.
-    */
-
-    hashTable = bt_privateRoot.keys[index];
-
-    if( 0 == hashTable )
-    {   
-	return( NULL );
-    }
-
-    /*
-    ** Search through the linked list the end is reached or an existing
-    ** entry is found.
-    */
-
-    currentEntry = hashTable->next[ hashBucket ];
+	/* save new value */
+	tls_set(tpd_beosTLSSlots[index], priv);
 
-    while( currentEntry != 0 )
-    {   
-	if( currentEntry->threadID == currentThread )
-	{   
-	    /*
-	    ** Found a structure previously created for this thread.
-	    ** Copy out the data, unlock, and return.
-	    */
-
-	    tempPointer = currentEntry->data;
-	    return( tempPointer );
+	    return( PR_SUCCESS );
 	}
 
-	currentEntry  = currentEntry->next;
-    }
-
-    /*
-    ** Ooops, we ran out of entries.  This thread isn't listed.
-    */
-
-    return( NULL );
-}
-
 PR_IMPLEMENT(void*)
     PR_GetThreadPrivate (PRUintn index)
 {
-    void *returnValue;
+	/* 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;
+    }
 
-    PR_Lock( bt_privateRoot.lock );
-    returnValue = _bt_getThreadPrivate( index );
-    PR_Unlock( bt_privateRoot.lock );
-
-    return( returnValue );
-}
+	/* return the value */
+	return tls_get( tpd_beosTLSSlots[index] );
+	}
 
 
 PR_IMPLEMENT(PRStatus)
     PR_Interrupt (PRThread* thred)
 {
     PRIntn rv;
 
     PR_ASSERT(thred != NULL);
@@ -743,36 +598,73 @@ PR_IMPLEMENT(PRStatus)
 
     PR_ASSERT(me->state & BT_THREAD_PRIMORD);
     if ((me->state & BT_THREAD_PRIMORD) == 0) {
         return PR_FAILURE;
     }
 
     PR_Lock( bt_book.ml );
 
-    while( bt_book.threadCount > 0 )
+	if (bt_book.threadCount != 0)
     {
-	PR_Unlock( bt_book.ml );
-        PR_WaitCondVar(bt_book.cv, PR_INTERVAL_NO_TIMEOUT);
-	PR_Lock( bt_book.ml );
+		/* 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 );
 
-#if 0
-    /* I am not sure if it's safe to delete the cv and lock here, since
-     * there may still be "system" threads around. If this call isn't
-     * immediately prior to exiting, then there's a problem. */
-    if (0 == bt_book.system) {
-        PR_DestroyCondVar(bt_book.cv); bt_book.cv = NULL;
-        PR_DestroyLock(bt_book.ml); bt_book.ml = NULL;
-    }
-    PR_DELETE(me);
-#endif
+	/* 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;
+}
--- a/pr/tests/attach.c
+++ b/pr/tests/attach.c
@@ -31,25 +31,16 @@
 **			 The regress mode will be the default mode. Since the regress tool limits
 **           the output to a one line status:PASS or FAIL,all of the printf statements
 **			 have been handled with an if (debug_mode) statement.
 ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
 **			recognize the return code from tha main program.
 ** 12-June-97 Revert to return code 0 and 1.
 ***********************************************************************/
 
-#ifdef XP_BEOS
-#include <stdio.h>
-int main()
-{
-    printf( "This test currently does not run on BeOS\n" );
-    return 0;
-}
-#else
-
 /***********************************************************************
 ** Includes
 ***********************************************************************/
 
 /* Used to get the command line option */
 #include "nspr.h"
 #include "pprthred.h"
 #include "plgetopt.h"
@@ -69,16 +60,18 @@ int main()
 #include <errno.h>
 #elif defined(SOLARIS)
 #include <thread.h>
 #elif defined(OS2)
 #define INCL_DOS
 #define INCL_ERRORS
 #include <os2.h>
 #include <process.h>
+#elif defined(XP_BEOS)
+#include <kernel/OS.h>
 #endif
 
 #define DEFAULT_COUNT 1000
 PRIntn failed_already=0;
 PRIntn debug_mode;
 
 
 int count;
@@ -120,16 +113,18 @@ static void Measure(void (*func)(void), 
 	if (debug_mode)
     printf("%40s: %6.2f usec\n", msg, d / count);
 }
 
 #ifdef WIN32
 static unsigned __stdcall threadStartFunc(void *arg)
 #elif defined(IRIX) && !defined(_PR_PTHREADS)
 static void threadStartFunc(void *arg)
+#elif defined(XP_BEOS)
+static int32 threadStartFunc(void *arg)
 #else
 static void * threadStartFunc(void *arg)
 #endif
 {
 #ifdef _PR_DCETHREADS
     {
         int rv;
         pthread_t self = pthread_self();
@@ -160,16 +155,20 @@ int main(int argc, char **argv)
     unsigned threadID;
     HANDLE hThread;
 #elif defined(IRIX)
     int rv;
     int threadID;
 #elif defined(OS2)
     int rv;
     TID threadID;
+#elif defined(XP_BEOS)
+	thread_id threadID;
+	int32 threadRV;
+	status_t waitRV;
 #endif
 
 	/* The command line argument: -d is used to determine if the test is being run
 	in debug mode. The regress tool requires only one line output:PASS or FAIL.
 	All of the printfs associated with this test has been handled with a if (debug_mode)
 	test.
 	Usage: test_name [-d] [-c n]
 	*/
@@ -323,26 +322,46 @@ int main(int argc, char **argv)
     }
     rv = DosWaitThread(&threadID, DCWW_WAIT);
     if (debug_mode)PR_ASSERT(rv == NO_ERROR);
 	else if (rv == NO_ERROR) {
 		failed_already=1;
 		goto exit_now;
 	}
 
+#elif defined(XP_BEOS)
+	
+	threadID = spawn_thread(threadStartFunc, NULL, B_NORMAL_PRIORITY, NULL);
+	if (threadID <= B_ERROR) {
+		fprintf(stderr, "thread creation failed: error code %08lx\n", threadID);
+		failed_already = 1;
+		goto exit_now;
+	}
+	if (resume_thread(threadID) != B_OK) {
+		fprintf(stderr, "failed starting thread: error code %08lx\n", threadID);
+		failed_already = 1;
+		goto exit_now;
+	}
+
+	waitRV = wait_for_thread(threadID, &threadRV);
+	if (debug_mode)
+		PR_ASSERT(waitRV == B_OK);
+	else if (waitRV != B_OK) {
+		failed_already = 1;
+		goto exit_now;
+	}
+	
 #else
 	if (!debug_mode)
 		failed_already=1;
 	else	
 		printf("The attach test does not apply to this platform because\n"
 	    "either this platform does not have native threads or the\n"
 	    "test needs to be written for this platform.\n");
 	goto exit_now;
 #endif
 
 exit_now:
    if(failed_already)	
 		return 1;
 	else
 		return 0;
 }
-
-#endif /* XP_BEOS */