kernel/eka/nkern/win32/ncsched.cpp
changeset 0 a41df078684a
child 21 e7d2d738d3c2
equal deleted inserted replaced
-1:000000000000 0:a41df078684a
       
     1 // Copyright (c) 1998-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\nkern\win32\ncsched.cpp
       
    15 // 
       
    16 //
       
    17 
       
    18 // NThreadBase member data
       
    19 #define __INCLUDE_NTHREADBASE_DEFINES__
       
    20 
       
    21 #include <e32cmn.h>
       
    22 #include <e32cmn_private.h>
       
    23 #include "nk_priv.h"
       
    24 
       
    25 #ifdef __EMI_SUPPORT__
       
    26 extern void EMI_AddTaskSwitchEvent(TAny* aPrevious, TAny* aNext);
       
    27 extern void EMI_CheckDfcTag(TAny* aNext);
       
    28 #endif
       
    29 typedef void (*ProcessHandler)(TAny* aAddressSpace);
       
    30 
       
    31 static DWORD TlsIndex = TLS_OUT_OF_INDEXES;
       
    32 
       
    33 static NThreadBase* SelectThread(TScheduler& aS)
       
    34 //
       
    35 // Select the next thread to run.
       
    36 // This is the heart of the rescheduling algorithm.
       
    37 //
       
    38 	{
       
    39 	NThreadBase* t = static_cast<NThreadBase*>(aS.First());
       
    40 	__NK_ASSERT_DEBUG(t);
       
    41 #ifdef _DEBUG
       
    42 	if (t->iHeldFastMutex)
       
    43 		{
       
    44 		__KTRACE_OPT(KSCHED2,DEBUGPRINT("Resched init->%T, Holding %M",t,t->iHeldFastMutex));
       
    45 		}
       
    46 	else
       
    47 		{
       
    48 		__KTRACE_OPT(KSCHED2,DEBUGPRINT("Resched init->%T",t));
       
    49 		}
       
    50 #endif
       
    51 	if (t->iTime == 0 && !t->Alone())
       
    52 		{
       
    53 		// round robin
       
    54 		// get here if thread's timeslice has expired and there is another
       
    55 		// thread ready at the same priority
       
    56 		if (t->iHeldFastMutex)
       
    57 			{
       
    58 			// round-robin deferred due to fast mutex held
       
    59 			t->iHeldFastMutex->iWaiting = 1;
       
    60 			return t;
       
    61 			}
       
    62 		t->iTime = t->iTimeslice;		// reset old thread time slice
       
    63 		t = static_cast<NThreadBase*>(t->iNext);					// next thread
       
    64 		aS.iQueue[t->iPriority] = t;		// make it first in list
       
    65 		__KTRACE_OPT(KSCHED2,DEBUGPRINT("RoundRobin->%T",t));
       
    66 		}
       
    67 	if (t->iHeldFastMutex)
       
    68 		{
       
    69 		if (t->iHeldFastMutex == &aS.iLock)
       
    70 			{
       
    71 			// thread holds system lock: use it
       
    72 			return t;
       
    73 			}
       
    74 		if ((t->i_ThrdAttr & KThreadAttImplicitSystemLock) != 0 && aS.iLock.iHoldingThread)
       
    75 			t->iHeldFastMutex->iWaiting = 1;
       
    76 		__NK_ASSERT_DEBUG((t->i_ThrdAttr & KThreadAttAddressSpace) == 0);
       
    77 /*
       
    78 		Check for an address space change. Not implemented for Win32, but useful as
       
    79 		documentaiton of the algorithm.
       
    80 
       
    81 		if ((t->i_ThrdAttr & KThreadAttAddressSpace) != 0 && t->iAddressSpace != aS.iAddressSpace)
       
    82 			t->iHeldFastMutex->iWaiting = 1;
       
    83 */
       
    84 		}
       
    85 	else if (t->iWaitFastMutex && t->iWaitFastMutex->iHoldingThread)
       
    86 		{
       
    87 		__KTRACE_OPT(KSCHED2,DEBUGPRINT("Resched inter->%T, Blocked on %M",t->iWaitFastMutex->iHoldingThread,t->iWaitFastMutex));
       
    88 		t = t->iWaitFastMutex->iHoldingThread;
       
    89 		}
       
    90 	else if (t->i_ThrdAttr & KThreadAttImplicitSystemLock)
       
    91 		{
       
    92 		// implicit system lock required
       
    93 		if (aS.iLock.iHoldingThread)
       
    94 			{
       
    95 			// system lock held, switch to that thread
       
    96 			t = aS.iLock.iHoldingThread;
       
    97 			__KTRACE_OPT(KSCHED2,DEBUGPRINT("Resched inter->%T (IMP SYS)",t));
       
    98 			t->iHeldFastMutex->iWaiting = 1;	// aS.iLock.iWaiting = 1;
       
    99 			return t;
       
   100 			}
       
   101 		__NK_ASSERT_DEBUG((t->i_ThrdAttr & KThreadAttAddressSpace) == 0);
       
   102 /*
       
   103 		Check for an address space change. Not implemented for Win32, but useful as
       
   104 		documentaiton of the algorithm.
       
   105 
       
   106 		if ((t->i_ThrdAttr & KThreadAttAddressSpace) != 0 || t->iAddressSpace != aS.iAddressSpace)
       
   107 			{
       
   108 			// what do we do now?
       
   109 			__NK_ASSERT_DEBUG(FALSE);
       
   110 			}
       
   111 */
       
   112 		}
       
   113 	return t;
       
   114 	}
       
   115 
       
   116 // from NThread
       
   117 #undef i_ThrdAttr
       
   118 
       
   119 TBool NThread::WakeUp()
       
   120 //
       
   121 // Wake up the thread. What to do depends on whether we were preempted or voluntarily
       
   122 // rescheduled.
       
   123 //
       
   124 // Return TRUE if we need to immediately reschedule again because we had to unlock
       
   125 // the kernel but there are DFCs pending. In this case, the thread does not wake up.
       
   126 //
       
   127 // NB. kernel is locked
       
   128 //
       
   129 	{
       
   130 	switch (iWakeup)
       
   131 		{
       
   132 	default:
       
   133 		FAULT();
       
   134 	case EIdle:
       
   135 		__NK_ASSERT_ALWAYS(TheScheduler.iCurrentThread == this);
       
   136 		__NK_ASSERT_ALWAYS(SetEvent(iScheduleLock));
       
   137 		break;
       
   138 	case ERelease:
       
   139 		TheScheduler.iCurrentThread = this;
       
   140 		__NK_ASSERT_ALWAYS(SetEvent(iScheduleLock));
       
   141 		break;
       
   142 	case EResumeLocked:
       
   143 		// The thread is Win32 suspended and must be resumed.
       
   144 		//
       
   145 		// A newly created thread does not need the kernel unlocked so we can
       
   146 		// just resume the suspended thread
       
   147 		//
       
   148 		__KTRACE_OPT(KSCHED,DEBUGPRINT("Win32Resume->%T",this));
       
   149 		iWakeup = ERelease;
       
   150 		TheScheduler.iCurrentThread = this;
       
   151 		if (TheScheduler.iProcessHandler)
       
   152 			(*ProcessHandler(TheScheduler.iProcessHandler))(iAddressSpace); // new thread will need to have its static data updated
       
   153 		__NK_ASSERT_ALWAYS(TInt(ResumeThread(iWinThread)) > 0);	// check thread was previously suspended
       
   154 		break;
       
   155 	case EResumeDiverted:
       
   156 		// The thread is Win32 suspended and must be resumed.
       
   157 		//
       
   158 		// The thread needs to be diverted, and does not need the kernel
       
   159 		// unlocked.
       
   160 		//
       
   161 		// It's safe the divert the thread here because we called
       
   162 		// IsSafeToPreempt() when we suspended it - otherwise the diversion
       
   163 		// could get lost.
       
   164 		//
       
   165 		__KTRACE_OPT(KSCHED,DEBUGPRINT("Win32Resume->%T (Resuming diverted thread)",this));
       
   166 		iWakeup = ERelease;
       
   167 		ApplyDiversion();
       
   168 		TheScheduler.iCurrentThread = this;
       
   169 		__NK_ASSERT_ALWAYS(TInt(ResumeThread(iWinThread)) == 1);
       
   170 		break;
       
   171 	case EResume:
       
   172 		// The thread is Win32 suspended and must be resumed.
       
   173 		//
       
   174 		// the complication here is that we have to unlock the kernel on behalf of the
       
   175 		// pre-empted thread. This means that we have to check to see if there are more DFCs
       
   176 		// pending or a reschedule required, as we unlock the kernel. That check is
       
   177 		// carried out with interrupts disabled.
       
   178 		//
       
   179 		// If so, we go back around the loop in this thread context
       
   180 		//
       
   181 		// Otherwise, we unlock the kernel (having marked us as not-preempted),
       
   182 		// enable interrupts and then resume the thread. If pre-emption occurs before the thread
       
   183 		// is resumed, it is the new thread that is pre-empted, not the running thread, so we are guaranteed
       
   184 		// to be able to call ResumeThread. If pre-emption occurs, and we are rescheduled to run before
       
   185 		// that occurs, we will once again be running with the kernel locked and the other thread will
       
   186 		// have been re-suspended by Win32: so all is well.
       
   187 		//		
       
   188 		{
       
   189 		__KTRACE_OPT(KSCHED,DEBUGPRINT("Win32Resume->%T",this));
       
   190 		TInt irq = NKern::DisableAllInterrupts();
       
   191 		if (TheScheduler.iDfcPendingFlag || TheScheduler.iRescheduleNeededFlag)
       
   192 			{
       
   193 			// we were interrrupted... back to the top
       
   194 			TheScheduler.iRescheduleNeededFlag = TRUE;	// ensure we do the reschedule
       
   195 			return TRUE;
       
   196 			}
       
   197 		iWakeup = ERelease;
       
   198 		TheScheduler.iCurrentThread = this;
       
   199 		if (TheScheduler.iProcessHandler)
       
   200 			(*ProcessHandler(TheScheduler.iProcessHandler))(iAddressSpace); // threads resumed after interrupt or locks need to have static data updated
       
   201 
       
   202 		if (iInKernel == 0 && iUserModeCallbacks != NULL)
       
   203 			ApplyDiversion();
       
   204 		else 
       
   205 			TheScheduler.iKernCSLocked = 0;		// have to unlock the kernel on behalf of the new thread
       
   206 		
       
   207 		TheScheduler.iCurrentThread = this;
       
   208 		NKern::RestoreInterrupts(irq);
       
   209 		__NK_ASSERT_ALWAYS(TInt(ResumeThread(iWinThread)) > 0);	// check thread was previously suspended
       
   210 		}
       
   211 		break;
       
   212 		}
       
   213 	return FALSE;
       
   214 	}
       
   215 
       
   216 static void ThreadExit(NThread& aCurrent, NThread& aNext)
       
   217 //
       
   218 // The final context switch of a thread.
       
   219 // Wake up the next thread and then destroy this one's Win32 resources.
       
   220 //
       
   221 // Return without terminating if we need to immediately reschedule again because
       
   222 // we had to unlock the kernel but there are DFCs pending.
       
   223 //
       
   224 	{
       
   225 	// the thread is dead
       
   226 	// extract win32 handles from dying NThread object before rescheduling
       
   227 	HANDLE sl = aCurrent.iScheduleLock;
       
   228 	HANDLE th = aCurrent.iWinThread;
       
   229 
       
   230 	// wake up the next thread
       
   231 	if (aNext.WakeUp())
       
   232 		return;			// need to re-reschedule in this thread
       
   233 
       
   234 	// we are now a vanilla win32 thread, nKern no longer knows about us
       
   235 	// release resources and exit cleanly
       
   236 	CloseHandle(sl);
       
   237 	CloseHandle(th);
       
   238 	ExitThread(0);		// does not return
       
   239 	}
       
   240 
       
   241 #ifdef MONITOR_THREAD_CPU_TIME
       
   242 static inline void UpdateThreadCpuTime(NThread& aCurrent, NThread& aNext)
       
   243 	{	
       
   244 	TUint32 timestamp = NKern::FastCounter();
       
   245 	if (aCurrent.iLastStartTime)
       
   246 		aCurrent.iTotalCpuTime += timestamp - aCurrent.iLastStartTime;
       
   247 	aNext.iLastStartTime = timestamp;
       
   248 	}
       
   249 #else
       
   250 static inline void UpdateThreadCpuTime(NThread& /*aCurrent*/, NThread& /*aNext*/)
       
   251 	{	
       
   252 	}
       
   253 #endif
       
   254 
       
   255 static void SwitchThreads(NThread& aCurrent, NThread& aNext)
       
   256 //
       
   257 // The fundamental context switch - wake up the next thread and wait for reschedule
       
   258 // trivially is aNext.WakeUp(), Wait(aCurrent.iScheduleLock), but we may be able to
       
   259 // optimise the signal-and-wait
       
   260 //
       
   261 	{
       
   262 	UpdateThreadCpuTime(aCurrent, aNext);
       
   263 	if (aCurrent.iNState == NThread::EDead)
       
   264 		ThreadExit(aCurrent, aNext);
       
   265 	else if (Win32AtomicSOAW && aNext.iWakeup==NThread::ERelease)
       
   266 		{
       
   267 		// special case optimization for normally blocked threads using atomic Win32 primitive
       
   268 		TheScheduler.iCurrentThread = &aNext;
       
   269 		DWORD result=SignalObjectAndWait(aNext.iScheduleLock,aCurrent.iScheduleLock, INFINITE, FALSE);
       
   270 		if (result != WAIT_OBJECT_0)
       
   271 			{
       
   272 			__NK_ASSERT_ALWAYS(result == 0xFFFFFFFF);
       
   273 			KPrintf("SignalObjectAndWait() failed with %d (%T->%T)",GetLastError(),&aCurrent,&aNext);
       
   274 			FAULT();
       
   275 			}
       
   276 		}
       
   277 	else
       
   278 		{
       
   279 		if (aNext.WakeUp())
       
   280 			return;			// need to re-reschedule in this thread
       
   281 		__NK_ASSERT_ALWAYS(WaitForSingleObject(aCurrent.iScheduleLock, INFINITE) == WAIT_OBJECT_0);
       
   282 		}
       
   283 	}
       
   284 
       
   285 void TScheduler::YieldTo(NThreadBase*)
       
   286 //
       
   287 // Directed context switch to the nominated thread.
       
   288 // Enter with kernel locked, exit with kernel unlocked but interrupts disabled.
       
   289 //
       
   290 	{
       
   291 	RescheduleNeeded();
       
   292 	TScheduler::Reschedule();
       
   293 	}
       
   294 
       
   295 void TScheduler::Reschedule()
       
   296 //
       
   297 // Enter with kernel locked, exit with kernel unlocked, interrupts disabled.
       
   298 // If the thread is dead do not return, but terminate the thread.
       
   299 //
       
   300 	{
       
   301 	__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
       
   302 	NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread);
       
   303 	for (;;)
       
   304 		{
       
   305 		NKern::DisableAllInterrupts();
       
   306 		if (TheScheduler.iDfcPendingFlag)
       
   307 			TheScheduler.QueueDfcs();
       
   308 		if (!TheScheduler.iRescheduleNeededFlag)
       
   309 			break;
       
   310 		NKern::EnableAllInterrupts();
       
   311 		TheScheduler.iRescheduleNeededFlag = FALSE;
       
   312 		NThread* t = static_cast<NThread*>(SelectThread(TheScheduler));
       
   313 		__KTRACE_OPT(KSCHED,DEBUGPRINT("Reschedule->%T (%08x%08x)",t,TheScheduler.iPresent[1],TheScheduler.iPresent[0]));
       
   314 #ifdef __EMI_SUPPORT__
       
   315 		EMI_AddTaskSwitchEvent(&me,t);
       
   316 		EMI_CheckDfcTag(t);
       
   317 #endif
       
   318 #ifdef BTRACE_CPU_USAGE
       
   319 		if(TheScheduler.iCpuUsageFilter)
       
   320 			TheScheduler.iBTraceHandler(BTRACE_HEADER_C(4,BTrace::ECpuUsage,BTrace::ENewThreadContext),0,(TUint32)t,0,0,0,0,0);
       
   321 #endif
       
   322 		SwitchThreads(me, *t);
       
   323 
       
   324 		// we have just been scheduled to run... check for diversion/new Dfcs
       
   325 		NThread::TDivert divert = me.iDivert;
       
   326 		if (divert)
       
   327 			{
       
   328 			// diversion (e.g. force exit)
       
   329 			me.iDivert = NULL;
       
   330 			divert();						// does not return
       
   331 			}
       
   332 		}
       
   333 	if (TheScheduler.iProcessHandler)
       
   334 		(*ProcessHandler(TheScheduler.iProcessHandler))(me.iAddressSpace);
       
   335 	// interrrupts are disabled, the kernel is still locked
       
   336 	TheScheduler.iKernCSLocked = 0;
       
   337 	}
       
   338 
       
   339 /**	Put the emulator into 'idle'.
       
   340 	This is called by the idle thread when there is nothing else to do.
       
   341 
       
   342 	@internalTechnology
       
   343  */
       
   344 EXPORT_C void NThread::Idle()
       
   345 //
       
   346 // Rather than spin, we go to sleep on the schedule lock. Preemption detects
       
   347 // this state (Win32Idling) and pokes the event rather than diverting the thread.
       
   348 //
       
   349 // enter and exit with kernel locked
       
   350 //
       
   351 	{
       
   352 	NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread);
       
   353 	me.iWakeup = EIdle;
       
   354 	__NK_ASSERT_ALWAYS(WaitForSingleObject(me.iScheduleLock, INFINITE) == WAIT_OBJECT_0);
       
   355 	// something happened, and we've been prodded by an interrupt
       
   356 	// the kernel was locked by the interrupt, and now reschedule
       
   357 	me.iWakeup = ERelease;
       
   358 	TScheduler::Reschedule();
       
   359 	NKern::EnableAllInterrupts();
       
   360 	}
       
   361 
       
   362 void SchedulerInit(NThread& aInit)
       
   363 //
       
   364 // Initialise the win32 nKern scheduler
       
   365 //
       
   366 	{
       
   367 	DWORD procaffin,sysaffin;
       
   368 	if (GetProcessAffinityMask(GetCurrentProcess(),&procaffin,&sysaffin))
       
   369 		{
       
   370 		DWORD cpu;
       
   371 		switch (Win32SingleCpu)
       
   372 			{
       
   373 		default:
       
   374 			// bind the emulator to a nominated CPU on the host PC
       
   375 			cpu = (1<<Win32SingleCpu);
       
   376 			if (!(sysaffin & cpu))
       
   377 				cpu = procaffin;	// CPU selection invalid
       
   378 			break;
       
   379 		case NThread::ECpuSingle:
       
   380 			// bind the emulator to a single CPU on the host PC, pick one
       
   381 			cpu = procaffin ^ (procaffin & (procaffin-1));
       
   382 			break;
       
   383 		case NThread::ECpuAll:
       
   384 			// run the emulator on all CPUs on the host PC
       
   385 			cpu=sysaffin;
       
   386 			break;
       
   387 			}
       
   388 		SetProcessAffinityMask(GetCurrentProcess(), cpu);
       
   389 		}
       
   390 	// identify if we can use the atomic SignalObjectAndWait API in Win32 for rescheduling
       
   391 	Win32AtomicSOAW = (SignalObjectAndWait(aInit.iScheduleLock, aInit.iScheduleLock, INFINITE, FALSE) == WAIT_OBJECT_0);
       
   392 	//
       
   393 	// allocate the TLS used for thread identification, and set it for the init thread
       
   394 	TlsIndex = TlsAlloc();
       
   395 	__NK_ASSERT_ALWAYS(TlsIndex != TLS_OUT_OF_INDEXES);
       
   396 	SchedulerRegister(aInit);
       
   397 	//
       
   398 	Interrupt.Init();
       
   399 
       
   400 	Win32FindNonPreemptibleFunctions();
       
   401 	}
       
   402 
       
   403 void SchedulerRegister(NThread& aSelf)
       
   404 	{
       
   405 	TlsSetValue(TlsIndex,&aSelf);
       
   406 	}
       
   407 
       
   408 NThread* SchedulerThread()
       
   409 	{
       
   410 	if (TlsIndex != TLS_OUT_OF_INDEXES)
       
   411 		return static_cast<NThread*>(TlsGetValue(TlsIndex));
       
   412 	else
       
   413 		return NULL;  // not yet initialised
       
   414 	}
       
   415 
       
   416 inline TBool IsScheduledThread()
       
   417 	{
       
   418 	return SchedulerThread() == TheScheduler.iCurrentThread;
       
   419 	}
       
   420 	
       
   421 NThread& CheckedCurrentThread()
       
   422 	{
       
   423 	NThread* t = SchedulerThread();
       
   424 	__NK_ASSERT_ALWAYS(t == TheScheduler.iCurrentThread);
       
   425 	return *t;
       
   426 	}
       
   427 
       
   428 
       
   429 /**	Disable normal 'interrupts'.
       
   430 
       
   431 	@param	aLevel Ignored
       
   432 	@return	Cookie to be passed into RestoreInterrupts()
       
   433  */
       
   434 EXPORT_C TInt NKern::DisableInterrupts(TInt /*aLevel*/)
       
   435 	{
       
   436 	return Interrupt.Mask();
       
   437 	}
       
   438 
       
   439 
       
   440 /**	Disable all maskable 'interrupts'.
       
   441 
       
   442 	@return	Cookie to be passed into RestoreInterrupts()
       
   443  */
       
   444 EXPORT_C TInt NKern::DisableAllInterrupts()
       
   445 	{
       
   446 	return Interrupt.Mask();
       
   447 	}
       
   448 
       
   449 
       
   450 /**	Enable all maskable 'interrupts'
       
   451 
       
   452 	@internalComponent
       
   453  */
       
   454 EXPORT_C void NKern::EnableAllInterrupts()
       
   455 	{
       
   456 	Interrupt.Restore(0);
       
   457 	}
       
   458 
       
   459 
       
   460 /** Restore interrupt mask to state preceding a DisableInterrupts() call
       
   461 
       
   462 	@param	aLevel Cookie returned by Disable(All)Interrupts()
       
   463  */
       
   464 EXPORT_C void NKern::RestoreInterrupts(TInt aLevel)
       
   465 	{
       
   466 	Interrupt.Restore(aLevel);
       
   467 	}
       
   468 
       
   469 
       
   470 /**	Unlocks the kernel.
       
   471 
       
   472 	Decrements iKernCSLocked; if it becomes zero and IDFCs or a reschedule are
       
   473 	pending, calls the scheduler to process them.
       
   474 
       
   475     @pre    Call either in a thread or an IDFC context.
       
   476     @pre    Do not call from an ISR.
       
   477 	@pre	Do not call from bare Win32 threads.
       
   478  */
       
   479 EXPORT_C void NKern::Unlock()
       
   480 //
       
   481 // using this coding sequence it is possible to call Reschedule unnecessarily
       
   482 // if we are preempted after testing the flags (lock is zero at this point).
       
   483 // However, in the common case this is much faster because 'disabling interrupts'
       
   484 // can be very expensive.
       
   485 //
       
   486 	{
       
   487 	CHECK_PRECONDITIONS(MASK_NOT_ISR,"NKern::Unlock");	
       
   488 	__ASSERT_WITH_MESSAGE_DEBUG(IsScheduledThread(),"Do not call from bare Win32 threads","NKern::Unlock");	// check that we are a scheduled thread
       
   489 	__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked > 0);	// Can't unlock if it isn't locked!
       
   490 	if (--TheScheduler.iKernCSLocked == 0)
       
   491 		{
       
   492 		if (TheScheduler.iRescheduleNeededFlag || TheScheduler.iDfcPendingFlag)
       
   493 			{
       
   494 			TheScheduler.iKernCSLocked = 1;
       
   495 			TScheduler::Reschedule();
       
   496 			NKern::EnableAllInterrupts();
       
   497 			}
       
   498 		}
       
   499 	}
       
   500 
       
   501 
       
   502 /**	Locks the kernel.
       
   503 
       
   504 	Increments iKernCSLocked, thereby deferring IDFCs and preemption.
       
   505 
       
   506     @pre    Call either in a thread or an IDFC context.
       
   507     @pre    Do not call from an ISR.
       
   508 	@pre	Do not call from bare Win32 threads.
       
   509  */
       
   510 EXPORT_C void NKern::Lock()
       
   511 	{
       
   512 	CHECK_PRECONDITIONS(MASK_NOT_ISR,"NKern::Lock");		
       
   513 	__ASSERT_WITH_MESSAGE_ALWAYS(IsScheduledThread(),"Do not call from bare Win32 threads","NKern::Lock");	// check that we are a scheduled thread
       
   514 	++TheScheduler.iKernCSLocked;
       
   515 	}
       
   516 
       
   517 
       
   518 /**	Locks the kernel and returns a pointer to the current thread
       
   519 	Increments iKernCSLocked, thereby deferring IDFCs and preemption.
       
   520 
       
   521     @pre    Call either in a thread or an IDFC context.
       
   522     @pre    Do not call from an ISR.
       
   523 	@pre	Do not call from bare Win32 threads.
       
   524  */
       
   525 EXPORT_C NThread* NKern::LockC()
       
   526 	{
       
   527 	CHECK_PRECONDITIONS(MASK_NOT_ISR,"NKern::Lock");		
       
   528 	__ASSERT_WITH_MESSAGE_ALWAYS(IsScheduledThread(),"Do not call from bare Win32 threads","NKern::Lock");	// check that we are a scheduled thread
       
   529 	++TheScheduler.iKernCSLocked;
       
   530 	return (NThread*)TheScheduler.iCurrentThread;
       
   531 	}
       
   532 
       
   533 
       
   534 /**	Allows IDFCs and rescheduling if they are pending.
       
   535 
       
   536 	If IDFCs or a reschedule are pending and iKernCSLocked is exactly equal to 1
       
   537 	calls the scheduler to process the IDFCs and possibly reschedule.
       
   538 
       
   539 	@return	Nonzero if a reschedule actually occurred, zero if not.
       
   540 	
       
   541     @pre    Call either in a thread or an IDFC context.
       
   542     @pre    Do not call from an ISR.
       
   543 	@pre	Do not call from bare Win32 threads.
       
   544  */
       
   545 EXPORT_C TInt NKern::PreemptionPoint()
       
   546 	{
       
   547 	CHECK_PRECONDITIONS(MASK_NOT_ISR,"NKern::PreemptionPoint");		
       
   548 	__ASSERT_WITH_MESSAGE_DEBUG(IsScheduledThread(),"Do not call from bare Win32 threads","NKern::PreemptionPoint");	// check that we are a scheduled thread
       
   549 	if (TheScheduler.iKernCSLocked == 1 && 
       
   550 		(TheScheduler.iRescheduleNeededFlag || TheScheduler.iDfcPendingFlag))
       
   551 		{
       
   552 		TScheduler::Reschedule();
       
   553 		TheScheduler.iKernCSLocked = 1;
       
   554 		NKern::EnableAllInterrupts();
       
   555 		return TRUE;
       
   556 		}
       
   557 	return FALSE;
       
   558 	}
       
   559 
       
   560 
       
   561 /**	Mark the start of an 'interrupt' in the Win32 emulator.
       
   562 	This must be called in interrupt threads before using any other kernel APIs,
       
   563 	and should be paired with a call to EndOfInterrupt().
       
   564 
       
   565 	@pre	Win32 'interrupt' thread context
       
   566  */
       
   567 EXPORT_C void StartOfInterrupt()
       
   568 	{
       
   569 	__ASSERT_WITH_MESSAGE_DEBUG(!IsScheduledThread(),"Win32 'interrupt' thread context","StartOfInterrupt");	// check that we are a scheduled thread
       
   570 	Interrupt.Begin();
       
   571 	}
       
   572 
       
   573 
       
   574 /**	Mark the end of an 'interrupt' in the Win32 emulator.
       
   575 	This checks to see if we need to reschedule.
       
   576 
       
   577 	@pre	Win32 'interrupt' thread context
       
   578  */
       
   579 EXPORT_C void EndOfInterrupt()
       
   580 	{
       
   581 	__ASSERT_WITH_MESSAGE_DEBUG(!IsScheduledThread(),"Win32 'interrupt' thread context","EndOfInterrupt");	// check that we are a scheduled thread
       
   582 	Interrupt.End();
       
   583 	}
       
   584 
       
   585 
       
   586 void Win32Interrupt::Init()
       
   587 	{
       
   588 	iQ=CreateSemaphoreA(NULL, 0, KMaxTInt, NULL);
       
   589 	__NK_ASSERT_ALWAYS(iQ);
       
   590 	//
       
   591 	// create the NThread which exists solely to service reschedules for interrupts
       
   592 	// this makes the End() much simpler as it merely needs to kick this thread
       
   593 	SNThreadCreateInfo ni;
       
   594 	memclr(&ni, sizeof(ni));
       
   595 	ni.iFunction=&Reschedule;
       
   596 	ni.iTimeslice=-1;
       
   597 	ni.iPriority=1;
       
   598 	NKern::ThreadCreate(&iScheduler, ni);
       
   599 	NKern::Lock();
       
   600 	TScheduler::YieldTo(&iScheduler);
       
   601 	Restore(0);
       
   602 	}
       
   603 
       
   604 TInt Win32Interrupt::Mask()
       
   605 	{
       
   606 	if (!iQ)
       
   607 		return 0;				// interrupt scheme not enabled yet
       
   608 	DWORD id=GetCurrentThreadId();
       
   609 	if (__e32_atomic_add_ord32(&iLock, 1))
       
   610 		{
       
   611 		if (id==iOwner)
       
   612 			return iLevel++;
       
   613 		__NK_ASSERT_ALWAYS(WaitForSingleObject(iQ,INFINITE) == WAIT_OBJECT_0);
       
   614 		iRescheduleOnExit=IsScheduledThread() &&
       
   615 				(TheScheduler.iRescheduleNeededFlag || TheScheduler.iDfcPendingFlag);
       
   616 		}
       
   617 	else
       
   618 		iRescheduleOnExit=FALSE;
       
   619 	__NK_ASSERT_ALWAYS(iOwner==0 && iLevel==0);
       
   620 	iOwner=id;
       
   621 	iLevel=1;
       
   622 	return 0;
       
   623 	}
       
   624 
       
   625 void Win32Interrupt::Restore(TInt aLevel)
       
   626 	{
       
   627 	if (!iQ)
       
   628 		return;				// interrupt scheme not enabled yet
       
   629 	DWORD id=GetCurrentThreadId();
       
   630 	for (;;)
       
   631 		{
       
   632 		__NK_ASSERT_ALWAYS(id == iOwner);
       
   633 		TInt count = iLevel - aLevel;
       
   634 		if (count <= 0)
       
   635 			return;						// alredy restored to that level
       
   636 		TBool reschedule = FALSE;
       
   637 		iLevel = aLevel;		// update this value before releasing the lock
       
   638 		if (aLevel == 0)
       
   639 			{
       
   640 			// we release the lock
       
   641 			iOwner = 0;
       
   642 			if (iRescheduleOnExit && TheScheduler.iKernCSLocked == 0)
       
   643 				reschedule = TRUE;		// need to trigger reschedule on full release
       
   644 			}
       
   645 		// now release the lock
       
   646 		if (__e32_atomic_add_ord32(&iLock, TUint32(-count)) == (TUint32)count)
       
   647 			{	// fully released, check for reschedule
       
   648 			if (!reschedule)
       
   649 				return;
       
   650 			}
       
   651 		else
       
   652 			{	// not fully released
       
   653 			if (aLevel == 0)
       
   654 				__NK_ASSERT_ALWAYS(ReleaseSemaphore(iQ,1,NULL));
       
   655 			return;
       
   656 			}
       
   657 		// unlocked everything but a reschedule may be required
       
   658 		TheScheduler.iKernCSLocked = 1;
       
   659 		TScheduler::Reschedule();
       
   660 		// return with the kernel unlocked, but interrupts disabled
       
   661 		// instead of going recursive with a call to EnableAllInterrupts() we iterate
       
   662 		aLevel=0;
       
   663 		}
       
   664 	}
       
   665 
       
   666 void Win32Interrupt::Begin()
       
   667 	{
       
   668 	Mask();
       
   669 	__NK_ASSERT_ALWAYS(iInterrupted==0);	// check we haven't done this already
       
   670 	__NK_ASSERT_ALWAYS(!IsScheduledThread());	// check that we aren't a scheduled thread
       
   671 	NThread* pC;
       
   672 	for (;;)
       
   673 		{
       
   674 		pC=static_cast<NThread*>(TheScheduler.iCurrentThread);
       
   675 		DWORD r=SuspendThread(pC->iWinThread);
       
   676 		if (pC == TheScheduler.iCurrentThread)
       
   677 			{
       
   678 			// there was no race while suspending the thread, so we can carry on
       
   679 			__NK_ASSERT_ALWAYS(r != 0xffffffff);
       
   680 			break;
       
   681 			}
       
   682 		// We suspended the thread while doing a context switch, resume it and try again
       
   683 		if (r != 0xffffffff)
       
   684 			__NK_ASSERT_ALWAYS(TInt(ResumeThread(pC->iWinThread)) > 0);	// check thread was previously suspended
       
   685 		}
       
   686 #ifdef BTRACE_CPU_USAGE
       
   687 	BTrace0(BTrace::ECpuUsage,BTrace::EIrqStart);
       
   688 #endif
       
   689 	iInterrupted = pC;
       
   690 	}
       
   691 
       
   692 void Win32Interrupt::End()
       
   693 	{
       
   694 	__NK_ASSERT_ALWAYS(iOwner == GetCurrentThreadId());	// check we are the interrupting thread
       
   695 	NThread* pC = iInterrupted;
       
   696 	__NK_ASSERT_ALWAYS(pC==TheScheduler.iCurrentThread);
       
   697 	iInterrupted = 0;
       
   698 	if (iLock == 1 && TheScheduler.iKernCSLocked == 0 &&
       
   699 		(TheScheduler.iRescheduleNeededFlag || TheScheduler.iDfcPendingFlag) &&
       
   700 		pC->IsSafeToPreempt())
       
   701 		{
       
   702 		TheScheduler.iKernCSLocked = 1;		// prevent further pre-emption
       
   703 		if (pC->iWakeup == NThread::EIdle)
       
   704 			{
       
   705 			// wake up the NULL thread, it will always reschedule immediately
       
   706 			pC->WakeUp();
       
   707 			}
       
   708 		else
       
   709 			{
       
   710 			// pre-empt the current thread and poke the 'scheduler' thread
       
   711 			__NK_ASSERT_ALWAYS(pC->iWakeup == NThread::ERelease);
       
   712 			pC->iWakeup = NThread::EResume;
       
   713 			UpdateThreadCpuTime(*pC, iScheduler);
       
   714 			RescheduleNeeded();
       
   715 			NKern::EnableAllInterrupts();
       
   716 			iScheduler.WakeUp();
       
   717 			return;
       
   718 			}
       
   719 		}
       
   720 	else
       
   721 		{
       
   722 		// no thread reschedle, so emit trace...
       
   723 #ifdef BTRACE_CPU_USAGE
       
   724 		BTrace0(BTrace::ECpuUsage,BTrace::EIrqEnd);
       
   725 #endif
       
   726 		}
       
   727 
       
   728 	if (((NThread*)pC)->iInKernel == 0 &&		// thread is running in user mode
       
   729 		pC->iUserModeCallbacks != NULL && 		// and has callbacks queued
       
   730 		TheScheduler.iKernCSLocked == 0 &&		// and is not currently processing a diversion
       
   731 		pC->IsSafeToPreempt())					// and can be safely prempted at this point
       
   732 		{
       
   733 		TheScheduler.iKernCSLocked = 1;
       
   734 		pC->ApplyDiversion();
       
   735 		}
       
   736 	NKern::EnableAllInterrupts();
       
   737 	__NK_ASSERT_ALWAYS(TInt(ResumeThread(pC->iWinThread)) > 0);	// check thread was previously suspended
       
   738 	}
       
   739 
       
   740 void Win32Interrupt::Reschedule(TAny*)
       
   741 //
       
   742 // The entry-point for the interrupt-rescheduler thread.
       
   743 //
       
   744 // This spends its whole life going around the TScheduler::Reschedule() loop
       
   745 // selecting another thread to run.
       
   746 //
       
   747 	{
       
   748 	TheScheduler.iKernCSLocked = 1;
       
   749 	RescheduleNeeded();
       
   750 	TScheduler::Reschedule();
       
   751 	FAULT();
       
   752 	}
       
   753 
       
   754 void Win32Interrupt::ForceReschedule()
       
   755 	{
       
   756 	RescheduleNeeded();
       
   757 	iScheduler.WakeUp();
       
   758 	}
       
   759 
       
   760 void SchedulerEscape()
       
   761 	{
       
   762 	NThread& me=CheckedCurrentThread();
       
   763 	EnterKernel();
       
   764 	__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked==0);	// Can't call Escape() with the Emulator/kernel already locked
       
   765 	NKern::ThreadEnterCS();
       
   766 	NKern::Lock();
       
   767 	me.iNState=NThreadBase::EBlocked;
       
   768 	TheScheduler.Remove(&me);
       
   769 	me.iWakeup=NThread::EEscaped;
       
   770 	SetThreadPriority(me.iWinThread,THREAD_PRIORITY_ABOVE_NORMAL);
       
   771 	Interrupt.ForceReschedule();	// schedules some other thread so we can carry on outside the scheduler domain
       
   772 	// this will change the value of iCurrentThread to ensure the 'escaped' invariants are set
       
   773 	}
       
   774 
       
   775 void ReenterDfc(TAny* aPtr)
       
   776 	{
       
   777 	NThread& me = *static_cast<NThread*>(aPtr);
       
   778 	me.iWakeup = NThread::ERelease;
       
   779 	me.CheckSuspendThenReady();
       
   780 	}
       
   781 
       
   782 void SchedulerReenter()
       
   783 	{
       
   784 	NThread* me=SchedulerThread();
       
   785 	__NK_ASSERT_ALWAYS(me);
       
   786 	__NK_ASSERT_ALWAYS(me->iWakeup == NThread::EEscaped);
       
   787 	TDfc idfc(&ReenterDfc, me);
       
   788 	StartOfInterrupt();
       
   789 	idfc.Add();
       
   790 	EndOfInterrupt();
       
   791 	SetThreadPriority(me->iWinThread,THREAD_PRIORITY_NORMAL);
       
   792 	__NK_ASSERT_ALWAYS(WaitForSingleObject(me->iScheduleLock, INFINITE) == WAIT_OBJECT_0);
       
   793 	// when released, the kernel is locked and handed over to us
       
   794 	// need to complete the reschedule protocol in this thread now
       
   795 	TScheduler::Reschedule();
       
   796 	NKern::EnableAllInterrupts();
       
   797 	NKern::ThreadLeaveCS();
       
   798 	LeaveKernel();
       
   799 	}
       
   800 
       
   801 
       
   802 /**	Return the current processor context type
       
   803 	(thread, IDFC, interrupt or escaped thread)
       
   804 
       
   805 	@return	A value from NKern::TContext enumeration (including EEscaped)
       
   806 	@pre	Any context
       
   807 
       
   808 	@see	NKern::TContext
       
   809  */
       
   810 EXPORT_C TInt NKern::CurrentContext()
       
   811 	{
       
   812 	NThread* t = SchedulerThread();
       
   813 	if (!t)
       
   814 		return NKern::EInterrupt;
       
   815 	if (TheScheduler.iInIDFC)
       
   816 		return NKern::EIDFC;
       
   817 	if (t->iWakeup == NThread::EEscaped)
       
   818 		return NKern::EEscaped;
       
   819 	__NK_ASSERT_ALWAYS(NKern::Crashed() || t == TheScheduler.iCurrentThread);
       
   820 	return NKern::EThread;
       
   821 	}
       
   822 
       
   823 //
       
   824 // We use SuspendThread and ResumeThread to preempt threads.  This can cause
       
   825 // deadlock if the thread is using windows synchronisation primitives (eg
       
   826 // critical sections).  This isn't too much of a problem most of the time,
       
   827 // because threads generally use the symbian environment rather than the native
       
   828 // windows APIs.  However exceptions are an issue - they can happen at any time,
       
   829 // and cause execution of native windows code over which we have no control.
       
   830 //
       
   831 // To work around this we examine the call stack to see if the thread is inside
       
   832 // one of the windows exception handling functions.  If so, preemption is
       
   833 // deferred.
       
   834 //
       
   835 
       
   836 #include <winnt.h>
       
   837 
       
   838 const TInt KWin32NonPreemptibleFunctionCount = 2;
       
   839 
       
   840 struct TWin32FunctionInfo
       
   841 	{
       
   842 	TUint iStartAddr;
       
   843 	TUint iLength;
       
   844 	};
       
   845 
       
   846 static TWin32FunctionInfo Win32NonPreemptibleFunctions[KWin32NonPreemptibleFunctionCount];
       
   847 
       
   848 TWin32FunctionInfo Win32FindExportedFunction(const char* aModuleName, const char* aFunctionName)
       
   849 	{
       
   850 	HMODULE library = GetModuleHandleA(aModuleName);
       
   851 	__NK_ASSERT_ALWAYS(library != NULL);
       
   852 
       
   853 	// Find the start address of the function
       
   854 	TUint start = (TUint)GetProcAddress(library, aFunctionName);
       
   855 	__NK_ASSERT_ALWAYS(start);
       
   856 
       
   857 	// Now have to check all other exports to find the end of the function
       
   858 	TUint end = 0xffffffff;
       
   859 	TInt i = 1;
       
   860 	for (;;)
       
   861 		{
       
   862 		TUint addr = (TUint)GetProcAddress(library, MAKEINTRESOURCEA(i));
       
   863 		if (!addr)
       
   864 			break;
       
   865 		if (addr > start && addr < end)
       
   866 			end = addr;
       
   867 		++i;
       
   868 		}
       
   869 	__NK_ASSERT_ALWAYS(end != 0xffffffff);
       
   870 
       
   871 	TWin32FunctionInfo result = { start, end - start };
       
   872 	return result;
       
   873 	}
       
   874 
       
   875 void Win32FindNonPreemptibleFunctions()
       
   876 	{
       
   877 	Win32NonPreemptibleFunctions[0] = Win32FindExportedFunction("kernel32.dll", "RaiseException");
       
   878 	Win32NonPreemptibleFunctions[1] = Win32FindExportedFunction("ntdll.dll", "KiUserExceptionDispatcher");
       
   879 	}
       
   880 	
       
   881 TBool Win32IsThreadInNonPreemptibleFunction(HANDLE aWinThread, TLinAddr aStackTop)
       
   882 	{
       
   883 	const TInt KMaxSearchDepth = 16;		 // 12 max observed while handling exceptions
       
   884 	const TInt KMaxStackSize = 1024 * 1024;  // Default reserved stack size on windows
       
   885 	const TInt KMaxFrameSize = 4096;
       
   886 
       
   887 	CONTEXT c;
       
   888  	c.ContextFlags=CONTEXT_FULL;
       
   889 	GetThreadContext(aWinThread, &c);
       
   890 
       
   891 	TUint eip = c.Eip;
       
   892 	TUint ebp = c.Ebp;
       
   893 	TUint lastEbp = c.Esp;
       
   894 
       
   895 	// Walk the call stack
       
   896 	for (TInt i = 0 ; i < KMaxSearchDepth ; ++i)
       
   897 		{
       
   898 		for (TInt j = 0 ; j < KWin32NonPreemptibleFunctionCount ; ++j)
       
   899 			{
       
   900 			const TWin32FunctionInfo& info = Win32NonPreemptibleFunctions[j];
       
   901 			if (TUint(eip - info.iStartAddr) < info.iLength)
       
   902 				{
       
   903 				__KTRACE_OPT(KSCHED, DEBUGPRINT("Thread is in non-preemptible function %d at frame %d: eip == %08x", j, i, eip));
       
   904 				return TRUE;
       
   905 				}
       
   906 			}
       
   907 		
       
   908 		// Check frame pointer is valid before dereferencing it
       
   909 		if (TUint(aStackTop - ebp) > KMaxStackSize || TUint(ebp - lastEbp) > KMaxFrameSize || ebp & 3)
       
   910 			break;
       
   911 
       
   912 		TUint* frame = (TUint*)ebp;
       
   913 		lastEbp = ebp;
       
   914 		ebp = frame[0];
       
   915 		eip = frame[1];
       
   916 		}
       
   917 	
       
   918 	return FALSE;
       
   919 	}
       
   920 
       
   921 TBool NThread::IsSafeToPreempt()
       
   922 	{
       
   923 	return !Win32IsThreadInNonPreemptibleFunction(iWinThread, iUserStackBase);
       
   924 	}
       
   925 
       
   926 void LeaveKernel()
       
   927 	{
       
   928 	TInt& k=CheckedCurrentThread().iInKernel;
       
   929 	__NK_ASSERT_DEBUG(k>0);
       
   930 	if (k==1)  // just about to leave kernel
       
   931 		{
       
   932 		NThread& t = CheckedCurrentThread();
       
   933 		__NK_ASSERT_ALWAYS(t.iCsCount==0);
       
   934 		__NK_ASSERT_ALWAYS(t.iHeldFastMutex==0);
       
   935 		__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked==0);
       
   936 		NKern::DisableAllInterrupts();
       
   937 		t.CallUserModeCallbacks();
       
   938 		NKern::EnableAllInterrupts();
       
   939 		}
       
   940 	--k;
       
   941 	}
       
   942