Upload Tizen:Base source
[toolchains/nspr.git] / mozilla / nsprpub / pr / src / bthreads / btthread.c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is the Netscape Portable Runtime (NSPR).
16  *
17  * The Initial Developer of the Original Code is
18  * Netscape Communications Corporation.
19  * Portions created by the Initial Developer are Copyright (C) 1998-2000
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *
24  * Alternatively, the contents of this file may be used under the terms of
25  * either the GNU General Public License Version 2 or later (the "GPL"), or
26  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27  * in which case the provisions of the GPL or the LGPL are applicable instead
28  * of those above. If you wish to allow use of your version of this file only
29  * under the terms of either the GPL or the LGPL, and not to allow others to
30  * use your version of this file under the terms of the MPL, indicate your
31  * decision by deleting the provisions above and replace them with the notice
32  * and other provisions required by the GPL or the LGPL. If you do not delete
33  * the provisions above, a recipient may use your version of this file under
34  * the terms of any one of the MPL, the GPL or the LGPL.
35  *
36  * ***** END LICENSE BLOCK ***** */
37
38 #include <kernel/OS.h>
39 #include <support/TLS.h>
40
41 #include "prlog.h"
42 #include "primpl.h"
43 #include "prcvar.h"
44 #include "prpdce.h"
45
46 #include <stdlib.h>
47 #include <string.h>
48 #include <signal.h>
49
50 /* values for PRThread.state */
51 #define BT_THREAD_PRIMORD   0x01    /* this is the primordial thread */
52 #define BT_THREAD_SYSTEM    0x02    /* this is a system thread */
53 #define BT_THREAD_JOINABLE  0x04        /* this is a joinable thread */
54
55 struct _BT_Bookeeping
56 {
57     PRLock *ml;                 /* a lock to protect ourselves */
58         sem_id cleanUpSem;              /* the primoridal thread will block on this
59                                                            sem while waiting for the user threads */
60     PRInt32 threadCount;        /* user thred count */
61
62 } bt_book = { NULL, B_ERROR, 0 };
63
64
65 #define BT_TPD_LIMIT 128        /* number of TPD slots we'll provide (arbitrary) */
66
67 /* these will be used to map an index returned by PR_NewThreadPrivateIndex()
68    to the corresponding beos native TLS slot number, and to the destructor
69    for that slot - note that, because it is allocated globally, this data
70    will be automatically zeroed for us when the program begins */
71 static int32 tpd_beosTLSSlots[BT_TPD_LIMIT];
72 static PRThreadPrivateDTOR tpd_dtors[BT_TPD_LIMIT];
73
74 static vint32 tpd_slotsUsed=0;  /* number of currently-allocated TPD slots */
75 static int32 tls_prThreadSlot;  /* TLS slot in which PRThread will be stored */
76
77 /* this mutex will be used to synchronize access to every
78    PRThread.md.joinSem and PRThread.md.is_joining (we could
79    actually allocate one per thread, but that seems a bit excessive,
80    especially considering that there will probably be little
81    contention, PR_JoinThread() is allowed to block anyway, and the code
82    protected by the mutex is short/fast) */
83 static PRLock *joinSemLock;
84
85 static PRUint32 _bt_MapNSPRToNativePriority( PRThreadPriority priority );
86 static PRThreadPriority _bt_MapNativeToNSPRPriority( PRUint32 priority );
87 static void _bt_CleanupThread(void *arg);
88 static PRThread *_bt_AttachThread();
89
90 void
91 _PR_InitThreads (PRThreadType type, PRThreadPriority priority,
92                  PRUintn maxPTDs)
93 {
94     PRThread *primordialThread;
95     PRUint32  beThreadPriority;
96
97         /* allocate joinSem mutex */
98         joinSemLock = PR_NewLock();
99         if (joinSemLock == NULL)
100         {
101                 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
102                 return;
103     }
104
105     /*
106     ** Create and initialize NSPR structure for our primordial thread.
107     */
108     
109     primordialThread = PR_NEWZAP(PRThread);
110     if( NULL == primordialThread )
111     {
112         PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
113         return;
114     }
115
116         primordialThread->md.joinSem = B_ERROR;
117
118     /*
119     ** Set the priority to the desired level.
120     */
121
122     beThreadPriority = _bt_MapNSPRToNativePriority( priority );
123     
124     set_thread_priority( find_thread( NULL ), beThreadPriority );
125     
126     primordialThread->priority = priority;
127
128
129         /* set the thread's state - note that the thread is not joinable */
130     primordialThread->state |= BT_THREAD_PRIMORD;
131         if (type == PR_SYSTEM_THREAD)
132                 primordialThread->state |= BT_THREAD_SYSTEM;
133
134     /*
135     ** Allocate a TLS slot for the PRThread structure (just using
136     ** native TLS, as opposed to NSPR TPD, will make PR_GetCurrentThread()
137     ** somewhat faster, and will leave one more TPD slot for our client)
138     */
139         
140         tls_prThreadSlot = tls_allocate();
141
142     /*
143     ** Stuff our new PRThread structure into our thread specific
144     ** slot.
145     */
146
147         tls_set(tls_prThreadSlot, primordialThread);
148     
149         /* allocate lock for bt_book */
150     bt_book.ml = PR_NewLock();
151     if( NULL == bt_book.ml )
152     {
153         PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
154         return;
155     }
156 }
157
158 PRUint32
159 _bt_MapNSPRToNativePriority( PRThreadPriority priority )
160     {
161     switch( priority )
162     {
163         case PR_PRIORITY_LOW:    return( B_LOW_PRIORITY );
164         case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY );
165         case PR_PRIORITY_HIGH:   return( B_DISPLAY_PRIORITY );
166         case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY );
167         default:                 return( B_NORMAL_PRIORITY );
168     }
169 }
170
171 PRThreadPriority
172 _bt_MapNativeToNSPRPriority(PRUint32 priority)
173     {
174         if (priority < B_NORMAL_PRIORITY)
175                 return PR_PRIORITY_LOW;
176         if (priority < B_DISPLAY_PRIORITY)
177                 return PR_PRIORITY_NORMAL;
178         if (priority < B_URGENT_DISPLAY_PRIORITY)
179                 return PR_PRIORITY_HIGH;
180         return PR_PRIORITY_URGENT;
181 }
182
183 PRUint32
184 _bt_mapNativeToNSPRPriority( int32 priority )
185 {
186     switch( priority )
187     {
188         case PR_PRIORITY_LOW:    return( B_LOW_PRIORITY );
189         case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY );
190         case PR_PRIORITY_HIGH:   return( B_DISPLAY_PRIORITY );
191         case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY );
192         default:                 return( B_NORMAL_PRIORITY );
193     }
194 }
195
196 /* This method is called by all NSPR threads as they exit */
197 void _bt_CleanupThread(void *arg)
198 {
199         PRThread *me = PR_GetCurrentThread();
200         int32 i;
201
202         /* first, clean up all thread-private data */
203         for (i = 0; i < tpd_slotsUsed; i++)
204         {
205                 void *oldValue = tls_get(tpd_beosTLSSlots[i]);
206                 if ( oldValue != NULL && tpd_dtors[i] != NULL )
207                         (*tpd_dtors[i])(oldValue);
208         }
209
210         /* if this thread is joinable, wait for someone to join it */
211         if (me->state & BT_THREAD_JOINABLE)
212         {
213                 /* protect access to our joinSem */
214                 PR_Lock(joinSemLock);
215
216                 if (me->md.is_joining)
217                 {
218                         /* someone is already waiting to join us (they've
219                            allocated a joinSem for us) - let them know we're
220                            ready */
221                         delete_sem(me->md.joinSem);
222
223                         PR_Unlock(joinSemLock);
224
225                 }
226                 else
227     {
228                         /* noone is currently waiting for our demise - it
229                            is our responsibility to allocate the joinSem
230                            and block on it */
231                         me->md.joinSem = create_sem(0, "join sem");
232
233                         /* we're done accessing our joinSem */
234                         PR_Unlock(joinSemLock);
235
236                         /* wait for someone to join us */
237                         while (acquire_sem(me->md.joinSem) == B_INTERRUPTED);
238             }
239         }
240
241         /* if this is a user thread, we must update our books */
242         if ((me->state & BT_THREAD_SYSTEM) == 0)
243         {
244                 /* synchronize access to bt_book */
245     PR_Lock( bt_book.ml );
246
247                 /* decrement the number of currently-alive user threads */
248         bt_book.threadCount--;
249
250                 if (bt_book.threadCount == 0 && bt_book.cleanUpSem != B_ERROR) {
251                         /* we are the last user thread, and the primordial thread is
252                            blocked in PR_Cleanup() waiting for us to finish - notify
253                            it */
254                         delete_sem(bt_book.cleanUpSem);
255         }
256
257     PR_Unlock( bt_book.ml );
258         }
259
260         /* finally, delete this thread's PRThread */
261         PR_DELETE(me);
262 }
263
264 /**
265  * This is a wrapper that all threads invoke that allows us to set some
266  * things up prior to a thread's invocation and clean up after a thread has
267  * exited.
268  */
269 static void*
270 _bt_root (void* arg)
271         {
272     PRThread *thred = (PRThread*)arg;
273     PRIntn rv;
274     void *privData;
275     status_t result;
276     int i;
277
278         /* save our PRThread object into our TLS */
279         tls_set(tls_prThreadSlot, thred);
280
281     thred->startFunc(thred->arg);  /* run the dang thing */
282
283         /* clean up */
284         _bt_CleanupThread(NULL);
285
286         return 0;
287 }
288
289 PR_IMPLEMENT(PRThread*)
290     PR_CreateThread (PRThreadType type, void (*start)(void* arg), void* arg,
291                      PRThreadPriority priority, PRThreadScope scope,
292                      PRThreadState state, PRUint32 stackSize)
293 {
294     PRUint32 bePriority;
295
296     PRThread* thred;
297
298     if (!_pr_initialized) _PR_ImplicitInitialization();
299
300         thred = PR_NEWZAP(PRThread);
301         if (thred == NULL)
302         {
303         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
304         return NULL;
305     }
306
307     thred->md.joinSem = B_ERROR;
308
309         thred->arg = arg;
310         thred->startFunc = start;
311         thred->priority = priority;
312
313         if( state == PR_JOINABLE_THREAD )
314         {
315             thred->state |= BT_THREAD_JOINABLE;
316         }
317
318         /* keep some books */
319
320         PR_Lock( bt_book.ml );
321
322         if (type == PR_USER_THREAD)
323         {
324             bt_book.threadCount++;
325         }
326
327         PR_Unlock( bt_book.ml );
328
329         bePriority = _bt_MapNSPRToNativePriority( priority );
330
331         thred->md.tid = spawn_thread((thread_func)_bt_root, "moz-thread",
332                                      bePriority, thred);
333         if (thred->md.tid < B_OK) {
334             PR_SetError(PR_UNKNOWN_ERROR, thred->md.tid);
335             PR_DELETE(thred);
336                         return NULL;
337         }
338
339         if (resume_thread(thred->md.tid) < B_OK) {
340             PR_SetError(PR_UNKNOWN_ERROR, 0);
341             PR_DELETE(thred);
342                         return NULL;
343         }
344
345     return thred;
346     }
347
348 PR_IMPLEMENT(PRThread*)
349         PR_AttachThread(PRThreadType type, PRThreadPriority priority,
350                                         PRThreadStack *stack)
351 {
352         /* PR_GetCurrentThread() will attach a thread if necessary */
353         return PR_GetCurrentThread();
354 }
355
356 PR_IMPLEMENT(void)
357         PR_DetachThread()
358 {
359         /* we don't support detaching */
360 }
361
362 PR_IMPLEMENT(PRStatus)
363     PR_JoinThread (PRThread* thred)
364 {
365     status_t eval, status;
366
367     PR_ASSERT(thred != NULL);
368
369         if ((thred->state & BT_THREAD_JOINABLE) == 0)
370     {
371         PR_SetError( PR_INVALID_ARGUMENT_ERROR, 0 );
372         return( PR_FAILURE );
373     }
374
375         /* synchronize access to the thread's joinSem */
376         PR_Lock(joinSemLock);
377         
378         if (thred->md.is_joining)
379         {
380                 /* another thread is already waiting to join the specified
381                    thread - we must fail */
382                 PR_Unlock(joinSemLock);
383                 return PR_FAILURE;
384         }
385
386         /* let others know we are waiting to join */
387         thred->md.is_joining = PR_TRUE;
388
389         if (thred->md.joinSem == B_ERROR)
390         {
391                 /* the thread hasn't finished yet - it is our responsibility to
392                    allocate a joinSem and wait on it */
393                 thred->md.joinSem = create_sem(0, "join sem");
394
395                 /* we're done changing the joinSem now */
396                 PR_Unlock(joinSemLock);
397
398                 /* wait for the thread to finish */
399                 while (acquire_sem(thred->md.joinSem) == B_INTERRUPTED);
400
401         }
402         else
403         {
404                 /* the thread has already finished, and has allocated the
405                    joinSem itself - let it know it can finally die */
406                 delete_sem(thred->md.joinSem);
407                 
408                 PR_Unlock(joinSemLock);
409     }
410
411         /* make sure the thread is dead */
412     wait_for_thread(thred->md.tid, &eval);
413
414     return PR_SUCCESS;
415 }
416
417 PR_IMPLEMENT(PRThread*)
418     PR_GetCurrentThread ()
419 {
420     PRThread* thred;
421
422     if (!_pr_initialized) _PR_ImplicitInitialization();
423
424     thred = (PRThread *)tls_get( tls_prThreadSlot);
425         if (thred == NULL)
426         {
427                 /* this thread doesn't have a PRThread structure (it must be
428                    a native thread not created by the NSPR) - assimilate it */
429                 thred = _bt_AttachThread();
430         }
431     PR_ASSERT(NULL != thred);
432
433     return thred;
434 }
435
436 PR_IMPLEMENT(PRThreadScope)
437     PR_GetThreadScope (const PRThread* thred)
438 {
439     PR_ASSERT(thred != NULL);
440     return PR_GLOBAL_THREAD;
441 }
442
443 PR_IMPLEMENT(PRThreadType)
444     PR_GetThreadType (const PRThread* thred)
445 {
446     PR_ASSERT(thred != NULL);
447     return (thred->state & BT_THREAD_SYSTEM) ?
448         PR_SYSTEM_THREAD : PR_USER_THREAD;
449 }
450
451 PR_IMPLEMENT(PRThreadState)
452     PR_GetThreadState (const PRThread* thred)
453 {
454     PR_ASSERT(thred != NULL);
455     return (thred->state & BT_THREAD_JOINABLE)?
456                                         PR_JOINABLE_THREAD: PR_UNJOINABLE_THREAD;
457 }
458
459 PR_IMPLEMENT(PRThreadPriority)
460     PR_GetThreadPriority (const PRThread* thred)
461 {
462     PR_ASSERT(thred != NULL);
463     return thred->priority;
464 }  /* PR_GetThreadPriority */
465
466 PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred,
467                                         PRThreadPriority newPri)
468 {
469     PRUint32 bePriority;
470
471     PR_ASSERT( thred != NULL );
472
473     thred->priority = newPri;
474     bePriority = _bt_MapNSPRToNativePriority( newPri );
475     set_thread_priority( thred->md.tid, bePriority );
476 }
477
478 PR_IMPLEMENT(PRStatus)
479     PR_NewThreadPrivateIndex (PRUintn* newIndex,
480                               PRThreadPrivateDTOR destructor)
481 {
482         int32    index;
483
484     if (!_pr_initialized) _PR_ImplicitInitialization();
485
486         /* reserve the next available tpd slot */
487         index = atomic_add( &tpd_slotsUsed, 1 );
488         if (index >= BT_TPD_LIMIT)
489         {
490                 /* no slots left - decrement value, then fail */
491                 atomic_add( &tpd_slotsUsed, -1 );
492                 PR_SetError( PR_TPD_RANGE_ERROR, 0 );
493             return( PR_FAILURE );
494         }
495
496         /* allocate a beos-native TLS slot for this index (the new slot
497            automatically contains NULL) */
498         tpd_beosTLSSlots[index] = tls_allocate();
499
500         /* remember the destructor */
501         tpd_dtors[index] = destructor;
502
503     *newIndex = (PRUintn)index;
504
505     return( PR_SUCCESS );
506 }
507
508 PR_IMPLEMENT(PRStatus)
509     PR_SetThreadPrivate (PRUintn index, void* priv)
510 {
511         void *oldValue;
512
513     /*
514     ** Sanity checking
515     */
516
517     if(index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
518     {
519                 PR_SetError( PR_TPD_RANGE_ERROR, 0 );
520         return( PR_FAILURE );
521     }
522
523         /* if the old value isn't NULL, and the dtor for this slot isn't
524            NULL, we must destroy the data */
525         oldValue = tls_get(tpd_beosTLSSlots[index]);
526         if (oldValue != NULL && tpd_dtors[index] != NULL)
527                 (*tpd_dtors[index])(oldValue);
528
529         /* save new value */
530         tls_set(tpd_beosTLSSlots[index], priv);
531
532             return( PR_SUCCESS );
533         }
534
535 PR_IMPLEMENT(void*)
536     PR_GetThreadPrivate (PRUintn index)
537 {
538         /* make sure the index is valid */
539         if (index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
540     {   
541                 PR_SetError( PR_TPD_RANGE_ERROR, 0 );
542                 return NULL;
543     }
544
545         /* return the value */
546         return tls_get( tpd_beosTLSSlots[index] );
547         }
548
549
550 PR_IMPLEMENT(PRStatus)
551     PR_Interrupt (PRThread* thred)
552 {
553     PRIntn rv;
554
555     PR_ASSERT(thred != NULL);
556
557     /*
558     ** there seems to be a bug in beos R5 in which calling
559     ** resume_thread() on a blocked thread returns B_OK instead
560     ** of B_BAD_THREAD_STATE (beos bug #20000422-19095).  as such,
561     ** to interrupt a thread, we will simply suspend then resume it
562     ** (no longer call resume_thread(), check for B_BAD_THREAD_STATE,
563     ** the suspend/resume to wake up a blocked thread).  this wakes
564     ** up blocked threads properly, and doesn't hurt unblocked threads
565     ** (they simply get stopped then re-started immediately)
566     */
567
568     rv = suspend_thread( thred->md.tid );
569     if( rv != B_NO_ERROR )
570     {
571         /* this doesn't appear to be a valid thread_id */
572         PR_SetError( PR_UNKNOWN_ERROR, rv );
573         return PR_FAILURE;
574     }
575
576     rv = resume_thread( thred->md.tid );
577     if( rv != B_NO_ERROR )
578     {
579         PR_SetError( PR_UNKNOWN_ERROR, rv );
580         return PR_FAILURE;
581     }
582
583     return PR_SUCCESS;
584 }
585
586 PR_IMPLEMENT(void)
587     PR_ClearInterrupt ()
588 {
589 }
590
591 PR_IMPLEMENT(PRStatus)
592     PR_Yield ()
593 {
594     /* we just sleep for long enough to cause a reschedule (100
595        microseconds) */
596     snooze(100);
597 }
598
599 #define BT_MILLION 1000000UL
600
601 PR_IMPLEMENT(PRStatus)
602     PR_Sleep (PRIntervalTime ticks)
603 {
604     bigtime_t tps;
605     status_t status;
606
607     if (!_pr_initialized) _PR_ImplicitInitialization();
608
609     tps = PR_IntervalToMicroseconds( ticks );
610
611     status = snooze(tps);
612     if (status == B_NO_ERROR) return PR_SUCCESS;
613
614     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, status);
615     return PR_FAILURE;
616 }
617
618 PR_IMPLEMENT(PRStatus)
619     PR_Cleanup ()
620 {
621     PRThread *me = PR_GetCurrentThread();
622
623     PR_ASSERT(me->state & BT_THREAD_PRIMORD);
624     if ((me->state & BT_THREAD_PRIMORD) == 0) {
625         return PR_FAILURE;
626     }
627
628     PR_Lock( bt_book.ml );
629
630         if (bt_book.threadCount != 0)
631     {
632                 /* we'll have to wait for some threads to finish - create a
633                    sem to block on */
634                 bt_book.cleanUpSem = create_sem(0, "cleanup sem");
635     }
636
637     PR_Unlock( bt_book.ml );
638
639         /* note that, if all the user threads were already dead, we
640            wouldn't have created a sem above, so this acquire_sem()
641            will fail immediately */
642         while (acquire_sem(bt_book.cleanUpSem) == B_INTERRUPTED);
643
644     return PR_SUCCESS;
645 }
646
647 PR_IMPLEMENT(void)
648     PR_ProcessExit (PRIntn status)
649 {
650     exit(status);
651 }
652
653 PRThread *_bt_AttachThread()
654 {
655         PRThread *thread;
656         thread_info tInfo;
657
658         /* make sure this thread doesn't already have a PRThread structure */
659         PR_ASSERT(tls_get(tls_prThreadSlot) == NULL);
660
661         /* allocate a PRThread structure for this thread */
662         thread = PR_NEWZAP(PRThread);
663         if (thread == NULL)
664         {
665                 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
666                 return NULL;
667         }
668
669         /* get the native thread's current state */
670         get_thread_info(find_thread(NULL), &tInfo);
671
672         /* initialize new PRThread */
673         thread->md.tid = tInfo.thread;
674         thread->md.joinSem = B_ERROR;
675         thread->priority = _bt_MapNativeToNSPRPriority(tInfo.priority);
676
677         /* attached threads are always non-joinable user threads */
678         thread->state = 0;
679
680         /* increment user thread count */
681         PR_Lock(bt_book.ml);
682         bt_book.threadCount++;
683         PR_Unlock(bt_book.ml);
684
685         /* store this thread's PRThread */
686         tls_set(tls_prThreadSlot, thread);
687         
688         /* the thread must call _bt_CleanupThread() before it dies, in order
689            to clean up its PRThread, synchronize with the primordial thread,
690            etc. */
691         on_exit_thread(_bt_CleanupThread, NULL);
692         
693         return thread;
694 }