| 0 |      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 | 
 |