os/kernelhwsrv/kernel/eka/nkern/win32/ncthrd.cpp
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/kernelhwsrv/kernel/eka/nkern/win32/ncthrd.cpp	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,650 @@
     1.4 +// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
     1.5 +// All rights reserved.
     1.6 +// This component and the accompanying materials are made available
     1.7 +// under the terms of the License "Eclipse Public License v1.0"
     1.8 +// which accompanies this distribution, and is available
     1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.10 +//
    1.11 +// Initial Contributors:
    1.12 +// Nokia Corporation - initial contribution.
    1.13 +//
    1.14 +// Contributors:
    1.15 +//
    1.16 +// Description:
    1.17 +// e32\nkern\win32\ncthrd.cpp
    1.18 +// 
    1.19 +//
    1.20 +
    1.21 +// NThreadBase member data
    1.22 +#define __INCLUDE_NTHREADBASE_DEFINES__
    1.23 +
    1.24 +#include "nk_priv.h"
    1.25 +#include <emulator.h>
    1.26 +
    1.27 +extern "C" void ExcFault(TAny*);
    1.28 +
    1.29 +// initial Win32 thread stack size
    1.30 +const TInt KInitialStackSize = 0x1000;
    1.31 +
    1.32 +// maximum size of the parameter block passed to a new thread
    1.33 +const TInt KMaxParameterBlock = 512;
    1.34 +
    1.35 +// data passed to new thread to enable hand-off of the parameter block
    1.36 +struct SCreateThread
    1.37 +	{
    1.38 +	const SNThreadCreateInfo* iInfo;
    1.39 +	NFastMutex iHandoff;
    1.40 +	};
    1.41 +
    1.42 +/**
    1.43 + * Set the Win32 thread priority based on the thread type.
    1.44 + * Interrupt/Event threads must be able to preempt normal nKern threads,
    1.45 + * so they get a higher priority.
    1.46 + */
    1.47 +static void SetPriority(HANDLE aThread, TEmulThreadType aType)
    1.48 +	{
    1.49 +	TInt p;
    1.50 +	switch (aType)
    1.51 +		{
    1.52 +	default:
    1.53 +		FAULT();
    1.54 +	case EThreadEvent:
    1.55 +		p = THREAD_PRIORITY_ABOVE_NORMAL;
    1.56 +		break;
    1.57 +	case EThreadNKern:
    1.58 +		p = THREAD_PRIORITY_NORMAL;
    1.59 +		break;
    1.60 +		}
    1.61 +
    1.62 +	__NK_ASSERT_ALWAYS(SetThreadPriority(aThread, p));
    1.63 +	SetThreadPriorityBoost(aThread, TRUE);		// disable priority boost (for NT)
    1.64 +	}
    1.65 +
    1.66 +
    1.67 +/**	Create a Win32 thread for use in the emulator.
    1.68 +
    1.69 +	@param	aType Type of thread (Event or NKern) - determines Win32 priority
    1.70 +	@param	aThreadFunc Entry point of thread
    1.71 +	@param	aPtr Argument passed to entry point
    1.72 +	@param	aRun TRUE if thread should be resumed immediately
    1.73 +	@return	The Win32 handle to the thread, 0 if an error occurred
    1.74 +
    1.75 +	@pre    Call either in thread context.
    1.76 +	@pre	Do not call from bare Win32 threads.
    1.77 +
    1.78 +	@see TEmulThreadType
    1.79 + */
    1.80 +EXPORT_C HANDLE CreateWin32Thread(TEmulThreadType aType, LPTHREAD_START_ROUTINE aThreadFunc, LPVOID aPtr, TBool aRun)
    1.81 +	{
    1.82 +	__NK_ASSERT_DEBUG(!TheScheduler.iCurrentThread || NKern::CurrentContext() == NKern::EThread);
    1.83 +
    1.84 +	__LOCK_HOST;
    1.85 +	
    1.86 +	DWORD id;
    1.87 +	HANDLE handle = CreateThread(NULL , KInitialStackSize, aThreadFunc, aPtr, CREATE_SUSPENDED, &id);
    1.88 +	if (handle)
    1.89 +		{
    1.90 +		SetPriority(handle, aType);
    1.91 +		if (aRun)
    1.92 +			ResumeThread(handle);
    1.93 +		}
    1.94 +	return handle;
    1.95 +	}
    1.96 +
    1.97 +
    1.98 +/** Set some global properties of the emulator
    1.99 +	Called by the Win32 base port during boot.
   1.100 +
   1.101 +	@param	aTrace TRUE means trace Win32 thread ID for every thread created
   1.102 +	@param	aSingleCpu TRUE means lock the emulator process to a single CPU
   1.103 +
   1.104 +	@internalTechnology
   1.105 + */
   1.106 +EXPORT_C void NThread::SetProperties(TBool aTrace, TInt aSingleCpu)
   1.107 +	{
   1.108 +	Win32TraceThreadId = aTrace;
   1.109 +	Win32SingleCpu = aSingleCpu;
   1.110 +	}
   1.111 +
   1.112 +#if defined(__CW32__) && __MWERKS__ < 0x3200
   1.113 +DWORD NThread__ExceptionHandler(EXCEPTION_RECORD* aException, TAny* /*aRegistrationRecord*/, CONTEXT* aContext)
   1.114 +//
   1.115 +// Hook into exception handling for old version of CW
   1.116 +//
   1.117 +	{
   1.118 +	return NThread::ExceptionHandler(aException, aContext);
   1.119 +	}
   1.120 +#endif // old __CW32__
   1.121 +
   1.122 +DWORD WINAPI NThread::StartThread(LPVOID aParam)
   1.123 +//
   1.124 +// Win32 thread function for nKern threads.
   1.125 +//
   1.126 +// The thread first enters this function after the nScheduler has resumed
   1.127 +// it, following the context switch induced by the hand-off mutex.
   1.128 +//
   1.129 +// The parameter block for this thread needs to be copied into its
   1.130 +// own context, before releasing the mutex and handing control back to
   1.131 +// the creating thread.
   1.132 +//
   1.133 +	{
   1.134 +	SCreateThread* init = static_cast<SCreateThread*>(aParam);
   1.135 +	NThread& me=*static_cast<NThread*>(init->iHandoff.iHoldingThread);
   1.136 +	me.iWinThreadId = GetCurrentThreadId();
   1.137 +	SchedulerRegister(me);
   1.138 +#ifdef BTRACE_FAST_MUTEX
   1.139 +	BTraceContext4(BTrace::EFastMutex,BTrace::EFastMutexWait,&init->iHandoff);
   1.140 +#endif
   1.141 +	NKern::Unlock();
   1.142 +
   1.143 +#if defined(__CW32__) && __MWERKS__ < 0x3200
   1.144 +	// intercept the win32 exception mechanism manually
   1.145 +    asm {
   1.146 +    	push	ebp
   1.147 +    	mov		eax, -1
   1.148 +    	push	eax
   1.149 +    	push	eax
   1.150 +    	push	offset NThread__ExceptionHandler
   1.151 +    	push	fs:[0]
   1.152 +    	mov		fs:[0], esp
   1.153 +
   1.154 +		// realign the stack
   1.155 +    	sub		esp, 0x20
   1.156 +    	and		esp, ~0x1f
   1.157 +		}
   1.158 +#else // ! old __CW32__
   1.159 +	// intercept win32 exceptions in a debuggabble way
   1.160 +__try {
   1.161 +#endif // old __CW32__
   1.162 +
   1.163 +	// save the thread entry point and parameter block
   1.164 +	const SNThreadCreateInfo& info = *init->iInfo;
   1.165 +	TUint8 parameterBlock[KMaxParameterBlock];
   1.166 +	TAny* parameter=(TAny*)info.iParameterBlock;
   1.167 +	if (info.iParameterBlockSize)
   1.168 +		{
   1.169 +		__NK_ASSERT_DEBUG(TUint(info.iParameterBlockSize)<=TUint(KMaxParameterBlock));
   1.170 +		parameter=parameterBlock;
   1.171 +		memcpy(parameterBlock,info.iParameterBlock,info.iParameterBlockSize);
   1.172 +		}
   1.173 +	NThreadFunction threadFunction=info.iFunction;
   1.174 +
   1.175 +	// Calculate stack base
   1.176 +	me.iUserStackBase = (((TLinAddr)&parameterBlock)+0xfff)&~0xfff; // base address of stack
   1.177 +
   1.178 +	// some useful diagnostics for debugging
   1.179 +	if (Win32TraceThreadId)
   1.180 +		KPrintf("Thread %T created @ 0x%x - Win32 Thread ID 0x%x",init->iHandoff.iHoldingThread,init->iHandoff.iHoldingThread,GetCurrentThreadId());
   1.181 +
   1.182 +#ifdef MONITOR_THREAD_CPU_TIME
   1.183 +	me.iLastStartTime = 0;  // Don't count NThread setup in cpu time
   1.184 +#endif
   1.185 + 
   1.186 +	// start-up complete, release the handoff mutex, which will re-suspend us
   1.187 +	NKern::FMSignal(&init->iHandoff);
   1.188 +
   1.189 +	// thread has been resumed: invoke the thread function
   1.190 +	threadFunction(parameter);
   1.191 +
   1.192 +#if !defined(__CW32__) || __MWERKS__ >= 0x3200
   1.193 +	// handle win32 exceptions
   1.194 +} __except (ExceptionFilter(GetExceptionInformation())) {
   1.195 +	// Do nothing - filter does all the work and hooks
   1.196 +	// into EPOC h/w exception mechanism if necessary
   1.197 +	// by thread diversion
   1.198 +}
   1.199 +#endif // !old __CW32__
   1.200 +
   1.201 +	NKern::Exit();
   1.202 +
   1.203 +	return 0;
   1.204 +	}
   1.205 +
   1.206 +static HANDLE InitThread()
   1.207 +//
   1.208 +// Set up the initial thread and return the thread handle
   1.209 +//
   1.210 +	{
   1.211 +	HANDLE p = GetCurrentProcess();
   1.212 +	HANDLE me;
   1.213 +	__NK_ASSERT_ALWAYS(DuplicateHandle(p, GetCurrentThread(), p, &me, 0, FALSE, DUPLICATE_SAME_ACCESS));
   1.214 +	SetPriority(me, EThreadNKern);
   1.215 +	return me;
   1.216 +	}
   1.217 +
   1.218 +TInt NThread::Create(SNThreadCreateInfo& aInfo, TBool aInitial)
   1.219 +	{
   1.220 +	iWinThread = NULL;
   1.221 +	iWinThreadId = 0;
   1.222 +	iScheduleLock = NULL;
   1.223 +	iInKernel = 1;
   1.224 +	iDivert = NULL;
   1.225 +	iWakeup = aInitial ? ERelease : EResumeLocked;	// mark new threads as created (=> win32 suspend)
   1.226 +
   1.227 +	TInt r=NThreadBase::Create(aInfo,aInitial);
   1.228 +	if (r!=KErrNone)
   1.229 +		return r;
   1.230 +
   1.231 +	// the rest has to be all or nothing, we must complete it
   1.232 +	iScheduleLock = CreateEventA(NULL, FALSE, FALSE, NULL);
   1.233 +	if (iScheduleLock == NULL)
   1.234 +		return Emulator::LastError();
   1.235 +
   1.236 +	if (aInitial)
   1.237 +		{
   1.238 +		iWinThread = InitThread();
   1.239 +		FastCounterInit();
   1.240 +#ifdef MONITOR_THREAD_CPU_TIME
   1.241 +		iLastStartTime = NKern::FastCounter();
   1.242 +#endif
   1.243 +		iUserStackBase = (((TLinAddr)&r)+0xfff)&~0xfff; // base address of stack
   1.244 +		SchedulerInit(*this);
   1.245 +		return KErrNone;
   1.246 +		}
   1.247 +
   1.248 +	// create the thread proper
   1.249 +	//
   1.250 +	SCreateThread start;
   1.251 +	start.iInfo = &aInfo;
   1.252 +
   1.253 +	iWinThread = CreateWin32Thread(EThreadNKern, &StartThread, &start, FALSE);
   1.254 +	if (iWinThread == NULL)
   1.255 +		{
   1.256 +		r = Emulator::LastError();
   1.257 +		CloseHandle(iScheduleLock);
   1.258 +		return r;
   1.259 +		}
   1.260 +
   1.261 +#ifdef BTRACE_THREAD_IDENTIFICATION
   1.262 +	BTrace4(BTrace::EThreadIdentification,BTrace::ENanoThreadCreate,this);
   1.263 +#endif
   1.264 +	// switch to the new thread to hand over the parameter block
   1.265 +	NKern::Lock();
   1.266 +	ForceResume();	// mark the thread as ready
   1.267 +	// give the thread ownership of the handoff mutex
   1.268 +	start.iHandoff.iHoldingThread = this;
   1.269 +	iHeldFastMutex = &start.iHandoff;
   1.270 +	Suspend(1);		// will defer as holding a fast mutex (implicit critical section)
   1.271 +	// do the hand-over
   1.272 +	start.iHandoff.Wait();
   1.273 +	start.iHandoff.Signal();
   1.274 +	NKern::Unlock();
   1.275 +
   1.276 +	return KErrNone;
   1.277 +	}
   1.278 +
   1.279 +void NThread__HandleException(TWin32ExcInfo aExc)
   1.280 +//
   1.281 +// Final stage NKern exception handler.
   1.282 +//
   1.283 +// Check for a fatal exception when the kernel is locked
   1.284 +//
   1.285 +// Note that the parameter struct is passed by value, this allows for
   1.286 +// direct access to the exception context created on the call stack by
   1.287 +// NThread::Exception().
   1.288 +//
   1.289 +	{
   1.290 +	if (TheScheduler.iKernCSLocked)
   1.291 +		ExcFault(&aExc);
   1.292 +
   1.293 +	// Complete the exception data. Note that the call to EnterKernel() in
   1.294 +	// ExceptionFilter() will have incremented iInKernel after the exception
   1.295 +	// occurred.
   1.296 +	NThread* me = static_cast<NThread*>(TheScheduler.iCurrentThread);
   1.297 +	__NK_ASSERT_DEBUG(me->iInKernel);
   1.298 +	aExc.iFlags = me->iInKernel == 1 ? 0 : TWin32ExcInfo::EExcInKernel;
   1.299 +	aExc.iHandler = NULL;
   1.300 +
   1.301 +	// run NThread exception handler in 'kernel' mode
   1.302 +	me->iHandlers->iExceptionHandler(&aExc, me);
   1.303 +	LeaveKernel();
   1.304 +
   1.305 +	// If a 'user' handler is set by the kernel handler, run it
   1.306 +	if (aExc.iHandler)
   1.307 +		aExc.iHandler(aExc.iParam[0], aExc.iParam[1]);
   1.308 +	}
   1.309 +
   1.310 +void NKern__Unlock()
   1.311 +//
   1.312 +// CW asm ICE workaround
   1.313 +//
   1.314 +	{
   1.315 +	NKern::Unlock();
   1.316 +	}
   1.317 +
   1.318 +__NAKED__ void NThread::Exception()
   1.319 +//
   1.320 +// Trampoline to nKern exception handler
   1.321 +// must preserve all registers in the structure defined by TWin32Exc
   1.322 +//
   1.323 +	{
   1.324 +	// this is the TWin32Exc structure
   1.325 +	__asm push Win32ExcAddress			// save return address followed by EBP first to help debugger
   1.326 +	__asm push ebp
   1.327 +	__asm mov ebp, esp
   1.328 +	__asm push cs
   1.329 +	__asm pushfd
   1.330 +	__asm push gs
   1.331 +	__asm push fs
   1.332 +	__asm push es
   1.333 +	__asm push ds
   1.334 +	__asm push ss
   1.335 +	__asm push edi
   1.336 +	__asm push esi
   1.337 +	__asm lea esi, [ebp+8]
   1.338 +	__asm push esi		// original esp
   1.339 +	__asm push ebx
   1.340 +	__asm push edx
   1.341 +	__asm push ecx
   1.342 +	__asm push eax
   1.343 +	__asm push Win32ExcDataAddress
   1.344 +	__asm push Win32ExcCode
   1.345 +	__asm sub esp, 20	// struct init completed by NThread__HandleException()
   1.346 +
   1.347 +	__asm call NKern__Unlock
   1.348 +
   1.349 +	__asm call NThread__HandleException
   1.350 +
   1.351 +	__asm add esp, 28
   1.352 +	__asm pop eax
   1.353 +	__asm pop ecx
   1.354 +	__asm pop edx
   1.355 +	__asm pop ebx
   1.356 +	__asm pop esi		// original ESP - ignore
   1.357 +	__asm pop esi
   1.358 +	__asm pop edi
   1.359 +	__asm pop ebp		// original SS - ignore
   1.360 +	__asm pop ds
   1.361 +	__asm pop es
   1.362 +	__asm pop fs
   1.363 +	__asm pop gs
   1.364 +	__asm popfd
   1.365 +	__asm pop ebp		// original CS - ignore
   1.366 +	__asm pop ebp
   1.367 +	__asm ret
   1.368 +	}
   1.369 +
   1.370 +LONG WINAPI NThread::ExceptionFilter(EXCEPTION_POINTERS* aExc)
   1.371 +//
   1.372 +// Filter wrapper for main Win32 exception handler
   1.373 +//
   1.374 +	{
   1.375 +	LONG ret = EXCEPTION_CONTINUE_SEARCH;
   1.376 +
   1.377 +	switch (ExceptionHandler(aExc->ExceptionRecord, aExc->ContextRecord))
   1.378 +		{
   1.379 +		case ExceptionContinueExecution:
   1.380 +			{
   1.381 +			ret = EXCEPTION_CONTINUE_EXECUTION;
   1.382 +			}
   1.383 +			break;
   1.384 +		case ExceptionContinueSearch:
   1.385 +		default:
   1.386 +			{
   1.387 +			}
   1.388 +			break;
   1.389 +		}
   1.390 +
   1.391 +	return ret;
   1.392 +	}
   1.393 +
   1.394 +// From e32/commmon/win32/seh.cpp
   1.395 +extern DWORD CallFinalSEHHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext);
   1.396 +
   1.397 +extern void DivertHook();
   1.398 +
   1.399 +DWORD NThread::ExceptionHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext)
   1.400 +//
   1.401 +// Win32 exception handler for EPOC threads
   1.402 +//
   1.403 +	{
   1.404 +	if (aException->ExceptionCode == EXCEPTION_BREAKPOINT)
   1.405 +		{
   1.406 +		// Hardcoded breakpoint
   1.407 +		//
   1.408 +		// Jump directly to NT's default unhandled exception handler which will
   1.409 +		// either display a dialog, directly invoke the JIT debugger or do nothing
   1.410 +		// dependent upon the AeDebug and ErrorMode registry settings.
   1.411 +		//
   1.412 +		// Note this handler is always installed on the SEH chain and is always
   1.413 +		// the last handler on this chain, as it is installed by NT in kernel32.dll
   1.414 +		// before invoking the Win32 thread function.
   1.415 +		return CallFinalSEHHandler(aException, aContext);
   1.416 +		}
   1.417 +
   1.418 +	// deal with conflict between preemption and diversion
   1.419 +	// the diversion will have been applied to the pre-exception context, not
   1.420 +	// the current context, and thus will get 'lost'. Wake-up of a pre-empted
   1.421 +	// thread with a diversion will not unlock the kernel, so need to deal with
   1.422 +	// the possibility that the kernel may be locked if a diversion exists
   1.423 +
   1.424 +	NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread);
   1.425 +	if (me.iDiverted && me.iDivert)
   1.426 +		{
   1.427 +		// The thread is being forced to exit - run the diversion outside of Win32 exception handler
   1.428 +		__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
   1.429 +		aContext->Eip = (TUint32)&DivertHook;
   1.430 +		}
   1.431 +	else
   1.432 +		{
   1.433 +		if (me.iDiverted)
   1.434 +			{
   1.435 +			// The thread is being prodded to pick up its callbacks.  This will happen when the
   1.436 +			// exception handler calls LeaveKernel(), so we can remove the diversion
   1.437 +			__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
   1.438 +			if (aException->ExceptionAddress == &DivertHook)
   1.439 +				aException->ExceptionAddress = me.iDivertReturn;
   1.440 +			me.iDiverted = EFalse;
   1.441 +			me.iDivertReturn = NULL;
   1.442 +			EnterKernel(FALSE);
   1.443 +			}
   1.444 +		else
   1.445 +			{
   1.446 +			EnterKernel();
   1.447 +			TheScheduler.iKernCSLocked = 1;	// prevent pre-emption
   1.448 +			}
   1.449 +		
   1.450 +		// If the kernel was already locked, this will be detected in the next stage handler
   1.451 +		// run 2nd stage handler outside of Win32 exception context
   1.452 +		Win32ExcAddress = aException->ExceptionAddress;
   1.453 +		Win32ExcDataAddress = (TAny*)aException->ExceptionInformation[1];
   1.454 +		Win32ExcCode = aException->ExceptionCode;
   1.455 +		aContext->Eip = (TUint32)&Exception;
   1.456 +		}
   1.457 +	return ExceptionContinueExecution;
   1.458 +	}
   1.459 +
   1.460 +void NThread::Diverted()
   1.461 +//
   1.462 +// Forced diversion go through here, in order to 'enter' the kernel
   1.463 +//
   1.464 +	{
   1.465 +	NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread);
   1.466 +	__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
   1.467 +	__NK_ASSERT_ALWAYS(me.iDiverted);
   1.468 +	NThread::TDivert divert = me.iDivert;
   1.469 +	me.iDiverted = EFalse;
   1.470 +	me.iDivert = NULL;
   1.471 +	me.iDivertReturn = NULL;
   1.472 +	EnterKernel(FALSE);
   1.473 +	if (divert)
   1.474 +		divert();	// does not return
   1.475 +	NKern::Unlock();
   1.476 +	LeaveKernel();
   1.477 +	}
   1.478 +
   1.479 +void NThread__Diverted()
   1.480 +	{
   1.481 +	NThread::Diverted();
   1.482 +	}
   1.483 +
   1.484 +__NAKED__ void DivertHook()
   1.485 +	{
   1.486 +	// The stack frame is set up like this:
   1.487 +	//
   1.488 +	//		| return address |
   1.489 +	//		| frame pointer  |
   1.490 +	//		| flags			 |
   1.491 +	//		| saved eax		 |
   1.492 +	//		| saved ecx		 |
   1.493 +	//      | saved edx		 |
   1.494 +	//		
   1.495 +	__asm push eax					// reserve word for return address
   1.496 +	__asm push ebp
   1.497 +	__asm mov ebp, esp 
   1.498 +	__asm pushfd
   1.499 +	__asm push eax
   1.500 +	__asm push ecx
   1.501 +	__asm push edx
   1.502 +	__asm mov eax, [TheScheduler.iCurrentThread]
   1.503 +	__asm mov eax, [eax]NThread.iDivertReturn
   1.504 +	__asm mov [esp + 20], eax		// store return address
   1.505 +	__asm call NThread__Diverted
   1.506 +	__asm pop edx
   1.507 +	__asm pop ecx
   1.508 +	__asm pop eax
   1.509 +	__asm popfd
   1.510 +	__asm pop ebp
   1.511 +	__asm ret
   1.512 +	}
   1.513 +
   1.514 +
   1.515 +void NThread::ApplyDiversion()
   1.516 +	{
   1.517 +	// Called with interrupts disabled and kernel locked
   1.518 +	__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
   1.519 +	if (iDiverted)
   1.520 +		return;
   1.521 +	CONTEXT c;
   1.522 +	c.ContextFlags=CONTEXT_FULL;
   1.523 +	GetThreadContext(iWinThread, &c);
   1.524 +	__NK_ASSERT_ALWAYS(iDivertReturn == NULL);
   1.525 +	iDivertReturn = (TAny*)c.Eip;
   1.526 +	c.Eip=(TUint32)&DivertHook;
   1.527 +	SetThreadContext(iWinThread, &c);
   1.528 +	iDiverted = ETrue;
   1.529 +	}
   1.530 +
   1.531 +void NThread::Divert(TDivert aDivert)
   1.532 +//
   1.533 +// Divert the thread from its current path
   1.534 +// The diversion function is called with the kernel locked and interrupts enabled
   1.535 +//
   1.536 +	{
   1.537 +	iDivert = aDivert;
   1.538 +	if (iWakeup == EResume)
   1.539 +		iWakeup = EResumeDiverted;
   1.540 +	else
   1.541 +		__NK_ASSERT_ALWAYS(iWakeup == ERelease);
   1.542 +	}
   1.543 +
   1.544 +void NThread::ExitSync()
   1.545 +//
   1.546 +// Diversion used to terminate 'stillborn' threads.
   1.547 +// On entry, kernel is locked, interrupts are enabled and we hold an interlock mutex
   1.548 +//
   1.549 +	{
   1.550 +	NThreadBase& me=*TheScheduler.iCurrentThread;
   1.551 +	me.iHeldFastMutex->Signal();	// release the interlock
   1.552 +	me.iNState=EDead;				// mark ourselves as dead which will take thread out of scheduler
   1.553 +	TheScheduler.Remove(&me);
   1.554 +	RescheduleNeeded();
   1.555 +	TScheduler::Reschedule();	// this won't return
   1.556 +	FAULT();
   1.557 +	}
   1.558 +
   1.559 +void NThread::Stillborn()
   1.560 +//
   1.561 +// Called if the new thread creation was aborted - so it will not be killed in the usual manner
   1.562 +//
   1.563 +// This function needs to exit the thread synchronously as on return we will destroy the thread control block
   1.564 +// Thus wee need to use an interlock that ensure that the target thread runs the exit handler before we continue
   1.565 +//
   1.566 +	{
   1.567 +	// check if the Win32 thread was created
   1.568 +	if (!iWinThread)
   1.569 +		return;
   1.570 +
   1.571 +	NKern::Lock();
   1.572 +	Divert(&ExitSync);
   1.573 +	ForceResume();
   1.574 +	// create and assign mutex to stillborn thread
   1.575 +	NFastMutex interlock;
   1.576 +	interlock.iHoldingThread=this;
   1.577 +	iHeldFastMutex=&interlock;
   1.578 +	interlock.Wait();			// interlock on thread exit handler
   1.579 +	interlock.Signal();
   1.580 +	NKern::Unlock();
   1.581 +	}
   1.582 +
   1.583 +void NThread::ExitAsync()
   1.584 +//
   1.585 +// Diversion used to terminate 'killed' threads.
   1.586 +// On entry, kernel is locked and interrupts are enabled
   1.587 +//
   1.588 +	{
   1.589 +	NThreadBase& me = *TheScheduler.iCurrentThread;
   1.590 +	me.iCsCount = 0;
   1.591 +	__NK_ASSERT_ALWAYS(static_cast<NThread&>(me).iInKernel>0);
   1.592 +	me.Exit();
   1.593 +	}
   1.594 +
   1.595 +void NThreadBase::OnKill()
   1.596 +	{
   1.597 +	}
   1.598 +
   1.599 +void NThreadBase::OnExit()
   1.600 +	{
   1.601 +	}
   1.602 +
   1.603 +inline void NThread::DoForceExit()
   1.604 +	{
   1.605 +	__NK_ASSERT_DEBUG(TheScheduler.iKernCSLocked);
   1.606 +//
   1.607 +	Divert(&ExitAsync);
   1.608 +	}
   1.609 +
   1.610 +void NThreadBase::ForceExit()
   1.611 +//
   1.612 +// Called to force the thread to exit when it resumes
   1.613 +//
   1.614 +	{
   1.615 +	static_cast<NThread*>(this)->DoForceExit();
   1.616 +	}
   1.617 +
   1.618 +//
   1.619 +// We need a global lock in the emulator to avoid scheduling reentrancy problems with the host
   1.620 +// in particular, some host API calls acquire host mutexes, preempting such services results
   1.621 +// in suspension of those threads which can cause deadlock if another thread requires that host
   1.622 +// mutex.
   1.623 +//
   1.624 +// Because thread dreaction and code loading also require the same underlying mutex (used
   1.625 +// by NT to protect DLL entrypoint calling), this would be rather complex with a fast mutex.
   1.626 +// For now, keep it simple and use the preemption lock. Note that this means that the
   1.627 +// MS timer DFC may be significantly delayed when loading large DLL trees, for example.
   1.628 +//
   1.629 +
   1.630 +void SchedulerLock()
   1.631 +//
   1.632 +// Acquire the global lock. May be called before scheduler running, so handle that case
   1.633 +//
   1.634 +	{
   1.635 +	if (TheScheduler.iCurrentThread)
   1.636 +		{
   1.637 +		EnterKernel();
   1.638 +		NKern::Lock();
   1.639 +		}
   1.640 +	}
   1.641 +
   1.642 +void SchedulerUnlock()
   1.643 +//
   1.644 +// Release the global lock. May be called before scheduler running, so handle that case
   1.645 +//
   1.646 +	{
   1.647 +	if (TheScheduler.iCurrentThread)
   1.648 +		{
   1.649 +		NKern::Unlock();
   1.650 +		LeaveKernel();
   1.651 +		}
   1.652 +	}
   1.653 +