1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3
* The contents of this file are subject to the Mozilla Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/MPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is the Netscape Portable Runtime (NSPR).
15
* The Initial Developer of the Original Code is Netscape
16
* Communications Corporation. Portions created by Netscape are
17
* Copyright (C) 1998-2000 Netscape Communications Corporation. All
22
* Alternatively, the contents of this file may be used under the
23
* terms of the GNU General Public License Version 2 or later (the
24
* "GPL"), in which case the provisions of the GPL are applicable
25
* instead of those above. If you wish to allow use of your
26
* version of this file only under the terms of the GPL and not to
27
* allow others to use your version of this file under the MPL,
28
* indicate your decision by deleting the provisions above and
29
* replace them with the notice and other provisions required by
30
* the GPL. If you do not delete the provisions above, a recipient
31
* may use your version of this file under either the MPL or the
35
#include <kernel/OS.h>
36
#include <support/TLS.h>
47
/* values for PRThread.state */
48
#define BT_THREAD_PRIMORD 0x01 /* this is the primordial thread */
49
#define BT_THREAD_SYSTEM 0x02 /* this is a system thread */
50
#define BT_THREAD_JOINABLE 0x04 /* this is a joinable thread */
54
PRLock *ml; /* a lock to protect ourselves */
55
sem_id cleanUpSem; /* the primoridal thread will block on this
56
sem while waiting for the user threads */
57
PRInt32 threadCount; /* user thred count */
59
} bt_book = { NULL, B_ERROR, 0 };
62
#define BT_TPD_LIMIT 128 /* number of TPD slots we'll provide (arbitrary) */
64
/* these will be used to map an index returned by PR_NewThreadPrivateIndex()
65
to the corresponding beos native TLS slot number, and to the destructor
66
for that slot - note that, because it is allocated globally, this data
67
will be automatically zeroed for us when the program begins */
68
static int32 tpd_beosTLSSlots[BT_TPD_LIMIT];
69
static PRThreadPrivateDTOR tpd_dtors[BT_TPD_LIMIT];
71
static vint32 tpd_slotsUsed=0; /* number of currently-allocated TPD slots */
72
static int32 tls_prThreadSlot; /* TLS slot in which PRThread will be stored */
74
/* this mutex will be used to synchronize access to every
75
PRThread.md.joinSem and PRThread.md.is_joining (we could
76
actually allocate one per thread, but that seems a bit excessive,
77
especially considering that there will probably be little
78
contention, PR_JoinThread() is allowed to block anyway, and the code
79
protected by the mutex is short/fast) */
80
static PRLock *joinSemLock;
82
static PRUint32 _bt_MapNSPRToNativePriority( PRThreadPriority priority );
83
static PRThreadPriority _bt_MapNativeToNSPRPriority( PRUint32 priority );
84
static void _bt_CleanupThread(void *arg);
85
static PRThread *_bt_AttachThread();
88
_PR_InitThreads (PRThreadType type, PRThreadPriority priority,
91
PRThread *primordialThread;
92
PRUint32 beThreadPriority;
94
/* allocate joinSem mutex */
95
joinSemLock = PR_NewLock();
96
if (joinSemLock == NULL)
98
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
103
** Create and initialize NSPR structure for our primordial thread.
106
primordialThread = PR_NEWZAP(PRThread);
107
if( NULL == primordialThread )
109
PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
113
primordialThread->md.joinSem = B_ERROR;
116
** Set the priority to the desired level.
119
beThreadPriority = _bt_MapNSPRToNativePriority( priority );
121
set_thread_priority( find_thread( NULL ), beThreadPriority );
123
primordialThread->priority = priority;
126
/* set the thread's state - note that the thread is not joinable */
127
primordialThread->state |= BT_THREAD_PRIMORD;
128
if (type == PR_SYSTEM_THREAD)
129
primordialThread->state |= BT_THREAD_SYSTEM;
132
** Allocate a TLS slot for the PRThread structure (just using
133
** native TLS, as opposed to NSPR TPD, will make PR_GetCurrentThread()
134
** somewhat faster, and will leave one more TPD slot for our client)
137
tls_prThreadSlot = tls_allocate();
140
** Stuff our new PRThread structure into our thread specific
144
tls_set(tls_prThreadSlot, primordialThread);
146
/* allocate lock for bt_book */
147
bt_book.ml = PR_NewLock();
148
if( NULL == bt_book.ml )
150
PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
156
_bt_MapNSPRToNativePriority( PRThreadPriority priority )
160
case PR_PRIORITY_LOW: return( B_LOW_PRIORITY );
161
case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY );
162
case PR_PRIORITY_HIGH: return( B_DISPLAY_PRIORITY );
163
case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY );
164
default: return( B_NORMAL_PRIORITY );
169
_bt_MapNativeToNSPRPriority(PRUint32 priority)
171
if (priority < B_NORMAL_PRIORITY)
172
return PR_PRIORITY_LOW;
173
if (priority < B_DISPLAY_PRIORITY)
174
return PR_PRIORITY_NORMAL;
175
if (priority < B_URGENT_DISPLAY_PRIORITY)
176
return PR_PRIORITY_HIGH;
177
return PR_PRIORITY_URGENT;
181
_bt_mapNativeToNSPRPriority( int32 priority )
185
case PR_PRIORITY_LOW: return( B_LOW_PRIORITY );
186
case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY );
187
case PR_PRIORITY_HIGH: return( B_DISPLAY_PRIORITY );
188
case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY );
189
default: return( B_NORMAL_PRIORITY );
193
/* This method is called by all NSPR threads as they exit */
194
void _bt_CleanupThread(void *arg)
196
PRThread *me = PR_GetCurrentThread();
199
/* first, clean up all thread-private data */
200
for (i = 0; i < tpd_slotsUsed; i++)
202
void *oldValue = tls_get(tpd_beosTLSSlots[i]);
203
if ( oldValue != NULL && tpd_dtors[i] != NULL )
204
(*tpd_dtors[i])(oldValue);
207
/* if this thread is joinable, wait for someone to join it */
208
if (me->state & BT_THREAD_JOINABLE)
210
/* protect access to our joinSem */
211
PR_Lock(joinSemLock);
213
if (me->md.is_joining)
215
/* someone is already waiting to join us (they've
216
allocated a joinSem for us) - let them know we're
218
delete_sem(me->md.joinSem);
220
PR_Unlock(joinSemLock);
225
/* noone is currently waiting for our demise - it
226
is our responsibility to allocate the joinSem
228
me->md.joinSem = create_sem(0, "join sem");
230
/* we're done accessing our joinSem */
231
PR_Unlock(joinSemLock);
233
/* wait for someone to join us */
234
while (acquire_sem(me->md.joinSem) == B_INTERRUPTED);
238
/* if this is a user thread, we must update our books */
239
if ((me->state & BT_THREAD_SYSTEM) == 0)
241
/* synchronize access to bt_book */
242
PR_Lock( bt_book.ml );
244
/* decrement the number of currently-alive user threads */
245
bt_book.threadCount--;
247
if (bt_book.threadCount == 0 && bt_book.cleanUpSem != B_ERROR) {
248
/* we are the last user thread, and the primordial thread is
249
blocked in PR_Cleanup() waiting for us to finish - notify
251
delete_sem(bt_book.cleanUpSem);
254
PR_Unlock( bt_book.ml );
257
/* finally, delete this thread's PRThread */
262
* This is a wrapper that all threads invoke that allows us to set some
263
* things up prior to a thread's invocation and clean up after a thread has
269
PRThread *thred = (PRThread*)arg;
275
/* save our PRThread object into our TLS */
276
tls_set(tls_prThreadSlot, thred);
278
thred->startFunc(thred->arg); /* run the dang thing */
281
_bt_CleanupThread(NULL);
286
PR_IMPLEMENT(PRThread*)
287
PR_CreateThread (PRThreadType type, void (*start)(void* arg), void* arg,
288
PRThreadPriority priority, PRThreadScope scope,
289
PRThreadState state, PRUint32 stackSize)
295
if (!_pr_initialized) _PR_ImplicitInitialization();
297
thred = PR_NEWZAP(PRThread);
300
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
304
thred->md.joinSem = B_ERROR;
307
thred->startFunc = start;
308
thred->priority = priority;
310
if( state == PR_JOINABLE_THREAD )
312
thred->state |= BT_THREAD_JOINABLE;
315
/* keep some books */
317
PR_Lock( bt_book.ml );
319
if (type == PR_USER_THREAD)
321
bt_book.threadCount++;
324
PR_Unlock( bt_book.ml );
326
bePriority = _bt_MapNSPRToNativePriority( priority );
328
thred->md.tid = spawn_thread((thread_func)_bt_root, "moz-thread",
330
if (thred->md.tid < B_OK) {
331
PR_SetError(PR_UNKNOWN_ERROR, thred->md.tid);
336
if (resume_thread(thred->md.tid) < B_OK) {
337
PR_SetError(PR_UNKNOWN_ERROR, 0);
345
PR_IMPLEMENT(PRThread*)
346
PR_AttachThread(PRThreadType type, PRThreadPriority priority,
347
PRThreadStack *stack)
349
/* PR_GetCurrentThread() will attach a thread if necessary */
350
return PR_GetCurrentThread();
356
/* we don't support detaching */
359
PR_IMPLEMENT(PRStatus)
360
PR_JoinThread (PRThread* thred)
362
status_t eval, status;
364
PR_ASSERT(thred != NULL);
366
if ((thred->state & BT_THREAD_JOINABLE) == 0)
368
PR_SetError( PR_INVALID_ARGUMENT_ERROR, 0 );
369
return( PR_FAILURE );
372
/* synchronize access to the thread's joinSem */
373
PR_Lock(joinSemLock);
375
if (thred->md.is_joining)
377
/* another thread is already waiting to join the specified
378
thread - we must fail */
379
PR_Unlock(joinSemLock);
383
/* let others know we are waiting to join */
384
thred->md.is_joining = PR_TRUE;
386
if (thred->md.joinSem == B_ERROR)
388
/* the thread hasn't finished yet - it is our responsibility to
389
allocate a joinSem and wait on it */
390
thred->md.joinSem = create_sem(0, "join sem");
392
/* we're done changing the joinSem now */
393
PR_Unlock(joinSemLock);
395
/* wait for the thread to finish */
396
while (acquire_sem(thred->md.joinSem) == B_INTERRUPTED);
401
/* the thread has already finished, and has allocated the
402
joinSem itself - let it know it can finally die */
403
delete_sem(thred->md.joinSem);
405
PR_Unlock(joinSemLock);
408
/* make sure the thread is dead */
409
wait_for_thread(thred->md.tid, &eval);
414
PR_IMPLEMENT(PRThread*)
415
PR_GetCurrentThread ()
419
if (!_pr_initialized) _PR_ImplicitInitialization();
421
thred = (PRThread *)tls_get( tls_prThreadSlot);
424
/* this thread doesn't have a PRThread structure (it must be
425
a native thread not created by the NSPR) - assimilate it */
426
thred = _bt_AttachThread();
428
PR_ASSERT(NULL != thred);
433
PR_IMPLEMENT(PRThreadScope)
434
PR_GetThreadScope (const PRThread* thred)
436
PR_ASSERT(thred != NULL);
437
return PR_GLOBAL_THREAD;
440
PR_IMPLEMENT(PRThreadType)
441
PR_GetThreadType (const PRThread* thred)
443
PR_ASSERT(thred != NULL);
444
return (thred->state & BT_THREAD_SYSTEM) ?
445
PR_SYSTEM_THREAD : PR_USER_THREAD;
448
PR_IMPLEMENT(PRThreadState)
449
PR_GetThreadState (const PRThread* thred)
451
PR_ASSERT(thred != NULL);
452
return (thred->state & BT_THREAD_JOINABLE)?
453
PR_JOINABLE_THREAD: PR_UNJOINABLE_THREAD;
456
PR_IMPLEMENT(PRThreadPriority)
457
PR_GetThreadPriority (const PRThread* thred)
459
PR_ASSERT(thred != NULL);
460
return thred->priority;
461
} /* PR_GetThreadPriority */
463
PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred,
464
PRThreadPriority newPri)
468
PR_ASSERT( thred != NULL );
470
thred->priority = newPri;
471
bePriority = _bt_MapNSPRToNativePriority( newPri );
472
set_thread_priority( thred->md.tid, bePriority );
475
PR_IMPLEMENT(PRStatus)
476
PR_NewThreadPrivateIndex (PRUintn* newIndex,
477
PRThreadPrivateDTOR destructor)
481
if (!_pr_initialized) _PR_ImplicitInitialization();
483
/* reserve the next available tpd slot */
484
index = atomic_add( &tpd_slotsUsed, 1 );
485
if (index >= BT_TPD_LIMIT)
487
/* no slots left - decrement value, then fail */
488
atomic_add( &tpd_slotsUsed, -1 );
489
PR_SetError( PR_TPD_RANGE_ERROR, 0 );
490
return( PR_FAILURE );
493
/* allocate a beos-native TLS slot for this index (the new slot
494
automatically contains NULL) */
495
tpd_beosTLSSlots[index] = tls_allocate();
497
/* remember the destructor */
498
tpd_dtors[index] = destructor;
500
*newIndex = (PRUintn)index;
502
return( PR_SUCCESS );
505
PR_IMPLEMENT(PRStatus)
506
PR_SetThreadPrivate (PRUintn index, void* priv)
514
if(index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
516
PR_SetError( PR_TPD_RANGE_ERROR, 0 );
517
return( PR_FAILURE );
520
/* if the old value isn't NULL, and the dtor for this slot isn't
521
NULL, we must destroy the data */
522
oldValue = tls_get(tpd_beosTLSSlots[index]);
523
if (oldValue != NULL && tpd_dtors[index] != NULL)
524
(*tpd_dtors[index])(oldValue);
527
tls_set(tpd_beosTLSSlots[index], priv);
529
return( PR_SUCCESS );
533
PR_GetThreadPrivate (PRUintn index)
535
/* make sure the index is valid */
536
if (index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
538
PR_SetError( PR_TPD_RANGE_ERROR, 0 );
542
/* return the value */
543
return tls_get( tpd_beosTLSSlots[index] );
547
PR_IMPLEMENT(PRStatus)
548
PR_Interrupt (PRThread* thred)
552
PR_ASSERT(thred != NULL);
555
** there seems to be a bug in beos R5 in which calling
556
** resume_thread() on a blocked thread returns B_OK instead
557
** of B_BAD_THREAD_STATE (beos bug #20000422-19095). as such,
558
** to interrupt a thread, we will simply suspend then resume it
559
** (no longer call resume_thread(), check for B_BAD_THREAD_STATE,
560
** the suspend/resume to wake up a blocked thread). this wakes
561
** up blocked threads properly, and doesn't hurt unblocked threads
562
** (they simply get stopped then re-started immediately)
565
rv = suspend_thread( thred->md.tid );
566
if( rv != B_NO_ERROR )
568
/* this doesn't appear to be a valid thread_id */
569
PR_SetError( PR_UNKNOWN_ERROR, rv );
573
rv = resume_thread( thred->md.tid );
574
if( rv != B_NO_ERROR )
576
PR_SetError( PR_UNKNOWN_ERROR, rv );
588
PR_IMPLEMENT(PRStatus)
591
/* we just sleep for long enough to cause a reschedule (100
596
#define BT_MILLION 1000000UL
598
PR_IMPLEMENT(PRStatus)
599
PR_Sleep (PRIntervalTime ticks)
604
if (!_pr_initialized) _PR_ImplicitInitialization();
606
tps = PR_IntervalToMicroseconds( ticks );
608
status = snooze(tps);
609
if (status == B_NO_ERROR) return PR_SUCCESS;
611
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, status);
615
PR_IMPLEMENT(PRStatus)
618
PRThread *me = PR_CurrentThread();
620
PR_ASSERT(me->state & BT_THREAD_PRIMORD);
621
if ((me->state & BT_THREAD_PRIMORD) == 0) {
625
PR_Lock( bt_book.ml );
627
if (bt_book.threadCount != 0)
629
/* we'll have to wait for some threads to finish - create a
631
bt_book.cleanUpSem = create_sem(0, "cleanup sem");
634
PR_Unlock( bt_book.ml );
636
/* note that, if all the user threads were already dead, we
637
wouldn't have created a sem above, so this acquire_sem()
638
will fail immediately */
639
while (acquire_sem(bt_book.cleanUpSem) == B_INTERRUPTED);
645
PR_ProcessExit (PRIntn status)
650
PRThread *_bt_AttachThread()
655
/* make sure this thread doesn't already have a PRThread structure */
656
PR_ASSERT(tls_get(tls_prThreadSlot) == NULL);
658
/* allocate a PRThread structure for this thread */
659
thread = PR_NEWZAP(PRThread);
662
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
666
/* get the native thread's current state */
667
get_thread_info(find_thread(NULL), &tInfo);
669
/* initialize new PRThread */
670
thread->md.tid = tInfo.thread;
671
thread->md.joinSem = B_ERROR;
672
thread->priority = _bt_MapNativeToNSPRPriority(tInfo.priority);
674
/* attached threads are always non-joinable user threads */
677
/* increment user thread count */
679
bt_book.threadCount++;
680
PR_Unlock(bt_book.ml);
682
/* store this thread's PRThread */
683
tls_set(tls_prThreadSlot, thread);
685
/* the thread must call _bt_CleanupThread() before it dies, in order
686
to clean up its PRThread, synchronize with the primordial thread,
688
on_exit_thread(_bt_CleanupThread, NULL);