nsprpub/pr/src/bthreads/btthread.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include <kernel/OS.h>
     7 #include <support/TLS.h>
     9 #include "prlog.h"
    10 #include "primpl.h"
    11 #include "prcvar.h"
    12 #include "prpdce.h"
    14 #include <stdlib.h>
    15 #include <string.h>
    16 #include <signal.h>
    18 /* values for PRThread.state */
    19 #define BT_THREAD_PRIMORD   0x01    /* this is the primordial thread */
    20 #define BT_THREAD_SYSTEM    0x02    /* this is a system thread */
    21 #define BT_THREAD_JOINABLE  0x04	/* this is a joinable thread */
    23 struct _BT_Bookeeping
    24 {
    25     PRLock *ml;                 /* a lock to protect ourselves */
    26 	sem_id cleanUpSem;		/* the primoridal thread will block on this
    27 							   sem while waiting for the user threads */
    28     PRInt32 threadCount;	/* user thred count */
    30 } bt_book = { NULL, B_ERROR, 0 };
    33 #define BT_TPD_LIMIT 128	/* number of TPD slots we'll provide (arbitrary) */
    35 /* these will be used to map an index returned by PR_NewThreadPrivateIndex()
    36    to the corresponding beos native TLS slot number, and to the destructor
    37    for that slot - note that, because it is allocated globally, this data
    38    will be automatically zeroed for us when the program begins */
    39 static int32 tpd_beosTLSSlots[BT_TPD_LIMIT];
    40 static PRThreadPrivateDTOR tpd_dtors[BT_TPD_LIMIT];
    42 static vint32 tpd_slotsUsed=0;	/* number of currently-allocated TPD slots */
    43 static int32 tls_prThreadSlot;	/* TLS slot in which PRThread will be stored */
    45 /* this mutex will be used to synchronize access to every
    46    PRThread.md.joinSem and PRThread.md.is_joining (we could
    47    actually allocate one per thread, but that seems a bit excessive,
    48    especially considering that there will probably be little
    49    contention, PR_JoinThread() is allowed to block anyway, and the code
    50    protected by the mutex is short/fast) */
    51 static PRLock *joinSemLock;
    53 static PRUint32 _bt_MapNSPRToNativePriority( PRThreadPriority priority );
    54 static PRThreadPriority _bt_MapNativeToNSPRPriority( PRUint32 priority );
    55 static void _bt_CleanupThread(void *arg);
    56 static PRThread *_bt_AttachThread();
    58 void
    59 _PR_InitThreads (PRThreadType type, PRThreadPriority priority,
    60                  PRUintn maxPTDs)
    61 {
    62     PRThread *primordialThread;
    63     PRUint32  beThreadPriority;
    65 	/* allocate joinSem mutex */
    66 	joinSemLock = PR_NewLock();
    67 	if (joinSemLock == NULL)
    68 	{
    69 		PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    70 		return;
    71     }
    73     /*
    74     ** Create and initialize NSPR structure for our primordial thread.
    75     */
    77     primordialThread = PR_NEWZAP(PRThread);
    78     if( NULL == primordialThread )
    79     {
    80         PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
    81         return;
    82     }
    84 	primordialThread->md.joinSem = B_ERROR;
    86     /*
    87     ** Set the priority to the desired level.
    88     */
    90     beThreadPriority = _bt_MapNSPRToNativePriority( priority );
    92     set_thread_priority( find_thread( NULL ), beThreadPriority );
    94     primordialThread->priority = priority;
    97 	/* set the thread's state - note that the thread is not joinable */
    98     primordialThread->state |= BT_THREAD_PRIMORD;
    99 	if (type == PR_SYSTEM_THREAD)
   100 		primordialThread->state |= BT_THREAD_SYSTEM;
   102     /*
   103     ** Allocate a TLS slot for the PRThread structure (just using
   104     ** native TLS, as opposed to NSPR TPD, will make PR_GetCurrentThread()
   105     ** somewhat faster, and will leave one more TPD slot for our client)
   106     */
   108 	tls_prThreadSlot = tls_allocate();
   110     /*
   111     ** Stuff our new PRThread structure into our thread specific
   112     ** slot.
   113     */
   115 	tls_set(tls_prThreadSlot, primordialThread);
   117 	/* allocate lock for bt_book */
   118     bt_book.ml = PR_NewLock();
   119     if( NULL == bt_book.ml )
   120     {
   121     	PR_SetError( PR_OUT_OF_MEMORY_ERROR, 0 );
   122 	return;
   123     }
   124 }
   126 PRUint32
   127 _bt_MapNSPRToNativePriority( PRThreadPriority priority )
   128     {
   129     switch( priority )
   130     {
   131     	case PR_PRIORITY_LOW:	 return( B_LOW_PRIORITY );
   132 	case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY );
   133 	case PR_PRIORITY_HIGH:	 return( B_DISPLAY_PRIORITY );
   134 	case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY );
   135 	default:		 return( B_NORMAL_PRIORITY );
   136     }
   137 }
   139 PRThreadPriority
   140 _bt_MapNativeToNSPRPriority(PRUint32 priority)
   141     {
   142 	if (priority < B_NORMAL_PRIORITY)
   143 		return PR_PRIORITY_LOW;
   144 	if (priority < B_DISPLAY_PRIORITY)
   145 		return PR_PRIORITY_NORMAL;
   146 	if (priority < B_URGENT_DISPLAY_PRIORITY)
   147 		return PR_PRIORITY_HIGH;
   148 	return PR_PRIORITY_URGENT;
   149 }
   151 PRUint32
   152 _bt_mapNativeToNSPRPriority( int32 priority )
   153 {
   154     switch( priority )
   155     {
   156     	case PR_PRIORITY_LOW:	 return( B_LOW_PRIORITY );
   157 	case PR_PRIORITY_NORMAL: return( B_NORMAL_PRIORITY );
   158 	case PR_PRIORITY_HIGH:	 return( B_DISPLAY_PRIORITY );
   159 	case PR_PRIORITY_URGENT: return( B_URGENT_DISPLAY_PRIORITY );
   160 	default:		 return( B_NORMAL_PRIORITY );
   161     }
   162 }
   164 /* This method is called by all NSPR threads as they exit */
   165 void _bt_CleanupThread(void *arg)
   166 {
   167 	PRThread *me = PR_GetCurrentThread();
   168 	int32 i;
   170 	/* first, clean up all thread-private data */
   171 	for (i = 0; i < tpd_slotsUsed; i++)
   172 	{
   173 		void *oldValue = tls_get(tpd_beosTLSSlots[i]);
   174 		if ( oldValue != NULL && tpd_dtors[i] != NULL )
   175 			(*tpd_dtors[i])(oldValue);
   176 	}
   178 	/* if this thread is joinable, wait for someone to join it */
   179 	if (me->state & BT_THREAD_JOINABLE)
   180 	{
   181 		/* protect access to our joinSem */
   182 		PR_Lock(joinSemLock);
   184 		if (me->md.is_joining)
   185 		{
   186 			/* someone is already waiting to join us (they've
   187 			   allocated a joinSem for us) - let them know we're
   188 			   ready */
   189 			delete_sem(me->md.joinSem);
   191 			PR_Unlock(joinSemLock);
   193 		}
   194 		else
   195     {
   196 			/* noone is currently waiting for our demise - it
   197 			   is our responsibility to allocate the joinSem
   198 			   and block on it */
   199 			me->md.joinSem = create_sem(0, "join sem");
   201 			/* we're done accessing our joinSem */
   202 			PR_Unlock(joinSemLock);
   204 			/* wait for someone to join us */
   205 			while (acquire_sem(me->md.joinSem) == B_INTERRUPTED);
   206 	    }
   207 	}
   209 	/* if this is a user thread, we must update our books */
   210 	if ((me->state & BT_THREAD_SYSTEM) == 0)
   211 	{
   212 		/* synchronize access to bt_book */
   213     PR_Lock( bt_book.ml );
   215 		/* decrement the number of currently-alive user threads */
   216 	bt_book.threadCount--;
   218 		if (bt_book.threadCount == 0 && bt_book.cleanUpSem != B_ERROR) {
   219 			/* we are the last user thread, and the primordial thread is
   220 			   blocked in PR_Cleanup() waiting for us to finish - notify
   221 			   it */
   222 			delete_sem(bt_book.cleanUpSem);
   223 	}
   225     PR_Unlock( bt_book.ml );
   226 	}
   228 	/* finally, delete this thread's PRThread */
   229 	PR_DELETE(me);
   230 }
   232 /**
   233  * This is a wrapper that all threads invoke that allows us to set some
   234  * things up prior to a thread's invocation and clean up after a thread has
   235  * exited.
   236  */
   237 static void*
   238 _bt_root (void* arg)
   239 	{
   240     PRThread *thred = (PRThread*)arg;
   241     PRIntn rv;
   242     void *privData;
   243     status_t result;
   244     int i;
   246 	/* save our PRThread object into our TLS */
   247 	tls_set(tls_prThreadSlot, thred);
   249     thred->startFunc(thred->arg);  /* run the dang thing */
   251 	/* clean up */
   252 	_bt_CleanupThread(NULL);
   254 	return 0;
   255 }
   257 PR_IMPLEMENT(PRThread*)
   258     PR_CreateThread (PRThreadType type, void (*start)(void* arg), void* arg,
   259                      PRThreadPriority priority, PRThreadScope scope,
   260                      PRThreadState state, PRUint32 stackSize)
   261 {
   262     PRUint32 bePriority;
   264     PRThread* thred;
   266     if (!_pr_initialized) _PR_ImplicitInitialization();
   268 	thred = PR_NEWZAP(PRThread);
   269  	if (thred == NULL)
   270 	{
   271         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
   272         return NULL;
   273     }
   275     thred->md.joinSem = B_ERROR;
   277         thred->arg = arg;
   278         thred->startFunc = start;
   279         thred->priority = priority;
   281 	if( state == PR_JOINABLE_THREAD )
   282 	{
   283 	    thred->state |= BT_THREAD_JOINABLE;
   284 	}
   286         /* keep some books */
   288 	PR_Lock( bt_book.ml );
   290 	if (type == PR_USER_THREAD)
   291 	{
   292 	    bt_book.threadCount++;
   293         }
   295 	PR_Unlock( bt_book.ml );
   297 	bePriority = _bt_MapNSPRToNativePriority( priority );
   299         thred->md.tid = spawn_thread((thread_func)_bt_root, "moz-thread",
   300                                      bePriority, thred);
   301         if (thred->md.tid < B_OK) {
   302             PR_SetError(PR_UNKNOWN_ERROR, thred->md.tid);
   303             PR_DELETE(thred);
   304 			return NULL;
   305         }
   307         if (resume_thread(thred->md.tid) < B_OK) {
   308             PR_SetError(PR_UNKNOWN_ERROR, 0);
   309             PR_DELETE(thred);
   310 			return NULL;
   311         }
   313     return thred;
   314     }
   316 PR_IMPLEMENT(PRThread*)
   317 	PR_AttachThread(PRThreadType type, PRThreadPriority priority,
   318 					PRThreadStack *stack)
   319 {
   320 	/* PR_GetCurrentThread() will attach a thread if necessary */
   321 	return PR_GetCurrentThread();
   322 }
   324 PR_IMPLEMENT(void)
   325 	PR_DetachThread()
   326 {
   327 	/* we don't support detaching */
   328 }
   330 PR_IMPLEMENT(PRStatus)
   331     PR_JoinThread (PRThread* thred)
   332 {
   333     status_t eval, status;
   335     PR_ASSERT(thred != NULL);
   337 	if ((thred->state & BT_THREAD_JOINABLE) == 0)
   338     {
   339 	PR_SetError( PR_INVALID_ARGUMENT_ERROR, 0 );
   340 	return( PR_FAILURE );
   341     }
   343 	/* synchronize access to the thread's joinSem */
   344 	PR_Lock(joinSemLock);
   346 	if (thred->md.is_joining)
   347 	{
   348 		/* another thread is already waiting to join the specified
   349 		   thread - we must fail */
   350 		PR_Unlock(joinSemLock);
   351 		return PR_FAILURE;
   352 	}
   354 	/* let others know we are waiting to join */
   355 	thred->md.is_joining = PR_TRUE;
   357 	if (thred->md.joinSem == B_ERROR)
   358 	{
   359 		/* the thread hasn't finished yet - it is our responsibility to
   360 		   allocate a joinSem and wait on it */
   361 		thred->md.joinSem = create_sem(0, "join sem");
   363 		/* we're done changing the joinSem now */
   364 		PR_Unlock(joinSemLock);
   366 		/* wait for the thread to finish */
   367 		while (acquire_sem(thred->md.joinSem) == B_INTERRUPTED);
   369 	}
   370 	else
   371 	{
   372 		/* the thread has already finished, and has allocated the
   373 		   joinSem itself - let it know it can finally die */
   374 		delete_sem(thred->md.joinSem);
   376 		PR_Unlock(joinSemLock);
   377     }
   379 	/* make sure the thread is dead */
   380     wait_for_thread(thred->md.tid, &eval);
   382     return PR_SUCCESS;
   383 }
   385 PR_IMPLEMENT(PRThread*)
   386     PR_GetCurrentThread ()
   387 {
   388     PRThread* thred;
   390     if (!_pr_initialized) _PR_ImplicitInitialization();
   392     thred = (PRThread *)tls_get( tls_prThreadSlot);
   393 	if (thred == NULL)
   394 	{
   395 		/* this thread doesn't have a PRThread structure (it must be
   396 		   a native thread not created by the NSPR) - assimilate it */
   397 		thred = _bt_AttachThread();
   398 	}
   399     PR_ASSERT(NULL != thred);
   401     return thred;
   402 }
   404 PR_IMPLEMENT(PRThreadScope)
   405     PR_GetThreadScope (const PRThread* thred)
   406 {
   407     PR_ASSERT(thred != NULL);
   408     return PR_GLOBAL_THREAD;
   409 }
   411 PR_IMPLEMENT(PRThreadType)
   412     PR_GetThreadType (const PRThread* thred)
   413 {
   414     PR_ASSERT(thred != NULL);
   415     return (thred->state & BT_THREAD_SYSTEM) ?
   416         PR_SYSTEM_THREAD : PR_USER_THREAD;
   417 }
   419 PR_IMPLEMENT(PRThreadState)
   420     PR_GetThreadState (const PRThread* thred)
   421 {
   422     PR_ASSERT(thred != NULL);
   423     return (thred->state & BT_THREAD_JOINABLE)?
   424     					PR_JOINABLE_THREAD: PR_UNJOINABLE_THREAD;
   425 }
   427 PR_IMPLEMENT(PRThreadPriority)
   428     PR_GetThreadPriority (const PRThread* thred)
   429 {
   430     PR_ASSERT(thred != NULL);
   431     return thred->priority;
   432 }  /* PR_GetThreadPriority */
   434 PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred,
   435                                         PRThreadPriority newPri)
   436 {
   437     PRUint32 bePriority;
   439     PR_ASSERT( thred != NULL );
   441     thred->priority = newPri;
   442     bePriority = _bt_MapNSPRToNativePriority( newPri );
   443     set_thread_priority( thred->md.tid, bePriority );
   444 }
   446 PR_IMPLEMENT(PRStatus)
   447     PR_NewThreadPrivateIndex (PRUintn* newIndex,
   448                               PRThreadPrivateDTOR destructor)
   449 {
   450 	int32    index;
   452     if (!_pr_initialized) _PR_ImplicitInitialization();
   454 	/* reserve the next available tpd slot */
   455 	index = atomic_add( &tpd_slotsUsed, 1 );
   456 	if (index >= BT_TPD_LIMIT)
   457 	{
   458 		/* no slots left - decrement value, then fail */
   459 		atomic_add( &tpd_slotsUsed, -1 );
   460 		PR_SetError( PR_TPD_RANGE_ERROR, 0 );
   461 	    return( PR_FAILURE );
   462 	}
   464 	/* allocate a beos-native TLS slot for this index (the new slot
   465 	   automatically contains NULL) */
   466 	tpd_beosTLSSlots[index] = tls_allocate();
   468 	/* remember the destructor */
   469 	tpd_dtors[index] = destructor;
   471     *newIndex = (PRUintn)index;
   473     return( PR_SUCCESS );
   474 }
   476 PR_IMPLEMENT(PRStatus)
   477     PR_SetThreadPrivate (PRUintn index, void* priv)
   478 {
   479 	void *oldValue;
   481     /*
   482     ** Sanity checking
   483     */
   485     if(index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
   486     {
   487 		PR_SetError( PR_TPD_RANGE_ERROR, 0 );
   488 	return( PR_FAILURE );
   489     }
   491 	/* if the old value isn't NULL, and the dtor for this slot isn't
   492 	   NULL, we must destroy the data */
   493 	oldValue = tls_get(tpd_beosTLSSlots[index]);
   494 	if (oldValue != NULL && tpd_dtors[index] != NULL)
   495 		(*tpd_dtors[index])(oldValue);
   497 	/* save new value */
   498 	tls_set(tpd_beosTLSSlots[index], priv);
   500 	    return( PR_SUCCESS );
   501 	}
   503 PR_IMPLEMENT(void*)
   504     PR_GetThreadPrivate (PRUintn index)
   505 {
   506 	/* make sure the index is valid */
   507 	if (index < 0 || index >= tpd_slotsUsed || index >= BT_TPD_LIMIT)
   508     {   
   509 		PR_SetError( PR_TPD_RANGE_ERROR, 0 );
   510 		return NULL;
   511     }
   513 	/* return the value */
   514 	return tls_get( tpd_beosTLSSlots[index] );
   515 	}
   518 PR_IMPLEMENT(PRStatus)
   519     PR_Interrupt (PRThread* thred)
   520 {
   521     PRIntn rv;
   523     PR_ASSERT(thred != NULL);
   525     /*
   526     ** there seems to be a bug in beos R5 in which calling
   527     ** resume_thread() on a blocked thread returns B_OK instead
   528     ** of B_BAD_THREAD_STATE (beos bug #20000422-19095).  as such,
   529     ** to interrupt a thread, we will simply suspend then resume it
   530     ** (no longer call resume_thread(), check for B_BAD_THREAD_STATE,
   531     ** the suspend/resume to wake up a blocked thread).  this wakes
   532     ** up blocked threads properly, and doesn't hurt unblocked threads
   533     ** (they simply get stopped then re-started immediately)
   534     */
   536     rv = suspend_thread( thred->md.tid );
   537     if( rv != B_NO_ERROR )
   538     {
   539         /* this doesn't appear to be a valid thread_id */
   540         PR_SetError( PR_UNKNOWN_ERROR, rv );
   541         return PR_FAILURE;
   542     }
   544     rv = resume_thread( thred->md.tid );
   545     if( rv != B_NO_ERROR )
   546     {
   547         PR_SetError( PR_UNKNOWN_ERROR, rv );
   548         return PR_FAILURE;
   549     }
   551     return PR_SUCCESS;
   552 }
   554 PR_IMPLEMENT(void)
   555     PR_ClearInterrupt ()
   556 {
   557 }
   559 PR_IMPLEMENT(PRStatus)
   560     PR_Yield ()
   561 {
   562     /* we just sleep for long enough to cause a reschedule (100
   563        microseconds) */
   564     snooze(100);
   565 }
   567 #define BT_MILLION 1000000UL
   569 PR_IMPLEMENT(PRStatus)
   570     PR_Sleep (PRIntervalTime ticks)
   571 {
   572     bigtime_t tps;
   573     status_t status;
   575     if (!_pr_initialized) _PR_ImplicitInitialization();
   577     tps = PR_IntervalToMicroseconds( ticks );
   579     status = snooze(tps);
   580     if (status == B_NO_ERROR) return PR_SUCCESS;
   582     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, status);
   583     return PR_FAILURE;
   584 }
   586 PR_IMPLEMENT(PRStatus)
   587     PR_Cleanup ()
   588 {
   589     PRThread *me = PR_GetCurrentThread();
   591     PR_ASSERT(me->state & BT_THREAD_PRIMORD);
   592     if ((me->state & BT_THREAD_PRIMORD) == 0) {
   593         return PR_FAILURE;
   594     }
   596     PR_Lock( bt_book.ml );
   598 	if (bt_book.threadCount != 0)
   599     {
   600 		/* we'll have to wait for some threads to finish - create a
   601 		   sem to block on */
   602 		bt_book.cleanUpSem = create_sem(0, "cleanup sem");
   603     }
   605     PR_Unlock( bt_book.ml );
   607 	/* note that, if all the user threads were already dead, we
   608 	   wouldn't have created a sem above, so this acquire_sem()
   609 	   will fail immediately */
   610 	while (acquire_sem(bt_book.cleanUpSem) == B_INTERRUPTED);
   612     return PR_SUCCESS;
   613 }
   615 PR_IMPLEMENT(void)
   616     PR_ProcessExit (PRIntn status)
   617 {
   618     exit(status);
   619 }
   621 PRThread *_bt_AttachThread()
   622 {
   623 	PRThread *thread;
   624 	thread_info tInfo;
   626 	/* make sure this thread doesn't already have a PRThread structure */
   627 	PR_ASSERT(tls_get(tls_prThreadSlot) == NULL);
   629 	/* allocate a PRThread structure for this thread */
   630 	thread = PR_NEWZAP(PRThread);
   631 	if (thread == NULL)
   632 	{
   633 		PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
   634 		return NULL;
   635 	}
   637 	/* get the native thread's current state */
   638 	get_thread_info(find_thread(NULL), &tInfo);
   640 	/* initialize new PRThread */
   641 	thread->md.tid = tInfo.thread;
   642 	thread->md.joinSem = B_ERROR;
   643 	thread->priority = _bt_MapNativeToNSPRPriority(tInfo.priority);
   645 	/* attached threads are always non-joinable user threads */
   646 	thread->state = 0;
   648 	/* increment user thread count */
   649 	PR_Lock(bt_book.ml);
   650 	bt_book.threadCount++;
   651 	PR_Unlock(bt_book.ml);
   653 	/* store this thread's PRThread */
   654 	tls_set(tls_prThreadSlot, thread);
   656 	/* the thread must call _bt_CleanupThread() before it dies, in order
   657 	   to clean up its PRThread, synchronize with the primordial thread,
   658 	   etc. */
   659 	on_exit_thread(_bt_CleanupThread, NULL);
   661 	return thread;
   662 }

mercurial