kernel/eka/nkern/win32/ncthrd.cpp
changeset 0 a41df078684a
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\ncthrd.cpp
       
    15 // 
       
    16 //
       
    17 
       
    18 // NThreadBase member data
       
    19 #define __INCLUDE_NTHREADBASE_DEFINES__
       
    20 
       
    21 #include "nk_priv.h"
       
    22 #include <emulator.h>
       
    23 
       
    24 extern "C" void ExcFault(TAny*);
       
    25 
       
    26 // initial Win32 thread stack size
       
    27 const TInt KInitialStackSize = 0x1000;
       
    28 
       
    29 // maximum size of the parameter block passed to a new thread
       
    30 const TInt KMaxParameterBlock = 512;
       
    31 
       
    32 // data passed to new thread to enable hand-off of the parameter block
       
    33 struct SCreateThread
       
    34 	{
       
    35 	const SNThreadCreateInfo* iInfo;
       
    36 	NFastMutex iHandoff;
       
    37 	};
       
    38 
       
    39 /**
       
    40  * Set the Win32 thread priority based on the thread type.
       
    41  * Interrupt/Event threads must be able to preempt normal nKern threads,
       
    42  * so they get a higher priority.
       
    43  */
       
    44 static void SetPriority(HANDLE aThread, TEmulThreadType aType)
       
    45 	{
       
    46 	TInt p;
       
    47 	switch (aType)
       
    48 		{
       
    49 	default:
       
    50 		FAULT();
       
    51 	case EThreadEvent:
       
    52 		p = THREAD_PRIORITY_ABOVE_NORMAL;
       
    53 		break;
       
    54 	case EThreadNKern:
       
    55 		p = THREAD_PRIORITY_NORMAL;
       
    56 		break;
       
    57 		}
       
    58 
       
    59 	__NK_ASSERT_ALWAYS(SetThreadPriority(aThread, p));
       
    60 	SetThreadPriorityBoost(aThread, TRUE);		// disable priority boost (for NT)
       
    61 	}
       
    62 
       
    63 
       
    64 /**	Create a Win32 thread for use in the emulator.
       
    65 
       
    66 	@param	aType Type of thread (Event or NKern) - determines Win32 priority
       
    67 	@param	aThreadFunc Entry point of thread
       
    68 	@param	aPtr Argument passed to entry point
       
    69 	@param	aRun TRUE if thread should be resumed immediately
       
    70 	@return	The Win32 handle to the thread, 0 if an error occurred
       
    71 
       
    72 	@pre    Call either in thread context.
       
    73 	@pre	Do not call from bare Win32 threads.
       
    74 
       
    75 	@see TEmulThreadType
       
    76  */
       
    77 EXPORT_C HANDLE CreateWin32Thread(TEmulThreadType aType, LPTHREAD_START_ROUTINE aThreadFunc, LPVOID aPtr, TBool aRun)
       
    78 	{
       
    79 	__NK_ASSERT_DEBUG(!TheScheduler.iCurrentThread || NKern::CurrentContext() == NKern::EThread);
       
    80 
       
    81 	__LOCK_HOST;
       
    82 	
       
    83 	DWORD id;
       
    84 	HANDLE handle = CreateThread(NULL , KInitialStackSize, aThreadFunc, aPtr, CREATE_SUSPENDED, &id);
       
    85 	if (handle)
       
    86 		{
       
    87 		SetPriority(handle, aType);
       
    88 		if (aRun)
       
    89 			ResumeThread(handle);
       
    90 		}
       
    91 	return handle;
       
    92 	}
       
    93 
       
    94 
       
    95 /** Set some global properties of the emulator
       
    96 	Called by the Win32 base port during boot.
       
    97 
       
    98 	@param	aTrace TRUE means trace Win32 thread ID for every thread created
       
    99 	@param	aSingleCpu TRUE means lock the emulator process to a single CPU
       
   100 
       
   101 	@internalTechnology
       
   102  */
       
   103 EXPORT_C void NThread::SetProperties(TBool aTrace, TInt aSingleCpu)
       
   104 	{
       
   105 	Win32TraceThreadId = aTrace;
       
   106 	Win32SingleCpu = aSingleCpu;
       
   107 	}
       
   108 
       
   109 #if defined(__CW32__) && __MWERKS__ < 0x3200
       
   110 DWORD NThread__ExceptionHandler(EXCEPTION_RECORD* aException, TAny* /*aRegistrationRecord*/, CONTEXT* aContext)
       
   111 //
       
   112 // Hook into exception handling for old version of CW
       
   113 //
       
   114 	{
       
   115 	return NThread::ExceptionHandler(aException, aContext);
       
   116 	}
       
   117 #endif // old __CW32__
       
   118 
       
   119 DWORD WINAPI NThread::StartThread(LPVOID aParam)
       
   120 //
       
   121 // Win32 thread function for nKern threads.
       
   122 //
       
   123 // The thread first enters this function after the nScheduler has resumed
       
   124 // it, following the context switch induced by the hand-off mutex.
       
   125 //
       
   126 // The parameter block for this thread needs to be copied into its
       
   127 // own context, before releasing the mutex and handing control back to
       
   128 // the creating thread.
       
   129 //
       
   130 	{
       
   131 	SCreateThread* init = static_cast<SCreateThread*>(aParam);
       
   132 	NThread& me=*static_cast<NThread*>(init->iHandoff.iHoldingThread);
       
   133 	me.iWinThreadId = GetCurrentThreadId();
       
   134 	SchedulerRegister(me);
       
   135 #ifdef BTRACE_FAST_MUTEX
       
   136 	BTraceContext4(BTrace::EFastMutex,BTrace::EFastMutexWait,&init->iHandoff);
       
   137 #endif
       
   138 	NKern::Unlock();
       
   139 
       
   140 #if defined(__CW32__) && __MWERKS__ < 0x3200
       
   141 	// intercept the win32 exception mechanism manually
       
   142     asm {
       
   143     	push	ebp
       
   144     	mov		eax, -1
       
   145     	push	eax
       
   146     	push	eax
       
   147     	push	offset NThread__ExceptionHandler
       
   148     	push	fs:[0]
       
   149     	mov		fs:[0], esp
       
   150 
       
   151 		// realign the stack
       
   152     	sub		esp, 0x20
       
   153     	and		esp, ~0x1f
       
   154 		}
       
   155 #else // ! old __CW32__
       
   156 	// intercept win32 exceptions in a debuggabble way
       
   157 __try {
       
   158 #endif // old __CW32__
       
   159 
       
   160 	// save the thread entry point and parameter block
       
   161 	const SNThreadCreateInfo& info = *init->iInfo;
       
   162 	TUint8 parameterBlock[KMaxParameterBlock];
       
   163 	TAny* parameter=(TAny*)info.iParameterBlock;
       
   164 	if (info.iParameterBlockSize)
       
   165 		{
       
   166 		__NK_ASSERT_DEBUG(TUint(info.iParameterBlockSize)<=TUint(KMaxParameterBlock));
       
   167 		parameter=parameterBlock;
       
   168 		memcpy(parameterBlock,info.iParameterBlock,info.iParameterBlockSize);
       
   169 		}
       
   170 	NThreadFunction threadFunction=info.iFunction;
       
   171 
       
   172 	// Calculate stack base
       
   173 	me.iUserStackBase = (((TLinAddr)&parameterBlock)+0xfff)&~0xfff; // base address of stack
       
   174 
       
   175 	// some useful diagnostics for debugging
       
   176 	if (Win32TraceThreadId)
       
   177 		KPrintf("Thread %T created @ 0x%x - Win32 Thread ID 0x%x",init->iHandoff.iHoldingThread,init->iHandoff.iHoldingThread,GetCurrentThreadId());
       
   178 
       
   179 #ifdef MONITOR_THREAD_CPU_TIME
       
   180 	me.iLastStartTime = 0;  // Don't count NThread setup in cpu time
       
   181 #endif
       
   182  
       
   183 	// start-up complete, release the handoff mutex, which will re-suspend us
       
   184 	NKern::FMSignal(&init->iHandoff);
       
   185 
       
   186 	// thread has been resumed: invoke the thread function
       
   187 	threadFunction(parameter);
       
   188 
       
   189 #if !defined(__CW32__) || __MWERKS__ >= 0x3200
       
   190 	// handle win32 exceptions
       
   191 } __except (ExceptionFilter(GetExceptionInformation())) {
       
   192 	// Do nothing - filter does all the work and hooks
       
   193 	// into EPOC h/w exception mechanism if necessary
       
   194 	// by thread diversion
       
   195 }
       
   196 #endif // !old __CW32__
       
   197 
       
   198 	NKern::Exit();
       
   199 
       
   200 	return 0;
       
   201 	}
       
   202 
       
   203 static HANDLE InitThread()
       
   204 //
       
   205 // Set up the initial thread and return the thread handle
       
   206 //
       
   207 	{
       
   208 	HANDLE p = GetCurrentProcess();
       
   209 	HANDLE me;
       
   210 	__NK_ASSERT_ALWAYS(DuplicateHandle(p, GetCurrentThread(), p, &me, 0, FALSE, DUPLICATE_SAME_ACCESS));
       
   211 	SetPriority(me, EThreadNKern);
       
   212 	return me;
       
   213 	}
       
   214 
       
   215 TInt NThread::Create(SNThreadCreateInfo& aInfo, TBool aInitial)
       
   216 	{
       
   217 	iWinThread = NULL;
       
   218 	iWinThreadId = 0;
       
   219 	iScheduleLock = NULL;
       
   220 	iInKernel = 1;
       
   221 	iDivert = NULL;
       
   222 	iWakeup = aInitial ? ERelease : EResumeLocked;	// mark new threads as created (=> win32 suspend)
       
   223 
       
   224 	TInt r=NThreadBase::Create(aInfo,aInitial);
       
   225 	if (r!=KErrNone)
       
   226 		return r;
       
   227 
       
   228 	// the rest has to be all or nothing, we must complete it
       
   229 	iScheduleLock = CreateEventA(NULL, FALSE, FALSE, NULL);
       
   230 	if (iScheduleLock == NULL)
       
   231 		return Emulator::LastError();
       
   232 
       
   233 	if (aInitial)
       
   234 		{
       
   235 		iWinThread = InitThread();
       
   236 		FastCounterInit();
       
   237 #ifdef MONITOR_THREAD_CPU_TIME
       
   238 		iLastStartTime = NKern::FastCounter();
       
   239 #endif
       
   240 		iUserStackBase = (((TLinAddr)&r)+0xfff)&~0xfff; // base address of stack
       
   241 		SchedulerInit(*this);
       
   242 		return KErrNone;
       
   243 		}
       
   244 
       
   245 	// create the thread proper
       
   246 	//
       
   247 	SCreateThread start;
       
   248 	start.iInfo = &aInfo;
       
   249 
       
   250 	iWinThread = CreateWin32Thread(EThreadNKern, &StartThread, &start, FALSE);
       
   251 	if (iWinThread == NULL)
       
   252 		{
       
   253 		r = Emulator::LastError();
       
   254 		CloseHandle(iScheduleLock);
       
   255 		return r;
       
   256 		}
       
   257 
       
   258 #ifdef BTRACE_THREAD_IDENTIFICATION
       
   259 	BTrace4(BTrace::EThreadIdentification,BTrace::ENanoThreadCreate,this);
       
   260 #endif
       
   261 	// switch to the new thread to hand over the parameter block
       
   262 	NKern::Lock();
       
   263 	ForceResume();	// mark the thread as ready
       
   264 	// give the thread ownership of the handoff mutex
       
   265 	start.iHandoff.iHoldingThread = this;
       
   266 	iHeldFastMutex = &start.iHandoff;
       
   267 	Suspend(1);		// will defer as holding a fast mutex (implicit critical section)
       
   268 	// do the hand-over
       
   269 	start.iHandoff.Wait();
       
   270 	start.iHandoff.Signal();
       
   271 	NKern::Unlock();
       
   272 
       
   273 	return KErrNone;
       
   274 	}
       
   275 
       
   276 void NThread__HandleException(TWin32ExcInfo aExc)
       
   277 //
       
   278 // Final stage NKern exception handler.
       
   279 //
       
   280 // Check for a fatal exception when the kernel is locked
       
   281 //
       
   282 // Note that the parameter struct is passed by value, this allows for
       
   283 // direct access to the exception context created on the call stack by
       
   284 // NThread::Exception().
       
   285 //
       
   286 	{
       
   287 	if (TheScheduler.iKernCSLocked)
       
   288 		ExcFault(&aExc);
       
   289 
       
   290 	// Complete the exception data. Note that the call to EnterKernel() in
       
   291 	// ExceptionFilter() will have incremented iInKernel after the exception
       
   292 	// occurred.
       
   293 	NThread* me = static_cast<NThread*>(TheScheduler.iCurrentThread);
       
   294 	__NK_ASSERT_DEBUG(me->iInKernel);
       
   295 	aExc.iFlags = me->iInKernel == 1 ? 0 : TWin32ExcInfo::EExcInKernel;
       
   296 	aExc.iHandler = NULL;
       
   297 
       
   298 	// run NThread exception handler in 'kernel' mode
       
   299 	me->iHandlers->iExceptionHandler(&aExc, me);
       
   300 	LeaveKernel();
       
   301 
       
   302 	// If a 'user' handler is set by the kernel handler, run it
       
   303 	if (aExc.iHandler)
       
   304 		aExc.iHandler(aExc.iParam[0], aExc.iParam[1]);
       
   305 	}
       
   306 
       
   307 void NKern__Unlock()
       
   308 //
       
   309 // CW asm ICE workaround
       
   310 //
       
   311 	{
       
   312 	NKern::Unlock();
       
   313 	}
       
   314 
       
   315 __NAKED__ void NThread::Exception()
       
   316 //
       
   317 // Trampoline to nKern exception handler
       
   318 // must preserve all registers in the structure defined by TWin32Exc
       
   319 //
       
   320 	{
       
   321 	// this is the TWin32Exc structure
       
   322 	__asm push Win32ExcAddress			// save return address followed by EBP first to help debugger
       
   323 	__asm push ebp
       
   324 	__asm mov ebp, esp
       
   325 	__asm push cs
       
   326 	__asm pushfd
       
   327 	__asm push gs
       
   328 	__asm push fs
       
   329 	__asm push es
       
   330 	__asm push ds
       
   331 	__asm push ss
       
   332 	__asm push edi
       
   333 	__asm push esi
       
   334 	__asm lea esi, [ebp+8]
       
   335 	__asm push esi		// original esp
       
   336 	__asm push ebx
       
   337 	__asm push edx
       
   338 	__asm push ecx
       
   339 	__asm push eax
       
   340 	__asm push Win32ExcDataAddress
       
   341 	__asm push Win32ExcCode
       
   342 	__asm sub esp, 20	// struct init completed by NThread__HandleException()
       
   343 
       
   344 	__asm call NKern__Unlock
       
   345 
       
   346 	__asm call NThread__HandleException
       
   347 
       
   348 	__asm add esp, 28
       
   349 	__asm pop eax
       
   350 	__asm pop ecx
       
   351 	__asm pop edx
       
   352 	__asm pop ebx
       
   353 	__asm pop esi		// original ESP - ignore
       
   354 	__asm pop esi
       
   355 	__asm pop edi
       
   356 	__asm pop ebp		// original SS - ignore
       
   357 	__asm pop ds
       
   358 	__asm pop es
       
   359 	__asm pop fs
       
   360 	__asm pop gs
       
   361 	__asm popfd
       
   362 	__asm pop ebp		// original CS - ignore
       
   363 	__asm pop ebp
       
   364 	__asm ret
       
   365 	}
       
   366 
       
   367 LONG WINAPI NThread::ExceptionFilter(EXCEPTION_POINTERS* aExc)
       
   368 //
       
   369 // Filter wrapper for main Win32 exception handler
       
   370 //
       
   371 	{
       
   372 	LONG ret = EXCEPTION_CONTINUE_SEARCH;
       
   373 
       
   374 	switch (ExceptionHandler(aExc->ExceptionRecord, aExc->ContextRecord))
       
   375 		{
       
   376 		case ExceptionContinueExecution:
       
   377 			{
       
   378 			ret = EXCEPTION_CONTINUE_EXECUTION;
       
   379 			}
       
   380 			break;
       
   381 		case ExceptionContinueSearch:
       
   382 		default:
       
   383 			{
       
   384 			}
       
   385 			break;
       
   386 		}
       
   387 
       
   388 	return ret;
       
   389 	}
       
   390 
       
   391 // From e32/commmon/win32/seh.cpp
       
   392 extern DWORD CallFinalSEHHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext);
       
   393 
       
   394 extern void DivertHook();
       
   395 
       
   396 DWORD NThread::ExceptionHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext)
       
   397 //
       
   398 // Win32 exception handler for EPOC threads
       
   399 //
       
   400 	{
       
   401 	if (aException->ExceptionCode == EXCEPTION_BREAKPOINT)
       
   402 		{
       
   403 		// Hardcoded breakpoint
       
   404 		//
       
   405 		// Jump directly to NT's default unhandled exception handler which will
       
   406 		// either display a dialog, directly invoke the JIT debugger or do nothing
       
   407 		// dependent upon the AeDebug and ErrorMode registry settings.
       
   408 		//
       
   409 		// Note this handler is always installed on the SEH chain and is always
       
   410 		// the last handler on this chain, as it is installed by NT in kernel32.dll
       
   411 		// before invoking the Win32 thread function.
       
   412 		return CallFinalSEHHandler(aException, aContext);
       
   413 		}
       
   414 
       
   415 	// deal with conflict between preemption and diversion
       
   416 	// the diversion will have been applied to the pre-exception context, not
       
   417 	// the current context, and thus will get 'lost'. Wake-up of a pre-empted
       
   418 	// thread with a diversion will not unlock the kernel, so need to deal with
       
   419 	// the possibility that the kernel may be locked if a diversion exists
       
   420 
       
   421 	NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread);
       
   422 	if (me.iDiverted && me.iDivert)
       
   423 		{
       
   424 		// The thread is being forced to exit - run the diversion outside of Win32 exception handler
       
   425 		__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
       
   426 		aContext->Eip = (TUint32)&DivertHook;
       
   427 		}
       
   428 	else
       
   429 		{
       
   430 		if (me.iDiverted)
       
   431 			{
       
   432 			// The thread is being prodded to pick up its callbacks.  This will happen when the
       
   433 			// exception handler calls LeaveKernel(), so we can remove the diversion
       
   434 			__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
       
   435 			if (aException->ExceptionAddress == &DivertHook)
       
   436 				aException->ExceptionAddress = me.iDivertReturn;
       
   437 			me.iDiverted = EFalse;
       
   438 			me.iDivertReturn = NULL;
       
   439 			EnterKernel(FALSE);
       
   440 			}
       
   441 		else
       
   442 			{
       
   443 			EnterKernel();
       
   444 			TheScheduler.iKernCSLocked = 1;	// prevent pre-emption
       
   445 			}
       
   446 		
       
   447 		// If the kernel was already locked, this will be detected in the next stage handler
       
   448 		// run 2nd stage handler outside of Win32 exception context
       
   449 		Win32ExcAddress = aException->ExceptionAddress;
       
   450 		Win32ExcDataAddress = (TAny*)aException->ExceptionInformation[1];
       
   451 		Win32ExcCode = aException->ExceptionCode;
       
   452 		aContext->Eip = (TUint32)&Exception;
       
   453 		}
       
   454 	return ExceptionContinueExecution;
       
   455 	}
       
   456 
       
   457 void NThread::Diverted()
       
   458 //
       
   459 // Forced diversion go through here, in order to 'enter' the kernel
       
   460 //
       
   461 	{
       
   462 	NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread);
       
   463 	__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
       
   464 	__NK_ASSERT_ALWAYS(me.iDiverted);
       
   465 	NThread::TDivert divert = me.iDivert;
       
   466 	me.iDiverted = EFalse;
       
   467 	me.iDivert = NULL;
       
   468 	me.iDivertReturn = NULL;
       
   469 	EnterKernel(FALSE);
       
   470 	if (divert)
       
   471 		divert();	// does not return
       
   472 	NKern::Unlock();
       
   473 	LeaveKernel();
       
   474 	}
       
   475 
       
   476 void NThread__Diverted()
       
   477 	{
       
   478 	NThread::Diverted();
       
   479 	}
       
   480 
       
   481 __NAKED__ void DivertHook()
       
   482 	{
       
   483 	// The stack frame is set up like this:
       
   484 	//
       
   485 	//		| return address |
       
   486 	//		| frame pointer  |
       
   487 	//		| flags			 |
       
   488 	//		| saved eax		 |
       
   489 	//		| saved ecx		 |
       
   490 	//      | saved edx		 |
       
   491 	//		
       
   492 	__asm push eax					// reserve word for return address
       
   493 	__asm push ebp
       
   494 	__asm mov ebp, esp 
       
   495 	__asm pushfd
       
   496 	__asm push eax
       
   497 	__asm push ecx
       
   498 	__asm push edx
       
   499 	__asm mov eax, [TheScheduler.iCurrentThread]
       
   500 	__asm mov eax, [eax]NThread.iDivertReturn
       
   501 	__asm mov [esp + 20], eax		// store return address
       
   502 	__asm call NThread__Diverted
       
   503 	__asm pop edx
       
   504 	__asm pop ecx
       
   505 	__asm pop eax
       
   506 	__asm popfd
       
   507 	__asm pop ebp
       
   508 	__asm ret
       
   509 	}
       
   510 
       
   511 
       
   512 void NThread::ApplyDiversion()
       
   513 	{
       
   514 	// Called with interrupts disabled and kernel locked
       
   515 	__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
       
   516 	if (iDiverted)
       
   517 		return;
       
   518 	CONTEXT c;
       
   519 	c.ContextFlags=CONTEXT_FULL;
       
   520 	GetThreadContext(iWinThread, &c);
       
   521 	__NK_ASSERT_ALWAYS(iDivertReturn == NULL);
       
   522 	iDivertReturn = (TAny*)c.Eip;
       
   523 	c.Eip=(TUint32)&DivertHook;
       
   524 	SetThreadContext(iWinThread, &c);
       
   525 	iDiverted = ETrue;
       
   526 	}
       
   527 
       
   528 void NThread::Divert(TDivert aDivert)
       
   529 //
       
   530 // Divert the thread from its current path
       
   531 // The diversion function is called with the kernel locked and interrupts enabled
       
   532 //
       
   533 	{
       
   534 	iDivert = aDivert;
       
   535 	if (iWakeup == EResume)
       
   536 		iWakeup = EResumeDiverted;
       
   537 	else
       
   538 		__NK_ASSERT_ALWAYS(iWakeup == ERelease);
       
   539 	}
       
   540 
       
   541 void NThread::ExitSync()
       
   542 //
       
   543 // Diversion used to terminate 'stillborn' threads.
       
   544 // On entry, kernel is locked, interrupts are enabled and we hold an interlock mutex
       
   545 //
       
   546 	{
       
   547 	NThreadBase& me=*TheScheduler.iCurrentThread;
       
   548 	me.iHeldFastMutex->Signal();	// release the interlock
       
   549 	me.iNState=EDead;				// mark ourselves as dead which will take thread out of scheduler
       
   550 	TheScheduler.Remove(&me);
       
   551 	RescheduleNeeded();
       
   552 	TScheduler::Reschedule();	// this won't return
       
   553 	FAULT();
       
   554 	}
       
   555 
       
   556 void NThread::Stillborn()
       
   557 //
       
   558 // Called if the new thread creation was aborted - so it will not be killed in the usual manner
       
   559 //
       
   560 // This function needs to exit the thread synchronously as on return we will destroy the thread control block
       
   561 // Thus wee need to use an interlock that ensure that the target thread runs the exit handler before we continue
       
   562 //
       
   563 	{
       
   564 	// check if the Win32 thread was created
       
   565 	if (!iWinThread)
       
   566 		return;
       
   567 
       
   568 	NKern::Lock();
       
   569 	Divert(&ExitSync);
       
   570 	ForceResume();
       
   571 	// create and assign mutex to stillborn thread
       
   572 	NFastMutex interlock;
       
   573 	interlock.iHoldingThread=this;
       
   574 	iHeldFastMutex=&interlock;
       
   575 	interlock.Wait();			// interlock on thread exit handler
       
   576 	interlock.Signal();
       
   577 	NKern::Unlock();
       
   578 	}
       
   579 
       
   580 void NThread::ExitAsync()
       
   581 //
       
   582 // Diversion used to terminate 'killed' threads.
       
   583 // On entry, kernel is locked and interrupts are enabled
       
   584 //
       
   585 	{
       
   586 	NThreadBase& me = *TheScheduler.iCurrentThread;
       
   587 	me.iCsCount = 0;
       
   588 	__NK_ASSERT_ALWAYS(static_cast<NThread&>(me).iInKernel>0);
       
   589 	me.Exit();
       
   590 	}
       
   591 
       
   592 void NThreadBase::OnKill()
       
   593 	{
       
   594 	}
       
   595 
       
   596 void NThreadBase::OnExit()
       
   597 	{
       
   598 	}
       
   599 
       
   600 inline void NThread::DoForceExit()
       
   601 	{
       
   602 	__NK_ASSERT_DEBUG(TheScheduler.iKernCSLocked);
       
   603 //
       
   604 	Divert(&ExitAsync);
       
   605 	}
       
   606 
       
   607 void NThreadBase::ForceExit()
       
   608 //
       
   609 // Called to force the thread to exit when it resumes
       
   610 //
       
   611 	{
       
   612 	static_cast<NThread*>(this)->DoForceExit();
       
   613 	}
       
   614 
       
   615 //
       
   616 // We need a global lock in the emulator to avoid scheduling reentrancy problems with the host
       
   617 // in particular, some host API calls acquire host mutexes, preempting such services results
       
   618 // in suspension of those threads which can cause deadlock if another thread requires that host
       
   619 // mutex.
       
   620 //
       
   621 // Because thread dreaction and code loading also require the same underlying mutex (used
       
   622 // by NT to protect DLL entrypoint calling), this would be rather complex with a fast mutex.
       
   623 // For now, keep it simple and use the preemption lock. Note that this means that the
       
   624 // MS timer DFC may be significantly delayed when loading large DLL trees, for example.
       
   625 //
       
   626 
       
   627 void SchedulerLock()
       
   628 //
       
   629 // Acquire the global lock. May be called before scheduler running, so handle that case
       
   630 //
       
   631 	{
       
   632 	if (TheScheduler.iCurrentThread)
       
   633 		{
       
   634 		EnterKernel();
       
   635 		NKern::Lock();
       
   636 		}
       
   637 	}
       
   638 
       
   639 void SchedulerUnlock()
       
   640 //
       
   641 // Release the global lock. May be called before scheduler running, so handle that case
       
   642 //
       
   643 	{
       
   644 	if (TheScheduler.iCurrentThread)
       
   645 		{
       
   646 		NKern::Unlock();
       
   647 		LeaveKernel();
       
   648 		}
       
   649 	}
       
   650