os/kernelhwsrv/kernel/eka/nkernsmp/nkern.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of the License "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // e32\nkernsmp\nkern.cpp
    15 // 
    16 //
    17 
    18 // NThreadBase member data
    19 #define __INCLUDE_NTHREADBASE_DEFINES__
    20 
    21 #include "nk_priv.h"
    22 
    23 /******************************************************************************
    24  * Fast mutex
    25  ******************************************************************************/
    26 
    27 /** Acquires the fast mutex.
    28 
    29     This will block until the mutex is available, and causes
    30 	the thread to enter an implicit critical section until the mutex is released.
    31 
    32 	Generally threads would use NKern::FMWait() which manipulates the kernel lock
    33 	for you.
    34 	
    35 	@pre Kernel must be locked, with lock count 1.
    36 	@pre The calling thread holds no fast mutexes.
    37 	
    38 	@post Kernel is locked, with lock count 1.
    39 	@post The calling thread holds the mutex.
    40 	
    41 	@see NFastMutex::Signal()
    42 	@see NKern::FMWait()
    43 */
    44 EXPORT_C void NFastMutex::Wait()
    45 	{
    46 	NThreadBase* pC = NCurrentThreadL();
    47 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED_ONCE|MASK_NO_FAST_MUTEX,"NFastMutex::Wait");
    48 
    49 	pC->iHeldFastMutex = this;		// to handle kill/suspend between here and setting iHeldFastMutex
    50 	DoWaitL();
    51 	}
    52 
    53 void NFastMutex::DoWaitL()
    54 	{
    55 	NThreadBase* pC = NCurrentThreadL();
    56 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T FMWait %M",pC,this));
    57 	TBool waited = FALSE;				// set if we actually had to wait
    58 	iMutexLock.LockOnly();			// acquire mutex spin lock
    59 	__e32_atomic_ior_rlx_ptr(&iHoldingThread, 1);		// set contention flag to make sure any other thread must acquire the mutex spin lock
    60 	pC->AcqSLock();
    61 	FOREVER
    62 		{
    63 		if (pC->iFastMutexDefer == 1)
    64 			--pC->iParent->iFreezeCpu;
    65 		pC->iFastMutexDefer = 0;
    66 		NThreadBase* pH = (NThreadBase*)(TLinAddr(iHoldingThread) &~ 1);
    67 		if (!pH)
    68 			{
    69 			// mutex is free
    70 			TInt wp = iWaitQ.HighestPriority();		// -1 if no other thread wants the mutex
    71 
    72 			// don't grab mutex if we have been suspended/killed/migrated by the previous holding thread
    73 			if (!pC->iSuspended && pC->iCsFunction!=NThreadBase::ECSDivertPending && (!pC->iParent->iCpuChange || pC->iParent->iFreezeCpu))
    74 				{
    75 				TInt p = pC->iPriority;
    76 				if (p>wp || (p==wp && waited))
    77 					{
    78 					// if we are highest priority waiting thread or equal and we have waited then grab the mutex
    79 					// don't just grab it if we are equal priority and someone else was already waiting
    80 					// set contention flag if other threads waiting or if current thread has a round robin outstanding
    81 					pC->iMutexPri = (TUint8)(wp>=0 ? wp : 0);	// pC's actual priority doesn't change since p>=wp
    82 					iHoldingThread = (wp>=0 || TUint32(pC->iTime)==0x80000000u) ? (NThreadBase*)(TLinAddr(pC)|1) : pC;
    83 					__KTRACE_OPT(KNKERN,DEBUGPRINT("%T got mutex %M CF=%d WP=%d",TLinAddr(iHoldingThread)&~1,this,TLinAddr(iHoldingThread)&1,wp));
    84 					pC->RelSLock();
    85 					iMutexLock.UnlockOnly();
    86 #ifdef BTRACE_FAST_MUTEX
    87 					BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexWait, this);
    88 #endif
    89 					return;
    90 					}
    91 				}
    92 			}
    93 		pC->iFastMutexDefer = 2;	// signal to scheduler to allow ctxsw without incrementing iParent->iFreezeCpu
    94 		if (!pC->iSuspended && pC->iCsFunction!=NThreadBase::ECSDivertPending && (!pC->iParent->iCpuChange || pC->iParent->iFreezeCpu))
    95 			{
    96 			// this forces priority changes to wait for the mutex lock
    97 			pC->iLinkedObjType = NThreadBase::EWaitFastMutex;
    98 			pC->iLinkedObj = this;
    99 			pC->iWaitState.SetUpWait(NThreadBase::EWaitFastMutex, NThreadWaitState::EWtStObstructed, this);
   100 			pC->iWaitLink.iPriority = pC->iPriority;
   101 			iWaitQ.Add(&pC->iWaitLink);
   102 			pC->RelSLock();
   103 			if (pH)
   104 				pH->SetMutexPriority(this);
   105 do_pause:
   106 			iMutexLock.UnlockOnly();
   107 			RescheduleNeeded();
   108 #ifdef BTRACE_FAST_MUTEX
   109 			BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexBlock, this);
   110 #endif
   111 			NKern::PreemptionPoint();	// we block here until the mutex is released and we are 'nominated' for it or we are suspended/killed
   112 			iMutexLock.LockOnly();
   113 			pC->AcqSLock();
   114 			if (pC->iPauseCount || pC->iSuspended || pC->iCsFunction==NThreadBase::ECSDivertPending || (pC->iParent->iCpuChange && !pC->iParent->iFreezeCpu))
   115 				{
   116 				pC->RelSLock();
   117 				goto do_pause;			// let pause/suspend/kill take effect
   118 				}
   119 			// if thread was suspended it will have been removed from the wait queue
   120 			if (!pC->iLinkedObj)
   121 				goto thread_suspended;
   122 			iWaitQ.Remove(&pC->iWaitLink);	// take ourselves off the wait/contend queue while we try to grab the mutex
   123 			pC->iWaitLink.iNext = 0;
   124 			pC->iLinkedObj = 0;
   125 			pC->iLinkedObjType = NThreadBase::EWaitNone;
   126 			waited = TRUE;
   127 			// if we are suspended or killed, we loop round again and do the 'else' clause next time
   128 			}
   129 		else
   130 			{
   131 			pC->RelSLock();
   132 			if (pC->iSuspended || pC->iCsFunction==NThreadBase::ECSDivertPending)
   133 				{
   134 				// wake up next thread to take this one's place
   135 				if (!pH && !iWaitQ.IsEmpty())
   136 					{
   137 					NThreadBase* pT = _LOFF(iWaitQ.First(), NThreadBase, iWaitLink);
   138 					pT->AcqSLock();
   139 					// if thread is still blocked on this fast mutex, release it but leave it on the wait queue
   140 					// NOTE: it can't be suspended
   141 					pT->iWaitState.UnBlockT(NThreadBase::EWaitFastMutex, this, KErrNone);
   142 					pT->RelSLock();
   143 					}
   144 				}
   145 			iMutexLock.UnlockOnly();
   146 			NKern::PreemptionPoint();	// thread suspends/dies/migrates here
   147 			iMutexLock.LockOnly();
   148 			pC->AcqSLock();
   149 thread_suspended:
   150 			waited = FALSE;
   151 			// set contention flag to make sure any other thread must acquire the mutex spin lock
   152 			// need to do it again since mutex may have been released while thread was suspended
   153 			__e32_atomic_ior_rlx_ptr(&iHoldingThread, 1);
   154 			}
   155 		}
   156 	}
   157 
   158 
   159 #ifndef __FAST_MUTEX_MACHINE_CODED__
   160 /** Releases a previously acquired fast mutex.
   161 	
   162 	Generally, threads would use NKern::FMSignal() which manipulates the kernel lock
   163 	for you.
   164 	
   165 	@pre The calling thread holds the mutex.
   166 	@pre Kernel must be locked.
   167 	
   168 	@post Kernel is locked.
   169 	
   170 	@see NFastMutex::Wait()
   171 	@see NKern::FMSignal()
   172 */
   173 EXPORT_C void NFastMutex::Signal()
   174 	{
   175 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED,"NFastMutex::Signal");
   176 #ifdef BTRACE_FAST_MUTEX
   177 	BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexSignal, this);
   178 #endif
   179 	NThreadBase* pC = NCurrentThreadL();
   180 	((volatile TUint32&)pC->iHeldFastMutex) |= 1;	// flag to indicate about to release mutex
   181 
   182 	if (__e32_atomic_cas_rel_ptr(&iHoldingThread, &pC, 0))
   183 		{
   184 		// tricky if suspend/kill here
   185 		// suspend/kill should check flag set above and aMutex->iHoldingThread
   186 		// if bit 0 of iHeldFastMutex set and iHoldingThread==pC then set iHeldFastMutex=0 and proceed
   187 
   188 		// no-one else was waiting for the mutex - simple
   189 		pC->iHeldFastMutex = 0;
   190 		return;
   191 		}
   192 
   193 	// there was contention so do it the hard way
   194 	DoSignalL();
   195 	}
   196 #endif
   197 
   198 void NFastMutex::DoSignalL()
   199 	{
   200 	NThreadBase* pC = NCurrentThreadL();
   201 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T FMSignal %M",pC,this));
   202 	__ASSERT_WITH_MESSAGE_DEBUG(HeldByCurrentThread(),"The calling thread holds the mutex","NFastMutex::Signal");
   203 
   204 	iMutexLock.LockOnly();
   205 	if (!iWaitQ.IsEmpty())
   206 		{
   207 		NThreadBase* pT = _LOFF(iWaitQ.First(), NThreadBase, iWaitLink);
   208 		pT->AcqSLock();
   209 
   210 		// if thread is still blocked on this fast mutex, release it but leave it on the wait queue
   211 		// NOTE: it can't be suspended
   212 		pT->iWaitState.UnBlockT(NThreadBase::EWaitFastMutex, this, KErrNone);
   213 		pT->RelSLock();
   214 		iHoldingThread = (NThreadBase*)1;	// mark mutex as released but contended
   215 		}
   216 	else
   217 		iHoldingThread = 0;	// mark mutex as released and uncontended
   218 	__KTRACE_OPT(KNKERN,DEBUGPRINT("SiHT=%d",iHoldingThread));
   219 	pC->AcqSLock();
   220 	pC->iHeldFastMutex = 0;
   221 	iMutexLock.UnlockOnly();
   222 	pC->iMutexPri = 0;
   223 	if (pC->iPriority != pC->iBasePri)
   224 		{
   225 		// lose any inherited priority
   226 		pC->LoseInheritedPriorityT();
   227 		}
   228 	if (TUint32(pC->iTime)==0x80000000u)
   229 		{
   230 		pC->iTime = 0;
   231 		RescheduleNeeded();	// handle deferred timeslicing
   232 		__KTRACE_OPT(KNKERN,DEBUGPRINT("DTS %T",pC));
   233 		}
   234 	if (pC->iFastMutexDefer)
   235 		{
   236 		pC->iFastMutexDefer = 0;
   237 		--pC->iParent->iFreezeCpu;
   238 		}
   239 	if (pC->iParent->iCpuChange && !pC->iParent->iFreezeCpu)
   240 		RescheduleNeeded();	// need to migrate to another CPU
   241 	if (!pC->iCsCount && pC->iCsFunction)
   242 		pC->DoCsFunctionT();
   243 	pC->RelSLock();
   244 	}
   245 
   246 
   247 /** Checks if the current thread holds this fast mutex
   248 
   249 	@return TRUE if the current thread holds this fast mutex
   250 	@return FALSE if not
   251 	@pre	Call in thread context.
   252 */
   253 EXPORT_C TBool NFastMutex::HeldByCurrentThread()
   254 	{
   255 	return (TLinAddr(iHoldingThread)&~1) == (TLinAddr)NKern::CurrentThread();
   256 	}
   257 
   258 
   259 /** Returns the fast mutex held by the calling thread, if any.
   260 
   261 	@return	If the calling thread currently holds a fast mutex, this function
   262 			returns a pointer to it; otherwise it returns NULL.
   263 	@pre	Call in thread context.
   264 */
   265 EXPORT_C NFastMutex* NKern::HeldFastMutex()
   266 	{
   267 	NThreadBase* t = NKern::CurrentThread();
   268 	NFastMutex* m = (NFastMutex*)(TLinAddr(t->iHeldFastMutex)&~3);
   269 	return (m && m->HeldByCurrentThread()) ? m : 0;
   270 	}
   271 
   272 	
   273 #ifndef __FAST_MUTEX_MACHINE_CODED__
   274 /** Acquires a fast mutex.
   275 
   276     This will block until the mutex is available, and causes
   277 	the thread to enter an implicit critical section until the mutex is released.
   278 
   279 	@param aMutex The fast mutex to acquire.
   280 	
   281 	@post The calling thread holds the mutex.
   282 	
   283 	@see NFastMutex::Wait()
   284 	@see NKern::FMSignal()
   285 
   286 	@pre No fast mutex can be held.
   287 	@pre Call in a thread context.
   288 	@pre Kernel must be unlocked
   289 	@pre interrupts enabled
   290 
   291 */
   292 EXPORT_C void NKern::FMWait(NFastMutex* aMutex)
   293 	{
   294 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NFMW %M", aMutex));
   295 	CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"NKern::FMWait");
   296 	NThreadBase* pC = NKern::CurrentThread();
   297 
   298 	// If the reschedule IPI from an external suspend or kill occurs after this
   299 	// point the initiating CPU must observe the write to iHeldFastMutex before
   300 	// the cas operation.
   301 	pC->iHeldFastMutex = aMutex;	// kill/suspend after this point should set mutex contention flag
   302 	NThreadBase* expect = 0;
   303 	if (__e32_atomic_cas_acq_ptr(&aMutex->iHoldingThread, &expect, pC))
   304 		{
   305 		// mutex was free and we have just claimed it - simple
   306 #ifdef BTRACE_FAST_MUTEX
   307 		BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexWait, aMutex);
   308 #endif
   309 		return;
   310 		}
   311 
   312 	// care required if suspend/kill here
   313 
   314 	// there is contention so do it the hard way
   315 	NKern::Lock();
   316 	aMutex->DoWaitL();
   317 	NKern::Unlock();
   318 	}
   319 
   320 
   321 /** Releases a previously acquired fast mutex.
   322 	
   323 	@param aMutex The fast mutex to release.
   324 	
   325 	@pre The calling thread holds the mutex.
   326 	
   327 	@see NFastMutex::Signal()
   328 	@see NKern::FMWait()
   329 */
   330 EXPORT_C void NKern::FMSignal(NFastMutex* aMutex)
   331 	{
   332 	NThreadBase* pC = NKern::CurrentThread();
   333 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NFMS %M", aMutex));
   334 #ifdef BTRACE_FAST_MUTEX
   335 	BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexSignal, aMutex);
   336 #endif
   337 	((volatile TUint32&)pC->iHeldFastMutex) |= 1;	// flag to indicate about to release mutex
   338 
   339 	if (__e32_atomic_cas_rel_ptr(&aMutex->iHoldingThread, &pC, 0))
   340 		{
   341 		// no-one else was waiting for the mutex and we have just released it
   342 
   343 		// tricky if suspend/kill here
   344 		// suspend/kill should check flag set above and aMutex->iHoldingThread
   345 		// if bit 0 of iHeldFastMutex set and iHoldingThread==pC then set iHeldFastMutex=0 and proceed
   346 
   347 		// If the reschedule IPI from an external suspend or kill occurs after this
   348 		// point the initiating CPU must observe the write to iHeldFastMutex after
   349 		// the cas operation.
   350 		pC->iHeldFastMutex = 0;
   351 		return;
   352 		}
   353 
   354 	// there was contention so do it the hard way
   355 	NKern::Lock();
   356 	aMutex->DoSignalL();
   357 	NKern::Unlock();
   358 	}
   359 
   360 /** Acquires the System Lock.
   361 
   362     This will block until the mutex is available, and causes
   363 	the thread to enter an implicit critical section until the mutex is released.
   364 
   365 	@post System lock is held.
   366 
   367 	@see NKern::UnlockSystem()
   368 	@see NKern::FMWait()
   369 
   370 	@pre No fast mutex can be held.
   371 	@pre Call in a thread context.
   372 	@pre Kernel must be unlocked
   373 	@pre interrupts enabled
   374 
   375 */
   376 EXPORT_C void NKern::LockSystem()
   377 	{
   378 	NKern::FMWait(&TheScheduler.iLock);
   379 	}
   380 
   381 
   382 /** Releases the System Lock.
   383 
   384 	@pre System lock must be held.
   385 
   386 	@see NKern::LockSystem()
   387 	@see NKern::FMSignal()
   388 */
   389 EXPORT_C void NKern::UnlockSystem()
   390 	{
   391 	NKern::FMSignal(&TheScheduler.iLock);
   392 	}
   393 
   394 
   395 /** Temporarily releases a fast mutex if there is contention.
   396 
   397     If there is another thread attempting to acquire the mutex, the calling
   398 	thread releases the mutex and then acquires it again.
   399 	
   400 	This is more efficient than the equivalent code:
   401 	
   402 	@code
   403 	NKern::FMSignal();
   404 	NKern::FMWait();
   405 	@endcode
   406 
   407 	@return	TRUE if the mutex was relinquished, FALSE if not.
   408 
   409 	@pre	The mutex must be held.
   410 
   411 	@post	The mutex is held.
   412 */
   413 EXPORT_C TBool NKern::FMFlash(NFastMutex* aM)
   414 	{
   415 	NThreadBase* pC = NKern::CurrentThread();
   416 	__ASSERT_WITH_MESSAGE_DEBUG(aM->HeldByCurrentThread(),"The calling thread holds the mutex","NKern::FMFlash");
   417 	TBool w = (pC->iMutexPri >= pC->iBasePri);	// a thread of greater or equal priority is waiting
   418 	if (w)
   419 		{
   420 		NKern::Lock();
   421 		aM->Signal();
   422 		NKern::PreemptionPoint();
   423 		aM->Wait();
   424 		NKern::Unlock();
   425 		}
   426 #ifdef BTRACE_FAST_MUTEX
   427 	else
   428 		{
   429 		BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexFlash, aM);
   430 		}
   431 #endif
   432 	return w;
   433 	}
   434 
   435 
   436 /** Temporarily releases the System Lock if there is contention.
   437 
   438     If there
   439 	is another thread attempting to acquire the System lock, the calling
   440 	thread releases the mutex and then acquires it again.
   441 	
   442 	This is more efficient than the equivalent code:
   443 	
   444 	@code
   445 	NKern::UnlockSystem();
   446 	NKern::LockSystem();
   447 	@endcode
   448 
   449 	Note that this can only allow higher priority threads to use the System
   450 	lock as lower priority cannot cause contention on a fast mutex.
   451 
   452 	@return	TRUE if the system lock was relinquished, FALSE if not.
   453 
   454 	@pre	System lock must be held.
   455 
   456 	@post	System lock is held.
   457 
   458 	@see NKern::LockSystem()
   459 	@see NKern::UnlockSystem()
   460 */
   461 EXPORT_C TBool NKern::FlashSystem()
   462 	{
   463 	CHECK_PRECONDITIONS(MASK_SYSTEM_LOCKED,"NKern::FlashSystem");
   464 	return NKern::FMFlash(&TheScheduler.iLock);
   465 	}
   466 #endif
   467 
   468 /******************************************************************************
   469  * Fast semaphore
   470  ******************************************************************************/
   471 
   472 /** Sets the owner of a fast semaphore.
   473 
   474 	@param aThread The thread to own this semaphore. If aThread==0, then the
   475 					owner is set to the current thread.
   476 
   477 	@pre Kernel must be locked.
   478 	@pre If changing ownership form one thread to another, the there must be no
   479 		 pending signals or waits.
   480 	@pre Call either in a thread or an IDFC context.
   481 	
   482 	@post Kernel is locked.
   483 */
   484 EXPORT_C void NFastSemaphore::SetOwner(NThreadBase* aThread)
   485 	{
   486 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR,"NFastSemaphore::SetOwner");		
   487 	if (!aThread)
   488 		aThread = NCurrentThreadL();
   489 	if (iOwningThread && iOwningThread!=aThread)
   490 		{
   491 		__NK_ASSERT_ALWAYS(!iCount);	// Can't change owner if iCount!=0
   492 		}
   493 	iOwningThread = aThread;
   494 	}
   495 
   496 
   497 #ifndef __FAST_SEM_MACHINE_CODED__
   498 /** Waits on a fast semaphore.
   499 
   500     Decrements the signal count for the semaphore and
   501 	removes the calling thread from the ready-list if the semaphore becomes
   502 	unsignalled. Only the thread that owns a fast semaphore can wait on it.
   503 	
   504 	Note that this function does not block, it merely updates the NThread state,
   505 	rescheduling will only occur when the kernel is unlocked. Generally threads
   506 	would use NKern::FSWait() which manipulates the kernel lock for you.
   507 
   508 	@pre The calling thread must own the semaphore.
   509 	@pre No fast mutex can be held.
   510 	@pre Kernel must be locked.
   511 	
   512 	@post Kernel is locked.
   513 	
   514 	@see NFastSemaphore::Signal()
   515 	@see NKern::FSWait()
   516 	@see NKern::Unlock()
   517  */
   518 EXPORT_C void NFastSemaphore::Wait()
   519 	{
   520 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NO_FAST_MUTEX,"NFastSemaphore::Wait");
   521 	NThreadBase* pC = NCurrentThreadL();
   522 	__ASSERT_WITH_MESSAGE_ALWAYS(pC==iOwningThread,"The calling thread must own the semaphore","NFastSemaphore::Wait");
   523 	pC->iWaitState.SetUpWait(NThreadBase::EWaitFastSemaphore, 0, this);
   524 	if (Dec(pC))						// full barrier
   525 		pC->iWaitState.CancelWait();	// don't have to wait
   526 	else
   527 		RescheduleNeeded();				// have to wait
   528 	}
   529 
   530 
   531 /** Signals a fast semaphore.
   532 
   533     Increments the signal count of a fast semaphore by
   534 	one and releases any waiting thread if the semphore becomes signalled.
   535 	
   536 	Note that a reschedule will not occur before this function returns, this will
   537 	only take place when the kernel is unlocked. Generally threads
   538 	would use NKern::FSSignal() which manipulates the kernel lock for you.
   539 	
   540 	@pre Kernel must be locked.
   541 	@pre Call either in a thread or an IDFC context.
   542 	
   543 	@post Kernel is locked.
   544 	
   545 	@see NFastSemaphore::Wait()
   546 	@see NKern::FSSignal()
   547 	@see NKern::Unlock()
   548  */
   549 EXPORT_C void NFastSemaphore::Signal()
   550 	{
   551 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR,"NFastSemaphore::Signal");			
   552 	NThreadBase* t = Inc(1);	// release semantics
   553 	if (t)
   554 		{
   555 		t->AcqSLock();
   556 		t->iWaitState.UnBlockT(NThreadBase::EWaitFastSemaphore, this, KErrNone);
   557 		t->RelSLock();
   558 		}
   559 	}
   560 
   561 
   562 /** Signals a fast semaphore multiple times.
   563 
   564 	@pre Kernel must be locked.
   565 	@pre Call either in a thread or an IDFC context.
   566 	
   567 	@post Kernel is locked.
   568 
   569 	@internalComponent	
   570  */
   571 EXPORT_C void NFastSemaphore::SignalN(TInt aCount)
   572 	{
   573 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR,"NFastSemaphore::SignalN");			
   574 	__NK_ASSERT_DEBUG(aCount>=0);
   575 	if (aCount > 0)
   576 		{
   577 		NThreadBase* t = Inc(aCount);
   578 		if (t)
   579 			{
   580 			t->AcqSLock();
   581 			t->iWaitState.UnBlockT(NThreadBase::EWaitFastSemaphore, this, KErrNone);
   582 			t->RelSLock();
   583 			}
   584 		}
   585 	}
   586 
   587 
   588 /** Cancels a wait on a fast semaphore.
   589 
   590 	@pre Kernel must be locked.
   591 	@pre Call either in a thread or an IDFC context.
   592 	
   593 	@post Kernel is locked.
   594 
   595 	@internalComponent	
   596  */
   597 void NFastSemaphore::WaitCancel()
   598 	{
   599 	Inc(1);
   600 	}
   601 
   602 
   603 /** Waits for a signal on the current thread's I/O semaphore.
   604 
   605 	@pre No fast mutex can be held.
   606 	@pre Call in a thread context.
   607 	@pre Kernel must be unlocked
   608 	@pre interrupts enabled
   609  */
   610 EXPORT_C void NKern::WaitForAnyRequest()
   611 	{
   612 	CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"NKern::WaitForAnyRequest");
   613 	__KTRACE_OPT(KNKERN,DEBUGPRINT("WfAR"));
   614 	NThreadBase* t = NKern::LockC();
   615 	NFastSemaphore* s = &t->iRequestSemaphore;
   616 	t->iWaitState.SetUpWait(NThreadBase::EWaitFastSemaphore, 0, s);
   617 	if (s->Dec(t))					// fully ordered semantics
   618 		t->iWaitState.CancelWait();	// don't have to wait
   619 	else
   620 		RescheduleNeeded();			// have to wait
   621 	NKern::Unlock();
   622 	}
   623 #endif
   624 
   625 
   626 /** Resets a fast semaphore.
   627 
   628 	@pre Kernel must be locked.
   629 	@pre Call either in a thread or an IDFC context.
   630 	
   631 	@post Kernel is locked.
   632 
   633 	@internalComponent	
   634  */
   635 EXPORT_C void NFastSemaphore::Reset()
   636 	{
   637 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR,"NFastSemaphore::Reset");
   638 	NThreadBase* t = DoReset();
   639 	if (t)
   640 		{
   641 		t->AcqSLock();
   642 		t->iWaitState.UnBlockT(NThreadBase::EWaitFastSemaphore, this, KErrNone);
   643 		t->RelSLock();
   644 		}
   645 	}
   646 
   647 
   648 /** Sets the owner of a fast semaphore.
   649 
   650 	@param aSem The semaphore to change ownership off.
   651 	@param aThread The thread to own this semaphore. If aThread==0, then the
   652 					owner is set to the current thread.
   653 
   654 	@pre If changing ownership form one thread to another, the there must be no
   655 		 pending signals or waits.
   656 */
   657 EXPORT_C void NKern::FSSetOwner(NFastSemaphore* aSem,NThreadBase* aThread)
   658 	{
   659 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NKern::FSSetOwner %m %T",aSem,aThread));
   660 	NKern::Lock();
   661 	aSem->SetOwner(aThread);
   662 	NKern::Unlock();
   663 	}
   664 
   665 #ifndef __FAST_SEM_MACHINE_CODED__
   666 /** Waits on a fast semaphore.
   667 
   668     Decrements the signal count for the semaphore
   669 	and waits for a signal if the semaphore becomes unsignalled. Only the
   670 	thread that owns a fast	semaphore can wait on it.
   671 
   672 	@param aSem The semaphore to wait on.
   673 	
   674 	@pre The calling thread must own the semaphore.
   675 	@pre No fast mutex can be held.
   676 	
   677 	@see NFastSemaphore::Wait()
   678 */
   679 EXPORT_C void NKern::FSWait(NFastSemaphore* aSem)
   680 	{
   681 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NFSW %m",aSem));
   682 	NKern::Lock();
   683 	aSem->Wait();
   684 	NKern::Unlock();
   685 	}
   686 
   687 
   688 /** Signals a fast semaphore.
   689 
   690     Increments the signal count of a fast semaphore
   691 	by one and releases any	waiting thread if the semaphore becomes signalled.
   692 	
   693 	@param aSem The semaphore to signal.
   694 
   695 	@see NKern::FSWait()
   696 
   697 	@pre Interrupts must be enabled.
   698 	@pre Do not call from an ISR
   699  */
   700 EXPORT_C void NKern::FSSignal(NFastSemaphore* aSem)
   701 	{
   702 	CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR,"NKern::FSSignal(NFastSemaphore*)");
   703 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NFSS %m",aSem));
   704 	NKern::Lock();
   705 	aSem->Signal();
   706 	NKern::Unlock();
   707 	}
   708 
   709 
   710 /** Signals a fast semaphore multiple times.
   711 
   712     Increments the signal count of a
   713 	fast semaphore by aCount and releases any waiting thread if the semphore
   714 	becomes signalled.
   715 	
   716 	@param aSem The semaphore to signal.
   717 	@param aCount The number of times to signal the semaphore.
   718 
   719 	@see NKern::FSWait()
   720 
   721 	@pre Interrupts must be enabled.
   722 	@pre Do not call from an ISR
   723  */
   724 EXPORT_C void NKern::FSSignalN(NFastSemaphore* aSem, TInt aCount)
   725 	{
   726 	CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR,"NKern::FSSignalN(NFastSemaphore*, TInt)");
   727 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NFSSN %m %d",aSem,aCount));
   728 	__NK_ASSERT_DEBUG(aCount>=0);
   729 	if (aCount == 0)
   730 		return;
   731 	NKern::Lock();
   732 	aSem->SignalN(aCount);
   733 	NKern::Unlock();
   734 	}
   735 
   736 
   737 /** Signals the request semaphore of a nanothread.
   738 
   739 	This function is intended to be used by the EPOC layer and personality
   740 	layers.  Device drivers should use Kern::RequestComplete instead.
   741 
   742 	@param aThread Nanothread to signal. Must be non NULL.
   743 
   744 	@see Kern::RequestComplete()
   745 
   746 	@pre Interrupts must be enabled.
   747 	@pre Do not call from an ISR
   748  */
   749 EXPORT_C void NKern::ThreadRequestSignal(NThread* aThread)
   750 	{
   751 	NKern::FSSignal(&aThread->iRequestSemaphore);
   752 	}
   753 
   754 
   755 /** Signals the request semaphore of a nanothread several times.
   756 
   757 	This function is intended to be used by the EPOC layer and personality
   758 	layers.  Device drivers should use Kern::RequestComplete instead.
   759 
   760 	@param aThread Nanothread to signal.  If NULL, the current thread is signaled.
   761 	@param aCount Number of times the request semaphore must be signaled.
   762 	
   763 	@pre aCount >= 0
   764 
   765 	@see Kern::RequestComplete()
   766  */
   767 EXPORT_C void NKern::ThreadRequestSignal(NThread* aThread, TInt aCount)
   768 	{
   769 	__ASSERT_WITH_MESSAGE_DEBUG(aCount >= 0,"aCount >= 0","NKern::ThreadRequestSignal");
   770 	if (!aThread)
   771 		aThread = (NThread*)NKern::CurrentThread();
   772 	NKern::FSSignalN(&aThread->iRequestSemaphore, aCount);
   773 	}
   774 #endif
   775 
   776 
   777 
   778 /** Atomically signals a fast semaphore and releases a fast mutex.
   779 
   780 	Rescheduling only occurs after both synchronisation operations are complete.
   781 	
   782 	@param aSem The semaphore to signal.
   783 	@param aMutex The mutex to release. If NULL, the System Lock is released
   784 
   785 	@pre The calling thread must hold the mutex.
   786 	
   787 	@see NKern::FMSignal()
   788  */
   789 EXPORT_C void NKern::FSSignal(NFastSemaphore* aSem, NFastMutex* aMutex)
   790 	{
   791 	if (!aMutex)
   792 		aMutex=&TheScheduler.iLock;
   793 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NFSS %m +FM %M",aSem,aMutex));
   794 	NKern::Lock();
   795 	aSem->Signal();
   796 	aMutex->Signal();
   797 	NKern::Unlock();
   798 	}
   799 
   800 
   801 /** Atomically signals a fast semaphore multiple times and releases a fast mutex.
   802 
   803 	Rescheduling only occurs after both synchronisation operations are complete.
   804 	
   805 	@param aSem The semaphore to signal.
   806 	@param aCount The number of times to signal the semaphore.
   807 	@param aMutex The mutex to release. If NULL, the System Lock is released.
   808 
   809 	@pre The calling thread must hold the mutex.
   810 	
   811 	@see NKern::FMSignal()
   812  */
   813 EXPORT_C void NKern::FSSignalN(NFastSemaphore* aSem, TInt aCount, NFastMutex* aMutex)
   814 	{
   815 	if (!aMutex)
   816 		aMutex=&TheScheduler.iLock;
   817 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NFSSN %m %d + FM %M",aSem,aCount,aMutex));
   818 	NKern::Lock();
   819 	aSem->SignalN(aCount);
   820 	aMutex->Signal();
   821 	NKern::Unlock();
   822 	}
   823 
   824 
   825 /******************************************************************************
   826  * Thread
   827  ******************************************************************************/
   828 
   829 void NThreadBase::DoCsFunctionT()
   830 	{
   831 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T nDoCsFuncT %d",this,iCsFunction));
   832 	TInt f=iCsFunction;
   833 	if (f==0)
   834 		return;
   835 	if (f>0)
   836 		{
   837 		// suspend this thread f times
   838 		iCsFunction = 0;
   839 		iSuspendCount += f;
   840 		iSuspended = 1;
   841 		RescheduleNeeded();
   842 		return;
   843 		}
   844 	if (f==ECSExitPending || f==ECSDivertPending)
   845 		{
   846 		// We need to exit now
   847 		RelSLock();
   848 		Exit();	// this won't return
   849 		}
   850 //	UnknownState(ELeaveCS,f);	// call into RTOS personality
   851 	__NK_ASSERT_ALWAYS(0);
   852 	}
   853 
   854 TBool NThreadBase::DoSuspendOrKillT(TInt aCount, TSubScheduler* aS)
   855 	{
   856 	TBool result = TRUE;
   857 	if (aCount>=0)
   858 		{
   859 		if (iSuspended)
   860 			result = FALSE;
   861 		iSuspendCount+=aCount;
   862 		iSuspended = 1;
   863 		if (!iCurrent)
   864 			{
   865 			if (aS)
   866 				UnReadyT();
   867 			else if (iReady)
   868 				{
   869 				NThreadGroup* g = (NThreadGroup*)iParent;
   870 				g->iNThreadList.Remove(this);
   871 				}
   872 			}
   873 		if (this == NCurrentThreadL())
   874 			RescheduleNeeded();
   875 		if (aS)
   876 			aS->iReadyListLock.UnlockOnly();
   877 		}
   878 	else
   879 		{
   880 		iCsFunction = ECSDivertPending;
   881 		iSuspendCount = 0;
   882 		iSuspended = 0;
   883 		if (aS)
   884 			aS->iReadyListLock.UnlockOnly();
   885 		DoReleaseT(KErrDied,0);
   886 		if (!iReady && !iPauseCount)
   887 			ReadyT(0);
   888 		}
   889 	return result;
   890 	}
   891 
   892 // If aCount>=0 suspend the thread aCount times
   893 // If aCount<0 kill the thread
   894 TBool NThreadBase::SuspendOrKill(TInt aCount)
   895 	{
   896 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T nSuspendOrKill %d", this, aCount));
   897 	if (aCount==0)
   898 		return FALSE;
   899 	TBool result = FALSE;
   900 	TBool concurrent = FALSE;
   901 	TSubScheduler* ss = 0;
   902 	AcqSLock();
   903 	NFastMutex* wfm = 0;
   904 	if (iLinkedObj && iLinkedObjType==EWaitFastMutex)
   905 		wfm = (NFastMutex*)iLinkedObj;
   906 	if (iCsFunction<0)
   907 		goto done2;	// if already exiting ignore suspend or kill
   908 	if (wfm)
   909 		{
   910 		// if thread is waiting on a fast mutex, need to acquire mutex lock
   911 		++iPauseCount;
   912 		RelSLock();
   913 		wfm->iMutexLock.LockOnly();
   914 		AcqSLock();
   915 		UnPauseT();
   916 		}
   917 	if (iReady && iParent->iReady)
   918 		{
   919 		ss = TheSubSchedulers + (iParent->iReady & EReadyCpuMask);
   920 		ss->iReadyListLock.LockOnly();
   921 		}
   922 	concurrent = (iCurrent && this!=NCurrentThreadL());
   923 	if (iWaitState.ThreadIsDead())				// already dead so suspension/kill is a no-op
   924 		goto done;
   925 	if (concurrent)
   926 		{
   927 		// thread is actually running on another CPU
   928 		// interrupt that CPU and wait for it to enter interrupt mode
   929 		// this allows a snapshot of the thread state to be observed
   930 		// in this state, the thread cannot enter or leave a critical section
   931 		send_resched_ipi_and_wait(iLastCpu);
   932 		}
   933 	if (iCsCount)
   934 		{
   935 suspend_or_kill_in_cs:
   936 		__KTRACE_OPT(KNKERN,DEBUGPRINT("n Suspend %T (CSF %d) %d",this,iCsFunction,aCount));
   937 		if (aCount>0)				// -ve means thread is about to exit
   938 			iCsFunction+=aCount;	// so thread will suspend itself when it leaves the critical section
   939 		else
   940 			iCsFunction = ECSExitPending;
   941 		goto done;
   942 		}
   943 	// iCsCount==0 and it can't become nonzero until we release the thread spin lock
   944 	// (since threads may not set iCsCount to a nonzero value with the kernel lock held)
   945 	// Make sure the thread isn't actually about to exit by itself
   946 	if (iCsFunction<0)
   947 		goto done;	// if already exiting ignore suspend or kill
   948 	if (wfm)
   949 		{
   950 		wfm->iWaitQ.Remove(&iWaitLink);	// take thread off the wait/contend queue
   951 		iWaitLink.iNext = 0;
   952 		iLinkedObj = 0;
   953 		iLinkedObjType = EWaitNone;
   954 		result = DoSuspendOrKillT(aCount, ss);
   955 		if (aCount>0)
   956 			DoReleaseT(KErrGeneral, 0);	// thread isn't blocked any more, just suspended
   957 		RelSLock();
   958 
   959 		// May need to adjust holding thread's inherited priority.
   960 		// May need to wake up next thread to take this one's place.
   961 		NThreadBase* pH = (NThreadBase*)(TLinAddr(wfm->iHoldingThread) &~ 1);
   962 		if (pH)
   963 			pH->SetMutexPriority(wfm);
   964 		else if (!pH && !wfm->iWaitQ.IsEmpty())
   965 			{
   966 			NThreadBase* pT = _LOFF(wfm->iWaitQ.First(), NThreadBase, iWaitLink);
   967 			pT->AcqSLock();
   968 			pT->iWaitState.UnBlockT(NThreadBase::EWaitFastMutex, wfm, KErrNone);
   969 			pT->RelSLock();
   970 			}
   971 		wfm->iMutexLock.UnlockOnly();
   972 		return result;
   973 		}
   974 	if (CheckFastMutexDefer())
   975 		goto suspend_or_kill_in_cs;
   976 
   977 	// thread not in critical section, so suspend it
   978 	result = DoSuspendOrKillT(aCount, ss);
   979 	goto done2;
   980 
   981 done:
   982 	if (wfm)
   983 		wfm->iMutexLock.UnlockOnly();
   984 	if (ss)
   985 		ss->iReadyListLock.UnlockOnly();
   986 done2:
   987 	RelSLock();
   988 
   989 	return result;
   990 	}
   991 
   992 
   993 /** Suspends a nanothread the specified number of times.
   994 	
   995 	For use by RTOS personality layers.
   996 	Do not use this function directly on a Symbian OS thread.
   997 	Since the kernel is locked on entry, any reschedule will be deferred until
   998 	it is unlocked.
   999 	The suspension will be deferred if the target thread is currently in a
  1000 	critical section; in this case the suspension will take effect when it exits
  1001 	the critical section.
  1002 	The thread's unknown state handler will be invoked with function ESuspend and
  1003 	parameter aCount if the current NState is not recognised and it is not in a
  1004 	critical section.
  1005 
  1006 	@param	aCount = the number of times to suspend.
  1007 	@return	TRUE, if the suspension has taken immediate effect;
  1008 			FALSE, if the thread is in a critical section or is already suspended.
  1009 	
  1010 	@pre	Kernel must be locked.
  1011 	@pre	Call in a thread context.
  1012 	
  1013 	@post	Kernel is locked.
  1014  */
  1015 EXPORT_C TBool NThreadBase::Suspend(TInt aCount)
  1016 	{
  1017 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"NThreadBase::Suspend");
  1018 	__NK_ASSERT_ALWAYS(aCount>=0);
  1019 
  1020 	// If thread is executing a critical section, we must defer the suspend
  1021 
  1022 	return SuspendOrKill(aCount);
  1023 	}
  1024 
  1025 
  1026 TBool NThreadBase::Resume(TBool aForce)
  1027 	{
  1028 	TBool result = FALSE;
  1029 	AcqSLock();
  1030 	if (iWaitState.ThreadIsDead() || iCsFunction<0)		// already dead or dying so resume is a no-op
  1031 		goto done;
  1032 
  1033 	if (iCsFunction>0)
  1034 		{
  1035 		if (aForce)
  1036 			iCsFunction = 0;
  1037 		else
  1038 			--iCsFunction;
  1039 		}
  1040 	else if (iSuspendCount)
  1041 		{
  1042 		if (aForce)
  1043 			iSuspendCount = 0;
  1044 		else
  1045 			--iSuspendCount;
  1046 		if (!iSuspendCount)
  1047 			{
  1048 			result = TRUE;
  1049 			iSuspended = 0;
  1050 			if (!iPauseCount && !iReady && !iWaitState.iWtC.iWtStFlags)
  1051 				ReadyT(0);
  1052 			}
  1053 		}
  1054 
  1055 done:
  1056 	RelSLock();
  1057 	return result;
  1058 	}
  1059 
  1060 /** Resumes a nanothread, cancelling one suspension.
  1061 	
  1062 	For use by RTOS personality layers.
  1063 	Do not use this function directly on a Symbian OS thread.
  1064 	Since the kernel is locked on entry, any reschedule will be deferred until
  1065 	it is unlocked.
  1066 	If the target thread is currently in a critical section this will simply
  1067 	cancel one deferred suspension.
  1068 	The thread's unknown state handler will be invoked with function EResume if
  1069 	the current NState is not recognised and it is not in a	critical section.
  1070 
  1071 	@return	TRUE, if the resumption has taken immediate effect;
  1072 			FALSE, if the thread is in a critical section or is still suspended.
  1073 	
  1074 	@pre	Kernel must be locked.
  1075 	@pre	Call either in a thread or an IDFC context.
  1076 	
  1077 	@post	Kernel must be locked.
  1078  */
  1079 EXPORT_C TBool NThreadBase::Resume()
  1080 	{
  1081 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"NThreadBase::Resume");
  1082 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T nRsm",this));
  1083 
  1084 	return Resume(FALSE);
  1085 	}
  1086 
  1087 
  1088 /** Resumes a nanothread, cancelling all outstanding suspensions.
  1089 	
  1090 	For use by RTOS personality layers.
  1091 	Do not use this function directly on a Symbian OS thread.
  1092 	Since the kernel is locked on entry, any reschedule will be deferred until
  1093 	it is unlocked.
  1094 	If the target thread is currently in a critical section this will simply
  1095 	cancel all deferred suspensions.
  1096 	The thread's unknown state handler will be invoked with function EForceResume
  1097 	if the current NState is not recognised and it is not in a	critical section.
  1098 
  1099 	@return	TRUE, if the resumption has taken immediate effect;
  1100 			FALSE, if the thread is in a critical section.
  1101 
  1102 	@pre	Kernel must be locked.
  1103 	@pre	Call either in a thread or an IDFC context.
  1104 
  1105 	@post	Kernel is locked.
  1106  */
  1107 EXPORT_C TBool NThreadBase::ForceResume()
  1108 	{
  1109 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR,"NThreadBase::ForceResume");
  1110 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T nFRsm",this));
  1111 
  1112 	return Resume(TRUE);
  1113 	}
  1114 
  1115 
  1116 void NThreadBase::DoReleaseT(TInt aReturnCode, TUint aMode)
  1117 	{
  1118 	TAny* wobj = 0;
  1119 	TUint32 b = iWaitState.ReleaseT(wobj, aReturnCode);	// cancels timer if necessary
  1120 
  1121 	// if wait pending or no wait, done
  1122 	// if wait in effect and nothing else stopping it, make thread ready
  1123 	// cancel any outstanding wait on fast semaphore if abnormal release
  1124 	// FIXME: Potential problems with abnormal release of generic wait objects
  1125 	if (aReturnCode<0 && ((b>>8)&0xff)==NThreadBase::EWaitFastSemaphore && wobj)
  1126 		((NFastSemaphore*)wobj)->WaitCancel();
  1127 
  1128 	if ((b & NThreadWaitState::EWtStWaitActive) && !iPauseCount && !iSuspended)
  1129 		ReadyT(aMode);
  1130 	}
  1131 
  1132 /** Releases a waiting nanokernel thread.
  1133 
  1134 	For use by RTOS personality layers.
  1135 	Do not use this function directly on a Symbian OS thread.
  1136 	This function should make the thread ready (provided it is not explicitly
  1137 	suspended) and cancel any wait timeout. It should also remove it from any
  1138 	wait queues.
  1139 	If aReturnCode is nonnegative it indicates normal completion of the wait.
  1140 	If aReturnCode is negative it indicates early/abnormal completion of the
  1141 	wait and so any wait object should be reverted as if the wait had never
  1142 	occurred (eg semaphore count should be incremented as this thread has not
  1143 	actually acquired the semaphore).
  1144 	The thread's unknown state handler will be invoked with function ERelease
  1145 	and parameter aReturnCode if the current NState is not recognised.
  1146 	
  1147 	@param aReturnCode	The reason code for release.
  1148 
  1149 	@pre	Kernel must be locked.
  1150 	@pre	Call either in a thread or an IDFC context.
  1151 	
  1152 	@post	Kernel is locked.
  1153  */
  1154 EXPORT_C void NThreadBase::Release(TInt aReturnCode, TUint aMode)
  1155 	{
  1156 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR,"NThreadBase::Release");		
  1157 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T nRel %d",this,aReturnCode));
  1158 	AcqSLock();
  1159 	DoReleaseT(aReturnCode, aMode);
  1160 	RelSLock();
  1161 	}
  1162 
  1163 
  1164 /** Signals a nanokernel thread's request semaphore.
  1165 
  1166 	This can also be used on Symbian OS threads.
  1167 	
  1168 	@pre	Kernel must be locked.
  1169 	@pre	Call either in a thread or an IDFC context.
  1170 	
  1171 	@post	Kernel is locked.
  1172  */
  1173 EXPORT_C void NThreadBase::RequestSignal()
  1174 	{
  1175 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR,"NThreadBase::RequestSignal");		
  1176 	iRequestSemaphore.Signal();
  1177 	}
  1178 
  1179 
  1180 void exit_sync_fn(TAny* aDfc)
  1181 	{
  1182 	((TDfc*)aDfc)->Enque();
  1183 	}
  1184 
  1185 void NThreadBase::Exit()
  1186 	{
  1187 	// The current thread is exiting
  1188 	// Enter with kernel locked, don't return
  1189 	__NK_ASSERT_DEBUG(this==NCurrentThreadL());
  1190 
  1191 	OnExit();
  1192 
  1193 	TInt threadCS = iCsCount;
  1194 	TInt kernCS = SubScheduler().iKernLockCount;
  1195 	iCsCount = 1;
  1196 	AcqSLock();
  1197 	iCsFunction = ECSExitInProgress;
  1198 	NFastMutex* m = NKern::HeldFastMutex();
  1199 	iHeldFastMutex = 0;
  1200 	RelSLock();
  1201 	NKern::Unlock();
  1202 	__KTRACE_OPT(KSCHED,DEBUGPRINT("Exit %T %u",this,NTickCount()));
  1203 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T nExit, CSC %d HeldFM %M KernCS %d",this,threadCS,iHeldFastMutex,kernCS));
  1204 	if (kernCS!=1)
  1205 		FAULT();
  1206 	if (m)
  1207 		FAULT();
  1208 	if (threadCS)
  1209 		FAULT();
  1210 	TDfc* pD = NULL;
  1211 	NThreadExitHandler xh = iHandlers->iExitHandler;
  1212 	if (xh)
  1213 		pD = (*xh)((NThread*)this);		// call exit handler
  1214 
  1215 	// detach any tied events
  1216 	DetachTiedEvents();
  1217 
  1218 	NKern::LeaveGroup();	// detach from group if exit handler didn't do it
  1219 
  1220 	NKern::Lock();
  1221 #ifdef BTRACE_THREAD_IDENTIFICATION
  1222 	BTrace4(BTrace::EThreadIdentification,BTrace::ENanoThreadDestroy,this);
  1223 #endif
  1224 	__NK_ASSERT_ALWAYS(iCsFunction == ECSExitInProgress);
  1225 	iWaitState.SetDead(pD);	// doesn't return
  1226 	FAULT();
  1227 	}
  1228 
  1229 /** Kills a nanokernel thread.
  1230 
  1231 	For use by RTOS personality layers.
  1232 	Do not use this function directly on a Symbian OS thread.
  1233 
  1234 	When acting on the calling thread, causes the calling thread to exit.
  1235 
  1236 	When acting on another thread, causes that thread to exit unless it is
  1237 	currently in a critical section. In this case the thread is marked as
  1238 	"exit pending" and will exit as soon as it leaves the critical section.
  1239 
  1240 	In either case the exiting thread first invokes its exit handler (if it
  1241 	exists). The handler runs with preemption enabled and with the thread in a
  1242 	critical section so that it may not be suspended or killed again. The
  1243 	handler may return a pointer to a TDfc, which will be enqueued just before
  1244 	the thread finally terminates (after the kernel has been relocked). This DFC
  1245 	will therefore execute once the NThread has been safely removed from the
  1246 	scheduler and is intended to be used to cleanup the NThread object and any
  1247 	associated personality layer resources.
  1248 	
  1249 	@pre	Kernel must be locked.
  1250 	@pre	Call in a thread context.
  1251 	@pre	If acting on calling thread, calling thread must not be in a
  1252 			critical section; if it is the kernel will fault. Also, the kernel
  1253 			must be locked exactly once (iKernCSLocked = 1).
  1254 	
  1255 	@post	Kernel is locked, if not acting on calling thread.
  1256 	@post	Does not return if it acts on the calling thread.
  1257  */
  1258 EXPORT_C void NThreadBase::Kill()
  1259 	{
  1260 	// Kill a thread
  1261 	// Enter with kernel locked
  1262 	// Exit with kernel locked if not current thread, otherwise does not return
  1263 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED_ONCE|MASK_NOT_IDFC|MASK_NOT_ISR,"NThreadBase::Kill");
  1264 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T nKill",this));
  1265 	OnKill(); // platform-specific hook
  1266 	NThreadBase* pC = NCurrentThreadL();
  1267 	if (this==pC)
  1268 		{
  1269 		if (iCsFunction==ECSExitInProgress)
  1270 			FAULT();
  1271 		Exit();				// this will not return
  1272 		}
  1273 	SuspendOrKill(-1);
  1274 	}
  1275 
  1276 
  1277 /** Change the CPU affinity of a thread
  1278 
  1279 	@pre	Kernel must be locked.
  1280 	@pre	Call in a thread context.
  1281 
  1282 	@param	The number of the CPU to which this thread should be locked, or
  1283 			KCpuAny if it should be able to run on any CPU.
  1284 	@return The previous affinity mask.
  1285 */
  1286 TUint32 NThreadBase::SetCpuAffinity(TUint32 aAffinity)
  1287 	{
  1288 	// check aAffinity is valid
  1289 	AcqSLock();
  1290 	TUint32 old_aff = iParent->iCpuAffinity;
  1291 	TBool migrate = FALSE;
  1292 	TBool make_ready = FALSE;
  1293 	TSubScheduler* ss0 = &SubScheduler();
  1294 	TSubScheduler* ss = 0;
  1295 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T nSetCpu %08x->%08x, F:%d R:%02x PR:%02x",this,iParent->iCpuAffinity,aAffinity,iParent->iFreezeCpu,iReady,iParent->iReady));
  1296 	if (i_NThread_Initial)
  1297 		goto done;	// can't change affinity of initial thread
  1298 	iParent->iCpuAffinity = aAffinity;		// set new affinity, might not take effect yet
  1299 	if (!iParent->iReady)
  1300 		goto done;	// thread/group not currently on a ready list so can just change affinity
  1301 	migrate = !CheckCpuAgainstAffinity(iParent->iReady & EReadyCpuMask, aAffinity);	// TRUE if thread's current CPU is incompatible with the new affinity
  1302 	if (!migrate)
  1303 		goto done;	// don't need to move thread, so just change affinity
  1304 	ss = TheSubSchedulers + (iParent->iReady & EReadyCpuMask);
  1305 	ss->iReadyListLock.LockOnly();
  1306 	if (iParent->iCurrent)
  1307 		{
  1308 		iParent->iCpuChange = TRUE;			// mark CPU change pending
  1309 		if (ss == ss0)
  1310 			RescheduleNeeded();
  1311 		else
  1312 			// kick other CPU now so migration happens before acquisition of fast mutex
  1313 			send_resched_ipi_and_wait(iParent->iReady & EReadyCpuMask);
  1314 		}
  1315 	else
  1316 		{
  1317 		// Note: Need to know here if any thread in group would return TRUE from CheckFastMutexDefer()
  1318 		// This is handled by the scheduler - when a thread belonging to a group is context switched
  1319 		// out while holding a fast mutex its iFastMutexDefer is set to 1 and the group's iFreezeCpu
  1320 		// is incremented.
  1321 		if (iParent->iFreezeCpu || (iParent==this && CheckFastMutexDefer()))
  1322 			iParent->iCpuChange = TRUE;	// CPU frozen or fast mutex held so just mark deferred CPU migration
  1323 		else
  1324 			{
  1325 			ss->Remove(iParent);
  1326 			iParent->iReady = 0;
  1327 			make_ready = TRUE;
  1328 			}
  1329 		}
  1330 	ss->iReadyListLock.UnlockOnly();
  1331 	if (make_ready)
  1332 		iParent->ReadyT(0);
  1333 done:
  1334 	RelSLock();
  1335 	return old_aff;
  1336 	}
  1337 
  1338 
  1339 /******************************************************************************
  1340  * Thread wait state
  1341  ******************************************************************************/
  1342 #ifndef __NTHREAD_WAITSTATE_MACHINE_CODED__
  1343 void NThreadWaitState::SetUpWait(TUint aType, TUint aFlags, TAny* aWaitObj)
  1344 	{
  1345 	SetUpWait(aType, aFlags, aWaitObj, 0);
  1346 	}
  1347 
  1348 void NThreadWaitState::SetUpWait(TUint aType, TUint aFlags, TAny* aWaitObj, TUint32 aTimeout)
  1349 	{
  1350 	aFlags &= EWtStObstructed;
  1351 	aFlags |= EWtStWaitPending;
  1352 	aType &= 0xff;
  1353 	TUint64 ws64 = (TUint32)aWaitObj;
  1354 	ws64 <<= 32;
  1355 	ws64 |= ((aType<<8)|aFlags);
  1356 	TUint64 oldws64 = __e32_atomic_swp_rlx64(&iWtSt64, ws64);
  1357 	if (I64LOW(oldws64)!=0)
  1358 		__crash();	// ??we were already waiting for something else??
  1359 	iTimer.iTriggerTime = aTimeout;
  1360 	}
  1361 
  1362 void NThreadWaitState::CancelWait()
  1363 	{
  1364 	TUint64 oldws64 = __e32_atomic_swp_rlx64(&iWtSt64, 0);
  1365 	if (oldws64 & (EWtStDead|EWtStWaitActive))
  1366 		__crash();
  1367 	}
  1368 
  1369 TInt NThreadWaitState::DoWait()
  1370 	{
  1371 	TUint64 oldws64 = iWtSt64;
  1372 	TUint64 ws64;
  1373 	TUint32 timeout = iTimer.iTriggerTime;
  1374 	TUint32 set = timeout ? (EWtStWaitActive|EWtStTimeout) : EWtStWaitActive;
  1375 	do	{
  1376 		TUint32 ws32 = I64LOW(oldws64);
  1377 		if (ws32 & EWtStDead)
  1378 			return KErrDied;
  1379 		if (!(ws32 & EWtStWaitPending))
  1380 			return KErrGeneral;
  1381 		ws64 = oldws64;
  1382 		ws64 &= ~TUint64(EWtStWaitPending);
  1383 		ws64 |= TUint64(set);
  1384 		} while(!__e32_atomic_cas_rlx64(&iWtSt64, &oldws64, ws64));
  1385 	if (timeout)
  1386 		{
  1387 		if (iTimer.OneShot(timeout, TRUE)!=KErrNone)
  1388 			__crash();
  1389 		++iTimer.iNTimerSpare1;
  1390 		}
  1391 	return TUint32(oldws64)>>8;
  1392 	}
  1393 
  1394 TInt NThreadWaitState::UnBlockT(TUint aType, TAny* aWaitObj, TInt aReturnValue)
  1395 	{
  1396 	TUint64 exp = TUint32(aWaitObj);
  1397 	exp <<= 32;
  1398 	exp |= (aType<<8);
  1399 	TUint64 oldws64 = iWtSt64;
  1400 	TUint64 ws64;
  1401 	do	{
  1402 		if ((oldws64 ^ exp) < TUint64(EWtStDead))
  1403 			ws64 = TUint64(TUint32(aReturnValue))<<32;
  1404 		else
  1405 			ws64 = oldws64;
  1406 		} while(!__e32_atomic_cas_rel64(&iWtSt64, &oldws64, ws64));
  1407 	if ((oldws64 ^ exp) >= TUint64(EWtStDead))
  1408 		return KErrGeneral;	// not unblocked - no matching wait
  1409 	if (oldws64 & EWtStTimeout)
  1410 		CancelTimerT();
  1411 	if (oldws64 & EWtStWaitActive)
  1412 		{
  1413 		NThreadBase* t = Thread();
  1414 		if (!t->iPauseCount && !t->iSuspended)
  1415 			t->ReadyT(0);
  1416 		}
  1417 	return KErrNone;
  1418 	}
  1419 
  1420 TUint32 NThreadWaitState::ReleaseT(TAny*& aWaitObj, TInt aReturnValue)
  1421 	{
  1422 	TUint64 leave = EWtStDead;
  1423 	TUint64 set = TUint64(TUint32(aReturnValue))<<32;
  1424 	TUint64 ws64 = __e32_atomic_axo_ord64(&iWtSt64, leave, set);
  1425 	aWaitObj = (TAny*)I64HIGH(ws64);
  1426 	TUint32 ws32 = I64LOW(ws64);
  1427 	if (ws32 & EWtStTimeout)
  1428 		CancelTimerT();
  1429 	return ws32;
  1430 	}
  1431 #endif
  1432 
  1433 void NThreadWaitState::SetDead(TDfc* aKillDfc)
  1434 	{
  1435 	TDfc syncDfc(&exit_sync_fn, aKillDfc, TheTimerQ.iDfc.iDfcQ, 0);
  1436 	NThreadBase* t = Thread();
  1437 	t->AcqSLock();
  1438 	iWtC.iWtStFlags = NThreadWaitState::EWtStDead;
  1439 	iWtC.iWtObjType = NThreadBase::EWaitNone;
  1440 	CancelTimerT();
  1441 	if (aKillDfc && iTimer.iNTimerSpare1)
  1442 		{
  1443 		// There is an outstanding timer expiry handler still running
  1444 		// so we must synchronise with DfcThread1.
  1445 		// Add a priority 0 DFC to DfcThread1 so this thread's exit DFC can
  1446 		// only run after the timer expiry handler has completed.
  1447 		aKillDfc = &syncDfc;
  1448 		}
  1449 	iWtC.iKillDfc = aKillDfc;
  1450 	RescheduleNeeded();
  1451 	t->RelSLock();
  1452 	NKern::Unlock();	// this won't return
  1453 	}
  1454 
  1455 void NThreadWaitState::CancelTimerT()
  1456 	{
  1457 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T nCancelTimerT ",Thread()));
  1458 	if (iTimer.Cancel())
  1459 		--iTimer.iNTimerSpare1;
  1460 	else
  1461 		{
  1462 		// Potential race condition - timer must have completed but expiry
  1463 		// handler has not yet run. Signal to the handler that it should do
  1464 		// nothing by flipping the bottom bit of iTimer.iPtr
  1465 		// This condition cannot possibly recur until the expiry handler has
  1466 		// run since all expiry handlers run in DfcThread1.
  1467 		volatile TLinAddr& x = *(volatile TLinAddr*)&iTimer.iPtr;
  1468 		x ^= 1;
  1469 		}
  1470 	}
  1471 
  1472 // Timeout handler, called in DfcThread1
  1473 // NOTE: aPtr is sampled with the timer queue locked, so if Cancel() on the timer fails
  1474 // and iTimer.iPtr is then changed, aPtr here will differ from iTimer.iPtr.
  1475 // This fact is used here to detect expiry of cancelled timers.
  1476 void NThreadWaitState::TimerExpired(TAny* aPtr)
  1477 	{
  1478 	TLinAddr cookie = (TLinAddr)aPtr;
  1479 	NThreadWaitState* pW = (NThreadWaitState*)(cookie &~ 3);
  1480 	NThread* pT = (NThread*)pW->Thread();
  1481 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T nTmExp",pT));
  1482 	NThreadTimeoutHandler th = pT->iHandlers->iTimeoutHandler;
  1483 	pT->LAcqSLock();
  1484 	TUint flags = pW->iWtSt32[0];
  1485 	if (!(flags & EWtStWaitActive) || ((flags>>8)&0xff)!=NThreadBase::EWaitBlocked)
  1486 		th = 0;
  1487 	if (th)
  1488 		{
  1489 		// Use higher level timeout handler
  1490 		pT->RelSLockU();
  1491 		(*th)(pT, NThreadBase::ETimeoutPreamble);
  1492 		TInt param = NThreadBase::ETimeoutPostamble;
  1493 		pT->LAcqSLock();
  1494 		TLinAddr current_cookie = *(volatile TLinAddr*)&pW->iTimer.iPtr;
  1495 		if ((cookie ^ current_cookie) & 1)
  1496 			{
  1497 			// The timer was cancelled just after expiring but before this function
  1498 			// managed to acquire the thread spin lock, so it's spurious
  1499 			param = NThreadBase::ETimeoutSpurious;
  1500 			}
  1501 		pT->RelSLockU();
  1502 		(*th)(pT, param);
  1503 		pT->LAcqSLock();
  1504 		--pW->iTimer.iNTimerSpare1;	// note timer has expired
  1505 		pT->RelSLockU();
  1506 		return;
  1507 		}
  1508 	TLinAddr current_cookie = *(volatile TLinAddr*)&pW->iTimer.iPtr;
  1509 	if ((cookie ^ current_cookie) & 1)
  1510 		// The timer was cancelled just after expiring but before this function
  1511 		// managed to acquire the thread spin lock, so just return without doing anything.
  1512 		goto done;
  1513 	pT->DoReleaseT(KErrTimedOut,0);
  1514 done:
  1515 	pT->RelSLockU();
  1516 	}
  1517 
  1518 
  1519 
  1520 /******************************************************************************
  1521  * NKern:: static functions
  1522  ******************************************************************************/
  1523 
  1524 /** Suspends the execution of a thread.
  1525 
  1526 	This function is intended to be used by the EPOC layer and personality layers.
  1527 	Do not use this function directly on a Symbian OS thread - use Kern::ThreadSuspend().
  1528 
  1529     If the thread is in a critical section or holds a fast mutex, the suspension will
  1530     be deferred until the thread leaves the critical section or signals the fast mutex.
  1531     Otherwise the thread will be suspended with immediate effect. If the thread it's
  1532     running, the execution of the thread will be suspended and a reschedule will occur.
  1533 
  1534     @param aThread Thread to be suspended.
  1535     @param aCount  Number of times to suspend this thread.
  1536     
  1537     @return TRUE, if the thread had changed the state from non-suspended to suspended;
  1538 	        FALSE, otherwise.
  1539 	     
  1540 	@see Kern::ThreadSuspend()
  1541 */
  1542 EXPORT_C TBool NKern::ThreadSuspend(NThread* aThread, TInt aCount)
  1543 	{	
  1544 	NKern::Lock();
  1545 	TBool r=aThread->Suspend(aCount);
  1546 	NKern::Unlock();
  1547 	return r;
  1548 	}
  1549 
  1550 
  1551 /** Resumes the execution of a thread.
  1552 
  1553 	This function is intended to be used by the EPOC layer and personality layers.
  1554 	Do not use this function directly on a Symbian OS thread - use Kern::ThreadResume().
  1555 
  1556     This function resumes the thread once. If the thread was suspended more than once
  1557     the thread will remain suspended.
  1558     If the thread is in a critical section, this function will decrease the number of
  1559     deferred suspensions.
  1560 
  1561     @param aThread Thread to be resumed.
  1562     
  1563     @return TRUE, if the thread had changed the state from suspended to non-suspended;
  1564             FALSE, otherwise.
  1565             
  1566 	@see Kern::ThreadResume()
  1567 */
  1568 EXPORT_C TBool NKern::ThreadResume(NThread* aThread)
  1569 	{	
  1570 	NKern::Lock();
  1571 	TBool r=aThread->Resume();
  1572 	NKern::Unlock();
  1573 	return r;
  1574 	}
  1575 
  1576 
  1577 /** Resumes the execution of a thread and signals a mutex.
  1578 
  1579 	This function is intended to be used by the EPOC layer and personality layers.
  1580 	Do not use this function directly on a Symbian OS thread - use Kern::ThreadResume().
  1581 
  1582     This function resumes the thread once. If the thread was suspended more than once
  1583     the thread will remain suspended.
  1584     If the thread is in a critical section, this function will decrease the number of
  1585     deferred suspensions.
  1586 
  1587     @param aThread Thread to be resumed.
  1588     @param aMutex Mutex to be signalled. If NULL, the scheduler's mutex will be signalled.
  1589 
  1590     @return TRUE, if the thread had changed the state from suspended to non-suspended;
  1591             FALSE, otherwise.
  1592            
  1593 	@see Kern::ThreadResume()
  1594 */
  1595 EXPORT_C TBool NKern::ThreadResume(NThread* aThread, NFastMutex* aMutex)
  1596 	{
  1597 	if (!aMutex)
  1598 		aMutex=&TheScheduler.iLock;
  1599 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T NRsm + FM %M",aThread,aMutex));
  1600 	NKern::Lock();
  1601 	TBool r=aThread->Resume();
  1602 	aMutex->Signal();
  1603 	NKern::Unlock();
  1604 	return r;
  1605 	}
  1606 
  1607 
  1608 /** Forces the execution of a thread to be resumed.
  1609 
  1610 	This function is intended to be used by the EPOC layer and personality layers.
  1611 	Do not use this function directly on a Symbian OS thread - use Kern::ThreadResume().
  1612 
  1613     This function cancels all suspensions on a thread.
  1614 
  1615     @param aThread Thread to be resumed.
  1616     
  1617     @return TRUE, if the thread had changed the state from suspended to non-suspended;
  1618             FALSE, otherwise.
  1619             
  1620 	@see Kern::ThreadResume()
  1621 */
  1622 EXPORT_C TBool NKern::ThreadForceResume(NThread* aThread)
  1623 	{	
  1624 	NKern::Lock();
  1625 	TBool r=aThread->ForceResume();
  1626 	NKern::Unlock();
  1627 	return r;
  1628 	}
  1629 
  1630 
  1631 /** Forces the execution of a thread to be resumed and signals a mutex.
  1632 
  1633 	This function is intended to be used by the EPOC layer and personality layers.
  1634 	Do not use this function directly on a Symbian OS thread - use Kern::ThreadResume().
  1635 
  1636     This function cancels all suspensions on a thread.
  1637 
  1638     @param aThread Thread to be resumed.
  1639     @param aMutex Mutex to be signalled. If NULL, the scheduler's mutex will be signalled.
  1640     
  1641     @return TRUE, if the thread had changed the state from suspended to non-suspended;
  1642             FALSE, otherwise.
  1643             
  1644     @see Kern::ThreadResume()
  1645 */
  1646 EXPORT_C TBool NKern::ThreadForceResume(NThread* aThread, NFastMutex* aMutex)
  1647 	{
  1648 	if (!aMutex)
  1649 		aMutex=&TheScheduler.iLock;
  1650 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T NFRsm + FM %M",aThread,aMutex));
  1651 	NKern::Lock();
  1652 	TBool r=aThread->ForceResume();
  1653 	aMutex->Signal();
  1654 	NKern::Unlock();
  1655 	return r;
  1656 	}
  1657 
  1658 
  1659 /** Awakens a nanothread.
  1660 
  1661 	This function is used to implement synchronisation primitives in the EPOC
  1662 	kernel (e.g. DMutex and DSemaphore) and in personality layers.  It is not
  1663 	intended to be used directly by device drivers.
  1664 
  1665 	If the nanothread is waiting on a fast semaphore, waiting for a DFC, or is
  1666 	blocked in a call to NKern::Block, it is awakened and put back on the ready
  1667 	list.  Otherwise, the thread state is unchanged.  In particular, nothing
  1668 	happens if the nanothread has been explicitly suspended.
  1669 
  1670 	@param aThread Thread to release.
  1671 	@param aReturnValue Value returned by NKern::Block if the thread was blocked.
  1672 
  1673 	@see NKern::Block()
  1674 
  1675 	@pre Interrupts must be enabled.
  1676 	@pre Do not call from an ISR
  1677  */
  1678 EXPORT_C void NKern::ThreadRelease(NThread* aThread, TInt aReturnValue)
  1679 	{
  1680 	CHECK_PRECONDITIONS(MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR,"NKern::ThreadRelease(NThread*, TInt)");
  1681 	NKern::Lock();
  1682 	aThread->Release(aReturnValue,0);
  1683 	NKern::Unlock();
  1684 	}
  1685 
  1686 
  1687 /** Atomically awakens a nanothread and signals a fast mutex.
  1688 
  1689 	This function is used to implement synchronisation primitives in the EPOC
  1690 	kernel (e.g. DMutex and DSemaphore) and in personality layers.  It is not
  1691 	intended to be used directly by device drivers.
  1692 
  1693 	@param aThread Thread to release.
  1694 	@param aReturnValue Value returned by NKern::Block if the thread was blocked.
  1695 	@param aMutex Fast mutex to signal. If NULL, the system lock is signalled.
  1696 
  1697 	@see NKern::ThreadRelease(NThread*, TInt)
  1698 	@see NKern::Block()
  1699 
  1700 	@pre	Call in a thread context.
  1701 	@pre	Interrupts must be enabled.
  1702 	@pre	Kernel must be unlocked.
  1703 	@pre	Specified mutex must be held
  1704  */
  1705 EXPORT_C void NKern::ThreadRelease(NThread* aThread, TInt aReturnValue, NFastMutex* aMutex)
  1706 	{
  1707 	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::ThreadRelease(NThread*,TInt,NFastMutex*)");
  1708 	if (!aMutex)
  1709 		aMutex=&TheScheduler.iLock;
  1710 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T NRel ret %d + FM %M",aThread,aReturnValue,aMutex));
  1711 	NKern::Lock();
  1712 	aThread->Release(aReturnValue,0);
  1713 	aMutex->Signal();
  1714 	NKern::Unlock();
  1715 	}
  1716 
  1717 
  1718 /** Changes the priority of a thread.
  1719 
  1720 	This function is intended to be used by the EPOC layer and personality layers.
  1721 	Do not use this function directly on a Symbian OS thread - use Kern::ThreadSetPriority().
  1722 
  1723     @param aThread Thread to receive the new priority.
  1724     @param aPriority New priority for aThread.
  1725     
  1726 	@see Kern::SetThreadPriority()
  1727 */
  1728 EXPORT_C void NKern::ThreadSetPriority(NThread* aThread, TInt aPriority)
  1729 	{
  1730 	NKern::Lock();
  1731 	aThread->SetPriority(aPriority);
  1732 	NKern::Unlock();
  1733 	}
  1734 
  1735 
  1736 /** Changes the priority of a thread and signals a mutex.
  1737 
  1738 	This function is intended to be used by the EPOC layer and personality layers.
  1739 	Do not use this function directly on a Symbian OS thread - use Kern::ThreadSetPriority().
  1740 
  1741     @param aThread Thread to receive the new priority.
  1742     @param aPriority New priority for aThread.
  1743     @param aMutex Mutex to be signalled. If NULL, the scheduler's mutex will be signalled.
  1744         
  1745 	@see Kern::SetThreadPriority()
  1746 */
  1747 EXPORT_C void NKern::ThreadSetPriority(NThread* aThread, TInt aPriority, NFastMutex* aMutex)
  1748 	{	
  1749 	if (!aMutex)
  1750 		aMutex=&TheScheduler.iLock;
  1751 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T NSPri->%d + FM %M",aThread,aPriority,aMutex));
  1752 	NKern::Lock();
  1753 	aThread->SetPriority(aPriority);
  1754 	aMutex->Signal();
  1755 	NKern::Unlock();
  1756 	}
  1757 
  1758 
  1759 /** Atomically signals the request semaphore of a nanothread and a fast mutex.
  1760 
  1761 	This function is intended to be used by the EPOC layer and personality
  1762 	layers.  Device drivers should use Kern::RequestComplete instead.
  1763 
  1764 	@param aThread Nanothread to signal.  Must be non NULL.
  1765 	@param aMutex Fast mutex to signal.  If NULL, the system lock is signaled.
  1766 
  1767 	@see Kern::RequestComplete()
  1768 
  1769 	@pre	Call in a thread context.
  1770 	@pre	Interrupts must be enabled.
  1771 	@pre	Kernel must be unlocked.
  1772 	@pre	Specified mutex must be held
  1773  */
  1774 EXPORT_C void NKern::ThreadRequestSignal(NThread* aThread, NFastMutex* aMutex)
  1775 	{
  1776 	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::ThreadRequestSignal(NThread*,NFastMutex*)");
  1777 	if (!aMutex)
  1778 		aMutex = &TheScheduler.iLock;
  1779 	NKern::Lock();
  1780 	aThread->iRequestSemaphore.Signal();
  1781 	aMutex->Signal();
  1782 	NKern::Unlock();
  1783 	}
  1784 
  1785 
  1786 /**	Kills a nanothread.
  1787 
  1788 	This function is intended to be used by the EPOC layer and personality layers.
  1789 	Do not use this function directly on a Symbian OS thread - use Kern::ThreadKill().
  1790 
  1791 	This function does not return if the current thread is killed.  
  1792 	This function is asynchronous (i.e. the thread to kill may still be alive when the call returns).
  1793 
  1794 	@param aThread Thread to kill.  Must be non NULL.
  1795 
  1796 	@pre If acting on calling thread, calling thread must not be in a
  1797 			critical section
  1798 	@pre Thread must not already be exiting.
  1799 
  1800 	@see Kern::ThreadKill()
  1801  */
  1802 EXPORT_C void NKern::ThreadKill(NThread* aThread)
  1803 	{
  1804 	NKern::Lock();
  1805 	aThread->Kill();
  1806 	NKern::Unlock();
  1807 	}
  1808 
  1809 
  1810 /**	Atomically kills a nanothread and signals a fast mutex.
  1811 
  1812 	This function is intended to be used by the EPOC layer and personality layers.
  1813 	Do not use this function directly on a Symbian OS thread - use Kern::ThreadKill().
  1814 
  1815 	@param aThread Thread to kill.  Must be non NULL.
  1816 	@param aMutex Fast mutex to signal.  If NULL, the system lock is signalled.
  1817 
  1818 	@pre	If acting on calling thread, calling thread must not be in a
  1819 			critical section
  1820 	@pre Thread must not already be exiting.
  1821 
  1822 	@see NKern::ThreadKill(NThread*)
  1823  */
  1824 EXPORT_C void NKern::ThreadKill(NThread* aThread, NFastMutex* aMutex)
  1825 	{
  1826 	if (!aMutex)
  1827 		aMutex = &TheScheduler.iLock;
  1828 	NThreadBase* pC = NKern::LockC();
  1829 	if (aThread==pC)
  1830 		{
  1831 		__NK_ASSERT_DEBUG(pC->iCsCount==0);	// Make sure thread isn't in critical section
  1832 		__NK_ASSERT_ALWAYS(aMutex->HeldByCurrentThread());
  1833 		pC->AcqSLock();
  1834 		aThread->iCsFunction = NThreadBase::ECSExitPending;
  1835 		pC->RelSLock();
  1836 		aMutex->iHoldingThread = (NThreadBase*)(TLinAddr(aThread) | 1);
  1837 		aMutex->Signal();	// this will make us exit
  1838 		FAULT();			// should never get here
  1839 		}
  1840 	else
  1841 		{
  1842 		aThread->Kill();
  1843 		aMutex->Signal();
  1844 		}
  1845 	NKern::Unlock();
  1846 	}
  1847 
  1848 
  1849 /** Enters thread critical section.
  1850 
  1851 	This function can safely be used in device drivers.
  1852 
  1853     The current thread will enter its critical section. While in critical section
  1854     the thread cannot be suspended or killed. Any suspension or kill will be deferred
  1855     until the thread leaves the critical section.
  1856     Some API explicitly require threads to be in critical section before calling that
  1857     API.
  1858     Only User threads need to call this function as the concept of thread critical
  1859     section applies to User threads only.
  1860 
  1861 	@pre	Call in a thread context.
  1862 	@pre	Kernel must be unlocked.
  1863 */
  1864 EXPORT_C void NKern::ThreadEnterCS()
  1865 	{
  1866 	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::ThreadEnterCS");
  1867 	NThreadBase* pC = NKern::CurrentThread();
  1868 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T NEntCS",pC));
  1869 	__NK_ASSERT_DEBUG(pC->iCsCount>=0);
  1870 	++pC->iCsCount;
  1871 	}
  1872 
  1873 NThread* NKern::_ThreadEnterCS()
  1874 	{
  1875 	NThreadBase* pC = NKern::CurrentThread();
  1876 	__NK_ASSERT_DEBUG(pC->iCsCount>=0);
  1877 	++pC->iCsCount;
  1878 	return (NThread*)pC;
  1879 	}
  1880 
  1881 
  1882 /** Leaves thread critical section.
  1883 
  1884 	This function can safely be used in device drivers.
  1885 
  1886     The current thread will leave its critical section. If the thread was suspended/killed
  1887     while in critical section, the thread will be suspended/killed after leaving the
  1888     critical section by calling this function.
  1889     Only User threads need to call this function as the concept of thread critical
  1890     section applies to User threads only.
  1891 
  1892 	@pre	Call in a thread context.
  1893 	@pre	Kernel must be unlocked.
  1894 */
  1895 EXPORT_C void NKern::ThreadLeaveCS()
  1896 	{
  1897 	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::ThreadLeaveCS");
  1898 	NThreadBase* pC = NKern::LockC();
  1899 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T NLvCS",pC));
  1900 	pC->AcqSLock();
  1901 	__NK_ASSERT_DEBUG(pC->iCsCount>0);
  1902 	if (--pC->iCsCount==0 && pC->iCsFunction!=0)
  1903 		{
  1904 		NFastMutex* m = HeldFastMutex();
  1905 		if (m)
  1906 			m->iHoldingThread = (NThreadBase*)(TLinAddr(pC) | 1);
  1907 		else
  1908 			pC->DoCsFunctionT();
  1909 		}
  1910 	pC->RelSLock();
  1911 	NKern::Unlock();
  1912 	}
  1913 
  1914 void NKern::_ThreadLeaveCS()
  1915 	{
  1916 	NThreadBase* pC = NKern::LockC();
  1917 	pC->AcqSLock();
  1918 	__NK_ASSERT_DEBUG(pC->iCsCount>0);
  1919 	if (--pC->iCsCount==0 && pC->iCsFunction!=0)
  1920 		{
  1921 		NFastMutex* m = HeldFastMutex();
  1922 		if (m)
  1923 			m->iHoldingThread = (NThreadBase*)(TLinAddr(pC) | 1);
  1924 		else
  1925 			pC->DoCsFunctionT();
  1926 		}
  1927 	pC->RelSLock();
  1928 	NKern::Unlock();
  1929 	}
  1930 
  1931 /** Freeze the CPU of the current thread
  1932 
  1933 	After this the current thread will not migrate to another processor
  1934 
  1935 	@return	A cookie to be passed to NKern::EndFreezeCpu() to allow nesting
  1936 */
  1937 EXPORT_C TInt NKern::FreezeCpu()
  1938 	{
  1939 	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::FreezeCpu");
  1940 	NKern::Lock();
  1941 	TSubScheduler& ss = SubScheduler();
  1942 	NThreadBase* pC = ss.iCurrentThread;
  1943 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T NFrzCpu",pC));
  1944 	if (pC->iFreezeCpu)
  1945 		{
  1946 		NKern::Unlock();
  1947 		return 1;
  1948 		}
  1949 	pC->iFreezeCpu = 1;
  1950 	if (pC->iParent != pC)
  1951 		{
  1952 		pC->AcqSLock();
  1953 		++pC->iParent->iFreezeCpu;
  1954 		pC->RelSLock();
  1955 		}
  1956 	NKern::Unlock();
  1957 	return 0;
  1958 	}
  1959 
  1960 
  1961 /** Unfreeze the current thread's CPU
  1962 
  1963 	After this the current thread will again be eligible to migrate to another processor
  1964 
  1965 	@param	aCookie the value returned by NKern::FreezeCpu()
  1966 */
  1967 EXPORT_C void NKern::EndFreezeCpu(TInt aCookie)
  1968 	{
  1969 	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::EndFreezeCpu");
  1970 	__KTRACE_OPT(KNKERN,DEBUGPRINT("%T NEndFrz %d",NKern::CurrentThread(),aCookie));
  1971 	if (aCookie)
  1972 		return;
  1973 	NKern::Lock();
  1974 	TSubScheduler& ss = SubScheduler();
  1975 	NThreadBase* pC = ss.iCurrentThread;
  1976 	if (pC->iFreezeCpu)
  1977 		{
  1978 		pC->iFreezeCpu = 0;
  1979 		mb();
  1980 		if (pC->iParent != pC)
  1981 			{
  1982 			pC->AcqSLock();
  1983 			if (!--pC->iParent->iFreezeCpu && pC->iParent->iCpuChange)
  1984 				RescheduleNeeded();
  1985 			pC->RelSLock();
  1986 			}
  1987 		else if (pC->iCpuChange)		// deferred CPU change?
  1988 			RescheduleNeeded();
  1989 		}
  1990 	NKern::Unlock();
  1991 	}
  1992 
  1993 
  1994 /** Change the CPU affinity of a thread
  1995 
  1996 	@pre	Call in a thread context.
  1997 
  1998 	@param	The new CPU affinity mask
  1999 	@return The old affinity mask
  2000  */
  2001 EXPORT_C TUint32 NKern::ThreadSetCpuAffinity(NThread* aThread, TUint32 aAffinity)
  2002 	{
  2003 	NKern::Lock();
  2004 	TUint32 r = aThread->SetCpuAffinity(aAffinity);
  2005 	NKern::Unlock();
  2006 	return r;
  2007 	}
  2008 
  2009 
  2010 /** Modify a thread's timeslice
  2011 
  2012 	@pre	Call in a thread context.
  2013 
  2014 	@param	aTimeslice	The new timeslice value
  2015  */
  2016 EXPORT_C void NKern::ThreadSetTimeslice(NThread* aThread, TInt aTimeslice)
  2017 	{
  2018 	NKern::Lock();
  2019 	aThread->AcqSLock();
  2020 	if (aThread->iTimeslice == aThread->iTime || aTimeslice<0)
  2021 		aThread->iTime = aTimeslice;
  2022 	aThread->iTimeslice = aTimeslice;
  2023 	aThread->RelSLock();
  2024 	NKern::Unlock();
  2025 	}
  2026 
  2027 
  2028 /** Blocks current nanothread.
  2029 
  2030 	This function is used to implement synchronisation primitives in the EPOC
  2031 	layer and in personality layers.  It is not intended to be used directly by
  2032 	device drivers.  
  2033 
  2034 	@param aTimeout If greater than 0, the nanothread will be blocked for at most
  2035 					aTimeout microseconds.
  2036 	@param aMode	Bitmask whose possible values are documented in TBlockMode.  
  2037 	@param aMutex	Fast mutex to operate on.  If NULL, the system lock is used.
  2038 
  2039 	@see NKern::ThreadRelease()
  2040 	@see TBlockMode
  2041 
  2042 	@pre	Call in a thread context.
  2043 	@pre	Interrupts must be enabled.
  2044 	@pre	Kernel must be unlocked.
  2045 	@pre	Specified mutex must be held
  2046  */
  2047 EXPORT_C TInt NKern::Block(TUint32 aTimeout, TUint aMode, NFastMutex* aMutex)
  2048 	{
  2049 	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::Block(TUint32,TUint,NFastMutex*)");
  2050 	if (!aMutex)
  2051 		aMutex = &TheScheduler.iLock;
  2052 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NKern::Block time %d mode %d FM %M",aTimeout,aMode,aMutex));
  2053 	if (aMode & EEnterCS)
  2054 		NKern::_ThreadEnterCS();	// NOTE: MUST DO THIS BEFORE CALLING NKern::Lock()
  2055 	NThreadBase* pC = NKern::LockC();
  2056 	TUint flags = (aMode & NKern::EObstruct) ? NThreadWaitState::EWtStObstructed : 0;
  2057 	pC->iWaitState.SetUpWait(NThreadBase::EWaitBlocked, flags, 0, aTimeout);
  2058 	if (aMode & ERelease)
  2059 		aMutex->Signal();
  2060 	RescheduleNeeded();
  2061 	NKern::Unlock();	// thread blocks here
  2062 	TInt r = pC->iWaitState.iWtC.iRetVal;	// sample here since it will be overwritten if we block on the fast mutex
  2063 	if (aMode & EClaim)
  2064 		FMWait(aMutex);
  2065 	return r;
  2066 	}
  2067 
  2068 
  2069 /**
  2070 @pre	Call in a thread context.
  2071 @pre	Interrupts must be enabled.
  2072 @pre	Kernel must be unlocked.
  2073 @pre	No fast mutex can be held
  2074 */
  2075 /** @see NKern::Block(TUint32, TUint, NFastMutex*) */
  2076 EXPORT_C TInt NKern::Block(TUint32 aTimeout, TUint aMode)
  2077 	{
  2078 	CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"NKern::Block(TUint32,TUint)");
  2079 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NKern::Block time %d mode %d",aTimeout,aMode));
  2080 	if (aMode & EEnterCS)
  2081 		NKern::_ThreadEnterCS();	// NOTE: MUST DO THIS BEFORE CALLING NKern::Lock()
  2082 	NThreadBase* pC = NKern::LockC();
  2083 	TUint flags = (aMode & NKern::EObstruct) ? NThreadWaitState::EWtStObstructed : 0;
  2084 	pC->iWaitState.SetUpWait(NThreadBase::EWaitBlocked, flags, 0, aTimeout);
  2085 	RescheduleNeeded();
  2086 	NKern::Unlock();	// thread blocks here
  2087 	return pC->iWaitState.iWtC.iRetVal;
  2088 	}
  2089 
  2090 
  2091 
  2092 
  2093 /**
  2094 Places the current nanothread into a wait state on an externally
  2095 defined wait object.
  2096 	
  2097 For use by RTOS personality layers.
  2098 Do not use this function directly on a Symbian OS thread.
  2099 
  2100 Since the kernel is locked on entry, any reschedule will be deferred until
  2101 it is unlocked. The thread should be added to any necessary wait queue after
  2102 a call to this function, since this function removes it from the ready list.
  2103 The thread's wait timer is started if aTimeout is nonzero.
  2104 The thread's NState and wait object are updated.
  2105 
  2106 Call NThreadBase::Release() when the wait condition is resolved.
  2107 
  2108 @param aTimeout The maximum time for which the thread should block, in nanokernel timer ticks.
  2109                 A zero value means wait forever.
  2110                 If the thread is still blocked when the timeout expires,
  2111                 then the timeout state handler will be called.
  2112 @param aState   The nanokernel thread state (N-State) value to be set.
  2113                 This state corresponds to the externally defined wait object.
  2114                 This value will be written into the member NThreadBase::iNState.
  2115 @param aWaitObj A pointer to an externally defined wait object.
  2116                 This value will be written into the member NThreadBase::iWaitObj.
  2117 
  2118 @pre	Kernel must be locked.
  2119 @pre	Call in a thread context.
  2120 
  2121 @post	Kernel is locked.
  2122 
  2123 @see	NThreadBase::Release()
  2124 */
  2125 EXPORT_C void NKern::NanoBlock(TUint32 aTimeout, TUint aState, TAny* aWaitObj)
  2126 	{
  2127 	CHECK_PRECONDITIONS(MASK_KERNEL_LOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::NanoBlock");		
  2128 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NanoBlock time %d state %d obj %08x", aTimeout, aState, aWaitObj));
  2129 	NThreadBase* pC = NCurrentThreadL();
  2130 	pC->iWaitState.SetUpWait(aState, aState>>8, aWaitObj, aTimeout);
  2131 	RescheduleNeeded();
  2132 	}
  2133 
  2134 
  2135 
  2136 
  2137 EXPORT_C void NKern::Sleep(TUint32 aTime)
  2138 /**
  2139 Puts the current nanothread to sleep for the specified duration.
  2140 
  2141 It can be called from Symbian OS threads.
  2142 
  2143 @param	aTime sleep time in nanokernel timer ticks.
  2144 
  2145 @pre    No fast mutex can be held.
  2146 @pre    Kernel must be unlocked.
  2147 @pre	Call in a thread context.
  2148 @pre	Interrupts must be enabled.
  2149 */
  2150 	{
  2151 	CHECK_PRECONDITIONS(MASK_THREAD_STANDARD,"NKern::Sleep");
  2152 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NSlp %d",aTime));
  2153 	NThreadBase* pC = NKern::LockC();
  2154 	pC->iWaitState.SetUpWait(NThreadBase::EWaitSleep, 0, 0, aTime);
  2155 	RescheduleNeeded();
  2156 	NKern::Unlock();
  2157 	}
  2158 
  2159 
  2160 /**	Terminates the current nanothread.
  2161 
  2162 	Calls to this function never return.
  2163 
  2164 	For use by RTOS personality layers.
  2165 	Do not use this function directly on a Symbian OS thread.
  2166 
  2167 	@pre	Call in a thread context.
  2168 	@pre	Interrupts must be enabled.
  2169 	@pre	Kernel must be unlocked.	
  2170  */
  2171 EXPORT_C void NKern::Exit()
  2172 	{
  2173 	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::Exit");
  2174 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NExit"));
  2175 	NKern::LockC()->Exit();		// this won't return
  2176 	FAULT();
  2177 	}
  2178 
  2179 
  2180 /**	Terminates the current nanothread at the next possible point.
  2181 
  2182 	If the calling thread is not currently in a critical section and does not
  2183 	currently hold a fast mutex, it exits immediately and this function does
  2184 	not return. On the other hand if the thread is in a critical section or
  2185 	holds a fast mutex the thread continues executing but it will exit as soon
  2186 	as it leaves the critical section and/or releases the fast mutex.
  2187 
  2188 	@pre	Call in a thread context.
  2189 	@pre	Interrupts must be enabled.
  2190 	@pre	Kernel must be unlocked.	
  2191  */
  2192 EXPORT_C void NKern::DeferredExit()
  2193 	{
  2194 	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::DeferredExit");
  2195 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NDefExit"));
  2196 	NFastMutex* m = HeldFastMutex();
  2197 	NThreadBase* pC = NKern::LockC();
  2198 	if (!m && !pC->iCsCount)
  2199 		pC->Exit();			// this won't return
  2200 	pC->AcqSLock();
  2201 	if (pC->iCsFunction >= 0)	// don't touch it if we are already exiting
  2202 		pC->iCsFunction = NThreadBase::ECSExitPending;
  2203 	pC->RelSLock();
  2204 	if (m && !pC->iCsCount)
  2205 		m->iHoldingThread = (NThreadBase*)(TLinAddr(pC) | 1);
  2206 	NKern::Unlock();
  2207 	}
  2208 
  2209 
  2210 /** Prematurely terminates the current thread's timeslice
  2211 
  2212 	@pre	Kernel must be unlocked.
  2213 	@pre	Call in a thread context.
  2214 	
  2215 	@post	Kernel is unlocked.
  2216  */
  2217 EXPORT_C void NKern::YieldTimeslice()
  2218 	{
  2219 	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::YieldTimeslice");
  2220 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NKern::YieldTimeslice"));
  2221 	NThreadBase* t = NKern::LockC();
  2222 	t->iTime = 0;
  2223 	mb();
  2224 	if (t->iNext!=t || t->iParent->iNext!=t->iParent)
  2225 		RescheduleNeeded();
  2226 	NKern::Unlock();
  2227 	}
  2228 
  2229 
  2230 /** Returns the number of CPUs available to Symbian OS
  2231 
  2232 	@return the number of CPUs
  2233 	
  2234 	@pre Call in any context.
  2235 */
  2236 EXPORT_C TInt NKern::NumberOfCpus()
  2237 	{
  2238 	return TheScheduler.iNumCpus;
  2239 	}
  2240 
  2241 
  2242 /** Rotates the specified CPU ready list for threads at the specified priority.
  2243 	
  2244 	For use by RTOS personality layers to allow external control of round-robin
  2245 	scheduling. Not intended for direct use by device drivers.
  2246 
  2247 	@param	aPriority = priority at which threads should be rotated.
  2248 						-1 means use calling thread's priority.
  2249 	@param	aCpu		CPU to act on
  2250 	
  2251 	@pre	Kernel must be unlocked.
  2252 	@pre	Call in a thread context.
  2253 	
  2254 	@post	Kernel is unlocked.
  2255  */
  2256 
  2257 EXPORT_C void NKern::RotateReadyList(TInt aPriority, TInt aCpu)
  2258 	{
  2259 //	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::RotateReadyList");
  2260 //	__KTRACE_OPT(KNKERN,DEBUGPRINT("NKern::RotateReadyList %d",aPriority));
  2261 //	if (aPriority<0 || aPriority>=KNumPriorities)
  2262 //		aPriority=NKern::CurrentThread()->iPriority;
  2263 //	NKern::Lock();
  2264 //	TheScheduler.RotateReadyList(aPriority);
  2265 //	NKern::Unlock();
  2266 	}
  2267 
  2268 
  2269 /** Rotates the ready list for threads at the specified priority.
  2270 	
  2271 	For use by RTOS personality layers to allow external control of round-robin
  2272 	scheduling. Not intended for direct use by device drivers.
  2273 
  2274 	@param	aPriority = priority at which threads should be rotated.
  2275 						-1 means use calling thread's priority.
  2276 	
  2277 	@pre	Kernel must be unlocked.
  2278 	@pre	Call in a thread context.
  2279 	
  2280 	@post	Kernel is unlocked.
  2281  */
  2282 EXPORT_C void NKern::RotateReadyList(TInt aPriority)
  2283 	{
  2284 	RotateReadyList(aPriority, -1);
  2285 	}
  2286 
  2287 
  2288 /** Returns a pointer to the thread group to which the current thread belongs,
  2289 	if any.	Returns NULL if current thread is a standalone thread.
  2290 	
  2291 	@pre	Call in a thread context.
  2292  */
  2293 EXPORT_C NThreadGroup* NKern::CurrentGroup()
  2294 	{
  2295 	NThreadBase* pC = NKern::CurrentThread();
  2296 	return (pC->iParent == pC) ? (NThreadGroup*)0 : (NThreadGroup*)pC->iParent;
  2297 	}
  2298 
  2299 
  2300 /** Detaches the current thread from the group to which it currently belongs,
  2301 	if any.	Returns a pointer to the group (NULL if none).
  2302 		
  2303 	@pre	Call in a thread context.
  2304 	@pre	Interrupts enabled
  2305 	@pre	Kernel unlocked
  2306  */
  2307 EXPORT_C NThreadGroup* NKern::LeaveGroup()
  2308 	{
  2309 	CHECK_PRECONDITIONS(MASK_NOT_ISR|MASK_NOT_IDFC|MASK_INTERRUPTS_ENABLED|MASK_KERNEL_UNLOCKED, "NKern::LeaveGroup");
  2310 	NKern::Lock();
  2311 	TSubScheduler& ss = SubScheduler();
  2312 	NThreadBase* pC = ss.iCurrentThread;
  2313 	pC->iNewParent = 0;	// cancel any pending Join
  2314 	NThreadGroup* g = (pC->iParent == pC) ? (NThreadGroup*)0 : (NThreadGroup*)pC->iParent;
  2315 	TBool make_group_ready = FALSE;
  2316 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NLeaveGroup %T (%G)",pC,g));
  2317 	if (g)
  2318 		{
  2319 		while (!pC->TiedEventLeaveInterlock())
  2320 			{
  2321 			TInt irq = NKern::DisableAllInterrupts();
  2322 			ss.QueueDfcs();
  2323 			NKern::RestoreInterrupts(irq);
  2324 			}
  2325 		pC->AcqSLock();
  2326 		ss.iReadyListLock.LockOnly();
  2327 		pC->UnReadyT();
  2328 		pC->iParent = pC;
  2329 		g->iCurrent = 0;	// since current thread is no longer in g
  2330 		ss.AddHead(pC);
  2331 		pC->iReady = TUint8(ss.iCpuNum | NSchedulable::EReadyOffset);
  2332 		pC->iCpuAffinity = g->iCpuAffinity;	// keep same CPU affinity
  2333 		// if we're frozen, the group's freeze count was incremented
  2334 		if (pC->iFreezeCpu)
  2335 			--g->iFreezeCpu;
  2336 		// if we've been marked as deferring, the group's freeze count was incremented
  2337 		if (pC->iFastMutexDefer == 1)
  2338 			{
  2339 			--g->iFreezeCpu;
  2340 			pC->iFastMutexDefer = 0;
  2341 			}
  2342 		// if the group was waiting to change cpu then this thread needs to change still
  2343 		if (g->iCpuChange)
  2344 			{
  2345 			pC->iCpuChange = g->iCpuChange;
  2346 			RescheduleNeeded();
  2347 			if (!g->iFreezeCpu)
  2348 				{
  2349 				// we were the last thread in the group stopping it from moving
  2350 				// but there may be no other threads left after UnReadyT'ing this one
  2351 				g->iCpuChange = FALSE;
  2352 				if (g->iReady)
  2353 					{
  2354 					ss.Remove(g);
  2355 					g->iReady = 0;
  2356 					make_group_ready = TRUE;
  2357 					}
  2358 				}
  2359 			}
  2360 		ss.iReadyListLock.UnlockOnly();
  2361 		--g->iThreadCount;
  2362 		if (make_group_ready)
  2363 			g->ReadyT(0);
  2364 		g->RelSLock();		// since pC is no longer attached to g
  2365 		pC->RelSLock();
  2366 		}
  2367 	NKern::Unlock();
  2368 	return g;
  2369 	}
  2370 
  2371 
  2372 /** Adds the current thread to the specified group.
  2373 	
  2374 	@param	aGroup = pointer to group to join
  2375 	
  2376 	@pre	Call in a thread context, not in one of the idle threads.
  2377 	@pre	Interrupts enabled
  2378 	@pre	Kernel unlocked
  2379 	@pre	Thread does not hold a fast mutex
  2380 	@pre	Thread does not have a freeze on CPU migration
  2381 	@pre	Current thread is not already in a group
  2382  */
  2383 EXPORT_C void NKern::JoinGroup(NThreadGroup* aGroup)
  2384 	{
  2385 	CHECK_PRECONDITIONS(MASK_THREAD_STANDARD, "NKern::JoinGroup");
  2386 	NKern::Lock();
  2387 	TSubScheduler& ss = SubScheduler();
  2388 	NThreadBase* pC = ss.iCurrentThread;
  2389 	__ASSERT_WITH_MESSAGE_DEBUG(pC->iParent==pC, "Thread not already in a group", "NKern::JoinGroup");
  2390 	__ASSERT_WITH_MESSAGE_DEBUG(!pC->iFreezeCpu, "No interdiction on CPU migration", "NKern::JoinGroup");
  2391 	__ASSERT_WITH_MESSAGE_DEBUG(!pC->i_NThread_Initial, "Not idle thread", "NKern::JoinGroup");
  2392 	__NK_ASSERT_ALWAYS(pC->iParent==pC && !pC->iFreezeCpu);
  2393 	__KTRACE_OPT(KNKERN,DEBUGPRINT("NJoinGroup %T->%G",pC,aGroup));
  2394 	pC->AcqSLock();
  2395 	aGroup->AcqSLock();
  2396 	TBool migrate = !CheckCpuAgainstAffinity(ss.iCpuNum, aGroup->iCpuAffinity);	// TRUE if thread's current CPU is incompatible with the group's affinity
  2397 	if (!aGroup->iReady || aGroup->iReady==pC->iReady)
  2398 		{
  2399 		// group not ready or ready on this CPU
  2400 		if (!migrate)
  2401 			{
  2402 			ss.iReadyListLock.LockOnly();
  2403 			pC->UnReadyT();
  2404 			pC->iParent = aGroup;
  2405 			aGroup->iNThreadList.AddHead(pC);
  2406 			if (!aGroup->iReady)
  2407 				{
  2408 				aGroup->iPriority = pC->iPriority;
  2409 				ss.AddHead(aGroup);
  2410 				aGroup->iReady = TUint8(ss.iCpuNum | NSchedulable::EReadyOffset);
  2411 				}
  2412 			else if (pC->iPriority > aGroup->iPriority)
  2413 				{
  2414 				ss.ChangePriority(aGroup, pC->iPriority);
  2415 				}
  2416 			pC->iReady = NSchedulable::EReadyGroup;
  2417 			aGroup->iCurrent = aGroup->iReady;
  2418 			ss.iReadyListLock.UnlockOnly();
  2419 			++aGroup->iThreadCount;
  2420 			goto done;
  2421 			}
  2422 		}
  2423 	// this thread needs to migrate to another CPU
  2424 	pC->iNewParent = aGroup;
  2425 	RescheduleNeeded();
  2426 
  2427 	// the following reschedule definitely joins the group even if the
  2428 	// thread's CPU affinity is incompatible with that of the group
  2429 	// (the thread's CPU affinity is subsequently determined by that of
  2430 	// the group)
  2431 
  2432 done:
  2433 	if (pC->iParent != aGroup)
  2434 		aGroup->RelSLock();
  2435 	pC->RelSLock();
  2436 	while (!pC->TiedEventJoinInterlock())
  2437 		{
  2438 		TInt irq = NKern::DisableAllInterrupts();
  2439 		ss.QueueDfcs();
  2440 		NKern::RestoreInterrupts(irq);
  2441 		}
  2442 	NKern::Unlock();
  2443 	}
  2444 
  2445 
  2446 /******************************************************************************
  2447  * Priority Lists
  2448  ******************************************************************************/
  2449 
  2450 #ifndef __PRI_LIST_MACHINE_CODED__
  2451 /** Returns the priority of the highest priority item present on a priority list.
  2452 
  2453 	@return	The highest priority present or -1 if the list is empty.
  2454  */
  2455 EXPORT_C TInt TPriListBase::HighestPriority()
  2456 	{
  2457 //	TUint64 present = MAKE_TUINT64(iPresent[1], iPresent[0]);
  2458 //	return __e32_find_ms1_64(present);
  2459 	return __e32_find_ms1_64(iPresent64);
  2460 	}
  2461 
  2462 
  2463 /** Finds the highest priority item present on a priority list.
  2464 
  2465 	If multiple items at the same priority are present, return the first to be
  2466 	added in chronological order.
  2467 
  2468 	@return	A pointer to the item or NULL if the list is empty.
  2469  */
  2470 EXPORT_C TPriListLink* TPriListBase::First()
  2471 	{
  2472 	TInt p = HighestPriority();
  2473 	return p >=0 ? static_cast<TPriListLink*>(iQueue[p]) : NULL;
  2474 	}
  2475 
  2476 
  2477 /** Adds an item to a priority list at the tail of the queue for its priority.
  2478 
  2479 	@param aLink A pointer to the item - must not be NULL.
  2480  */
  2481 EXPORT_C void TPriListBase::Add(TPriListLink* aLink)
  2482 	{
  2483 	TInt p = aLink->iPriority;
  2484 	SDblQueLink* head = iQueue[p];
  2485 	if (head)
  2486 		{
  2487 		// already some at this priority
  2488 		aLink->InsertBefore(head);
  2489 		}
  2490 	else
  2491 		{
  2492 		// 'create' new list
  2493 		iQueue[p] = aLink;
  2494 		aLink->iNext = aLink->iPrev = aLink;
  2495 		iPresent[p>>5] |= 1u << (p & 0x1f);
  2496 		}
  2497 	}
  2498 
  2499 
  2500 /** Removes an item from a priority list.
  2501 
  2502 	@param aLink A pointer to the item - must not be NULL.
  2503  */
  2504 EXPORT_C void TPriListBase::Remove(TPriListLink* aLink)
  2505 	{
  2506 	if (!aLink->Alone())
  2507 		{
  2508 		// not the last on this list
  2509 		TInt p = aLink->iPriority;
  2510 		if (iQueue[p] == aLink)
  2511 			iQueue[p] = aLink->iNext;
  2512 		aLink->Deque();
  2513 		}
  2514 	else
  2515 		{
  2516 		TInt p = aLink->iPriority;
  2517 		iQueue[p] = 0;
  2518 		iPresent[p>>5] &= ~(1u << (p & 0x1f));
  2519 		KILL_LINK(aLink);
  2520 		}
  2521 	}
  2522 
  2523 
  2524 /** Changes the priority of an item on a priority list.
  2525 
  2526 	@param	aLink A pointer to the item to act on - must not be NULL.
  2527 	@param	aNewPriority A new priority for the item.
  2528  */
  2529 EXPORT_C void TPriListBase::ChangePriority(TPriListLink* aLink, TInt aNewPriority)
  2530 	{
  2531 	if (aLink->iPriority!=aNewPriority)
  2532 		{
  2533 		Remove(aLink);
  2534 		aLink->iPriority=TUint8(aNewPriority);
  2535 		Add(aLink);
  2536 		}
  2537 	}
  2538 #endif
  2539 
  2540 /** Adds an item to a priority list at the head of the queue for its priority.
  2541 
  2542 	@param aLink A pointer to the item - must not be NULL.
  2543  */
  2544 EXPORT_C void TPriListBase::AddHead(TPriListLink* aLink)
  2545 	{
  2546 	TInt p = aLink->iPriority;
  2547 	SDblQueLink* head = iQueue[p];
  2548 	iQueue[p] = aLink;
  2549 	if (head)
  2550 		{
  2551 		// already some at this priority
  2552 		aLink->InsertBefore(head);
  2553 		}
  2554 	else
  2555 		{
  2556 		// 'create' new list
  2557 		aLink->iNext = aLink->iPrev = aLink;
  2558 		iPresent[p>>5] |= 1u << (p & 0x1f);
  2559 		}
  2560 	}
  2561 
  2562 
  2563 /******************************************************************************
  2564  * Generic IPIs
  2565  ******************************************************************************/
  2566 
  2567 TGenIPIList::TGenIPIList()
  2568 	:	iGenIPILock(TSpinLock::EOrderGenericIPIList)
  2569 	{
  2570 	}
  2571 
  2572 TGenIPIList GenIPIList;
  2573 
  2574 extern "C" {
  2575 extern void send_generic_ipis(TUint32);
  2576 
  2577 void generic_ipi_isr(TSubScheduler* aS)
  2578 	{
  2579 	TGenericIPI* ipi = aS->iNextIPI;
  2580 	if (!ipi)
  2581 		return;
  2582 	TUint32 m = aS->iCpuMask;
  2583 	SDblQueLink* anchor = &GenIPIList.iA;
  2584 	while (ipi != anchor)
  2585 		{
  2586 		__e32_atomic_and_acq32(&ipi->iCpusIn, ~m);
  2587 		(*ipi->iFunc)(ipi);
  2588 		TInt irq = GenIPIList.iGenIPILock.LockIrqSave();
  2589 		TGenericIPI* n = (TGenericIPI*)ipi->iNext;
  2590 		ipi->iCpusOut &= ~m;
  2591 		if (ipi->iCpusOut == 0)
  2592 			{
  2593 			ipi->Deque();
  2594 			mb();
  2595 			ipi->iNext = 0;
  2596 			}
  2597 		ipi = n;
  2598 		while (ipi!=anchor && !(ipi->iCpusIn & m))
  2599 			ipi = (TGenericIPI*)ipi->iNext;
  2600 		if (ipi == anchor)
  2601 			aS->iNextIPI = 0;
  2602 		GenIPIList.iGenIPILock.UnlockIrqRestore(irq);
  2603 		}
  2604 	}
  2605 }
  2606 
  2607 void TGenericIPI::Queue(TGenericIPIFn aFunc, TUint32 aCpuMask)
  2608 	{
  2609 	__KTRACE_OPT(KSCHED2,DEBUGPRINT("GenIPI F=%08x M=%08x", aFunc, aCpuMask));
  2610 	iFunc = aFunc;
  2611 	TScheduler& s = TheScheduler;
  2612 	TInt i;
  2613 	TUint32 ipis = 0;
  2614 	TInt irq = GenIPIList.iGenIPILock.LockIrqSave();
  2615 	if (aCpuMask & 0x80000000u)
  2616 		{
  2617 		if (aCpuMask==0xffffffffu)
  2618 			aCpuMask = s.iActiveCpus2;
  2619 		else if (aCpuMask==0xfffffffeu)
  2620 			aCpuMask = s.iActiveCpus2 &~ SubScheduler().iCpuMask;
  2621 		else
  2622 			aCpuMask = 0;
  2623 		}
  2624 	iCpusIn = aCpuMask;
  2625 	iCpusOut = aCpuMask;
  2626 	if (!aCpuMask)
  2627 		{
  2628 		GenIPIList.iGenIPILock.UnlockIrqRestore(irq);
  2629 		iNext = 0;
  2630 		return;
  2631 		}
  2632 	GenIPIList.Add(this);
  2633 	for (i=0; i<s.iNumCpus; ++i)
  2634 		{
  2635 		if (!(aCpuMask & (1<<i)))
  2636 			continue;
  2637 		TSubScheduler& ss = *s.iSub[i];
  2638 		if (!ss.iNextIPI)
  2639 			{
  2640 			ss.iNextIPI = this;
  2641 			ipis |= (1<<i);
  2642 			}
  2643 		}
  2644 	send_generic_ipis(ipis);
  2645 	GenIPIList.iGenIPILock.UnlockIrqRestore(irq);
  2646 	__KTRACE_OPT(KSCHED2,DEBUGPRINT("GenIPI ipis=%08x", ipis));
  2647 	}
  2648 
  2649 void TGenericIPI::QueueAll(TGenericIPIFn aFunc)
  2650 	{
  2651 	Queue(aFunc, 0xffffffffu);
  2652 	}
  2653 
  2654 void TGenericIPI::QueueAllOther(TGenericIPIFn aFunc)
  2655 	{
  2656 	Queue(aFunc, 0xfffffffeu);
  2657 	}
  2658 
  2659 // Call from thread or IDFC with interrupts enabled
  2660 void TGenericIPI::WaitEntry()
  2661 	{
  2662 	CHECK_PRECONDITIONS(MASK_NOT_ISR|MASK_INTERRUPTS_ENABLED,"TGenericIPI::WaitEntry");
  2663 	while (iCpusIn)
  2664 		{
  2665 		__chill();
  2666 		}
  2667 	mb();
  2668 	}
  2669 
  2670 // Call from thread or IDFC with interrupts enabled
  2671 void TGenericIPI::WaitCompletion()
  2672 	{
  2673 	CHECK_PRECONDITIONS(MASK_NOT_ISR|MASK_INTERRUPTS_ENABLED,"TGenericIPI::WaitCompletion");
  2674 	volatile TInt* p = (volatile TInt*)&iNext;
  2675 	while (*p)
  2676 		{
  2677 		__chill();
  2678 		}
  2679 	mb();
  2680 	}
  2681 
  2682 /**	Stop all other CPUs
  2683 
  2684 	Call with kernel locked
  2685 */
  2686 void TStopIPI::StopCPUs()
  2687 	{
  2688 	iFlag = 0;
  2689 	QueueAllOther(&Isr);	// send IPIs to all other CPUs
  2690 	WaitEntry();			// wait for other CPUs to reach the ISR
  2691 	}
  2692 
  2693 void TStopIPI::ReleaseCPUs()
  2694 	{
  2695 	iFlag = 1;				// allow other CPUs to proceed
  2696 	WaitCompletion();		// wait for them to finish with this IPI
  2697 	}
  2698 
  2699 void TStopIPI::Isr(TGenericIPI* a)
  2700 	{
  2701 	TStopIPI* s = (TStopIPI*)a;
  2702 	while (!s->iFlag)
  2703 		{
  2704 		__chill();
  2705 		}
  2706 	}
  2707 
  2708