summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlarryh%netscape.com <devnull@localhost>2000-10-10 20:27:32 +0000
committerlarryh%netscape.com <devnull@localhost>2000-10-10 20:27:32 +0000
commit9fe845c92575ac1b080e38aa47d1c9bd4e4470b2 (patch)
treed800dea306eb051d5570ac4a55343efa82912872
parent4e443117f2cc1fc7ab1ca341edbcbd727dc81cfc (diff)
downloadnspr-hg-9fe845c92575ac1b080e38aa47d1c9bd4e4470b2.tar.gz
Bugzilla: 55271. Beos attach thread
-rw-r--r--pr/include/md/_beos.h5
-rw-r--r--pr/src/bthreads/btthread.c696
-rw-r--r--pr/tests/attach.c41
3 files changed, 327 insertions, 415 deletions
diff --git a/pr/include/md/_beos.h b/pr/include/md/_beos.h
index 8b56dec3..2cb25407 100644
--- a/pr/include/md/_beos.h
+++ b/pr/include/md/_beos.h
@@ -72,8 +72,9 @@
/* 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 {
diff --git a/pr/src/bthreads/btthread.c b/pr/src/bthreads/btthread.c
index e7e7997f..35fdba38 100644
--- a/pr/src/bthreads/btthread.c
+++ b/pr/src/bthreads/btthread.c
@@ -12,6 +12,7 @@
*/
#include <kernel/OS.h>
+#include <support/TLS.h>
#include "prlog.h"
#include "primpl.h"
@@ -22,67 +23,63 @@
#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.
-*/
-
-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 )
-*/
+#define BT_TPD_LIMIT 128 /* number of TPD slots we'll provide (arbitrary) */
-struct _BT_PrivateHash
-{
- void (PR_CALLBACK *destructor)(void *arg); /* The destructor */
- struct _BT_PrivateEntry *next[256]; /* Pointer to the first element in the list */
-};
+/* 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];
-/*
-** A dynamically allocated structure that is a member of a linked list of
-** thread IDs.
-*/
+static vint32 tpd_slotsUsed=0; /* number of currently-allocated TPD slots */
+static int32 tls_prThreadSlot; /* TLS slot in which PRThread will be stored */
-struct _BT_PrivateEntry
-{
- struct _BT_PrivateEntry *next; /* Pointer to the next thread */
- thread_id threadID; /* The BeOS thread ID */
- void *data; /* The data */
-};
+/* 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;
-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);
@@ -92,82 +89,75 @@ _PR_InitThreads (PRThreadType type, PRThreadPriority priority,
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->state |= BT_THREAD_PRIMORD;
primordialThread->priority = priority;
- /*
- ** Initialize the thread tracking data structures
- */
- bt_privateRoot.lock = PR_NewLock();
- if( NULL == bt_privateRoot.lock )
- {
- PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
- return;
- }
+ /* 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;
/*
- ** Grab a key. We're guaranteed to be key #0, since we are
- ** always the first one in.
+ ** 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)
*/
-
- 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.
*/
-
- 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.
- */
+ 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;
}
+}
- tempLock = PR_NewLock();
- if( NULL == tempLock )
+PRUint32
+_bt_MapNSPRToNativePriority( PRThreadPriority priority )
{
- PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
- return;
+ 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 );
}
+}
- bt_book.cv = PR_NewCondVar( tempLock );
- if( NULL == bt_book.cv )
+PRThreadPriority
+_bt_MapNativeToNSPRPriority(PRUint32 priority)
{
- PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
- return;
- }
+ 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 )
{
@@ -179,97 +169,97 @@ _bt_mapPriority( PRThreadPriority priority )
}
}
-/**
- * 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)
+/* This method is called by all NSPR threads as they exit */
+void _bt_CleanupThread(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. */
+ PRThread *me = PR_GetCurrentThread();
+ int32 i;
- result = PR_SetThreadPrivate( 0, (void *) thred );
- PR_ASSERT( result == PR_SUCCESS );
+ /* 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);
+ }
- thred->startFunc(thred->arg); /* run the dang thing */
+ /* 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);
- /*
- ** Call the destructor, if available.
- */
+ 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_Lock( bt_privateRoot.lock );
+ PR_Unlock(joinSemLock);
- for( i = 0; i < 128; i++ )
+ }
+ else
{
- hashTable = bt_privateRoot.keys[i];
+ /* 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");
- if( hashTable != NULL )
- {
- if( hashTable->destructor != NULL )
- {
- privData = _bt_getThreadPrivate( i );
+ /* we're done accessing our joinSem */
+ PR_Unlock(joinSemLock);
- if( privData != NULL )
- {
- PR_Unlock( bt_privateRoot.lock );
- hashTable->destructor( privData );
- PR_Lock( bt_privateRoot.lock );
- }
+ /* wait for someone to join us */
+ while (acquire_sem(me->md.joinSem) == B_INTERRUPTED);
}
}
- }
-
- PR_Unlock( bt_privateRoot.lock );
-
- /* decrement our thread counters */
+ /* 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 );
- if (thred->state & BT_THREAD_SYSTEM) {
-#if 0
- bt_book.system -= 1;
-#endif
- } else
- {
+ /* decrement the number of currently-alive user threads */
bt_book.threadCount--;
- if( 0 == bt_book.threadCount )
- {
- PR_NotifyAllCondVar(bt_book.cv);
+ 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 );
+ }
- if( thred->md.is_joinable == 1 )
- {
- /*
- ** This is a joinable thread. Keep suspending
- ** until is_joining is set to 1
- */
+ /* finally, delete this thread's PRThread */
+ PR_DELETE(me);
+}
- if( thred->md.is_joining == 0 )
+/**
+ * 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)
{
- suspend_thread( thred->md.tid );
- }
- }
+ 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 */
- /* delete the thread object */
- PR_DELETE(thred);
+ /* clean up */
+ _bt_CleanupThread(NULL);
- result = PR_SetThreadPrivate( (PRUint8) 0, (void *) NULL );
- PR_ASSERT( result == PR_SUCCESS );
- exit_thread( NULL );
+ return 0;
}
PR_IMPLEMENT(PRThread*)
@@ -279,62 +269,70 @@ PR_IMPLEMENT(PRThread*)
{
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)
@@ -344,36 +342,71 @@ PR_IMPLEMENT(PRStatus)
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");
- PR_SetError(PR_UNKNOWN_ERROR, status);
- return PR_FAILURE;
+ /* 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 ()
{
- 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)
@@ -395,7 +428,8 @@ 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)
@@ -413,7 +447,7 @@ PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred,
PR_ASSERT( thred != NULL );
thred->priority = newPri;
- bePriority = _bt_mapPriority( newPri );
+ bePriority = _bt_MapNSPRToNativePriority( newPri );
set_thread_priority( thred->md.tid, bePriority );
}
@@ -421,56 +455,28 @@ 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.
- */
+ /* allocate a beos-native TLS slot for this index (the new slot
+ automatically contains NULL) */
+ tpd_beosTLSSlots[index] = tls_allocate();
- tempPointer = PR_NEWZAP( struct _BT_PrivateHash );
+ /* remember the destructor */
+ tpd_dtors[index] = destructor;
- if( 0 == tempPointer ) {
-
- PR_Unlock( bt_privateRoot.lock );
- return( PR_FAILURE );
- }
-
- bt_privateRoot.keys[index] = tempPointer;
- tempPointer->destructor = destructor;
-
- PR_Unlock( bt_privateRoot.lock );
-
- *newIndex = index;
+ *newIndex = (PRUintn)index;
return( PR_SUCCESS );
}
@@ -478,195 +484,44 @@ PR_IMPLEMENT(PRStatus)
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 )
+ if(index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
{
- PR_Unlock( bt_privateRoot.lock );
+ PR_SetError( PR_TPD_RANGE_ERROR, 0 );
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( 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 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);
- /*
- ** If the data was not NULL, and there was a destructor,
- ** it has already been called. Overwrite the existing
- ** data and return with success.
- */
+ /* save new value */
+ tls_set(tpd_beosTLSSlots[index], priv);
- 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 );
- 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)
+ PR_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.
- */
-
- 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 )
+ /* make sure the index is valid */
+ if (index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
{
- return( NULL );
+ PR_SetError( PR_TPD_RANGE_ERROR, 0 );
+ return NULL;
}
- /*
- ** Search through the linked list the end is reached or an existing
- ** entry is found.
- */
-
- currentEntry = hashTable->next[ hashBucket ];
-
- 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 the value */
+ return tls_get( tpd_beosTLSSlots[index] );
}
- 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;
-
- PR_Lock( bt_privateRoot.lock );
- returnValue = _bt_getThreadPrivate( index );
- PR_Unlock( bt_privateRoot.lock );
-
- return( returnValue );
-}
-
PR_IMPLEMENT(PRStatus)
PR_Interrupt (PRThread* thred)
@@ -748,25 +603,19 @@ PR_IMPLEMENT(PRStatus)
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;
}
@@ -776,3 +625,46 @@ PR_IMPLEMENT(void)
{
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;
+}
diff --git a/pr/tests/attach.c b/pr/tests/attach.c
index 4b890063..980fd6d4 100644
--- a/pr/tests/attach.c
+++ b/pr/tests/attach.c
@@ -36,15 +36,6 @@
** 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
***********************************************************************/
@@ -74,6 +65,8 @@ int main()
#define INCL_ERRORS
#include <os2.h>
#include <process.h>
+#elif defined(XP_BEOS)
+#include <kernel/OS.h>
#endif
#define DEFAULT_COUNT 1000
@@ -125,6 +118,8 @@ static void Measure(void (*func)(void), const char *msg)
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
@@ -165,6 +160,10 @@ int main(int argc, char **argv)
#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
@@ -328,6 +327,28 @@ int main(int argc, char **argv)
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;
@@ -344,5 +365,3 @@ exit_now:
else
return 0;
}
-
-#endif /* XP_BEOS */