diff -r dc268b18d709 -r 6a75fa55495f kernel/eka/nkern/win32/ncthrd.cpp --- a/kernel/eka/nkern/win32/ncthrd.cpp Wed Sep 22 10:53:45 2010 +0100 +++ b/kernel/eka/nkern/win32/ncthrd.cpp Mon Sep 27 10:52:00 2010 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). +// Copyright (c) 1998-2010 Nokia Corporation and/or its subsidiary(-ies). // All rights reserved. // This component and the accompanying materials are made available // under the terms of the License "Eclipse Public License v1.0" @@ -12,7 +12,7 @@ // // Description: // e32\nkern\win32\ncthrd.cpp -// +// // // NThreadBase member data @@ -21,6 +21,11 @@ #include "nk_priv.h" #include +#if defined(__CW32__) && defined(__MWERKS__) && (__MWERKS__ < 0x3200) +// Early versions didn't support try/except :( +#error "This compiler is no longer supported, because it doesn't provide C++ exception generation and handling" +#endif + extern "C" void ExcFault(TAny*); // initial Win32 thread stack size @@ -36,31 +41,298 @@ NFastMutex iHandoff; }; + +/** Set some global properties of the emulator + Called by the Win32 base port during boot. + + @param aTrace TRUE means trace Win32 thread ID for every thread created + @param aSingleCpu TRUE means lock the emulator process to a single CPU + + @internalTechnology + */ +EXPORT_C void NThread::SetProperties(TBool aTrace, TInt aSingleCpu) + { + Win32TraceThreadId = aTrace; + Win32SingleCpu = aSingleCpu; + } + + + +void NThread__HandleException(TWin32ExcInfo aExc) +// +// Final stage NKern exception handler. +// +// The first stage of exception processing (in ExceptionHandler()) entered the +// kernel and locked it, so we have to undo those two operations before returning. +// However, if the kernel was already locked when the exception occurred, it is +// a fatal condition and the system will be faulted. +// +// Note that the parameter struct is passed by value, this allows for direct +// access to the exception context created on the call stack by NThread::Exception(). +// + { + NKern::Unlock(); + if (TheScheduler.iKernCSLocked) + ExcFault(&aExc); + + // Complete the exception data. Note that the call to EnterKernel() in + // ExceptionHandler() will have incremented iInKernel after the exception + // occurred. + NThread* me = static_cast(TheScheduler.iCurrentThread); + __NK_ASSERT_DEBUG(me->iInKernel); + aExc.iFlags = me->iInKernel == 1 ? 0 : TWin32ExcInfo::EExcInKernel; + aExc.iHandler = NULL; + + // run NThread exception handler in 'kernel' mode + me->iHandlers->iExceptionHandler(&aExc, me); + LeaveKernel(); + + // If a 'user' handler is set by the kernel handler, run it + if (aExc.iHandler) + aExc.iHandler(aExc.iParam[0], aExc.iParam[1]); + } + +__NAKED__ void NThread::Exception() +// +// Trampoline to nKern exception handler +// Must preserve all registers in the structure defined by TWin32Exc +// +// This is an intermediate layer, to which control has been diverted by +// NThread::ExceptionHandler(). It constructs a TWin32Exc structure on +// the stack and passes it NThread__ExceptionHandler(). +// +// At this point we are no longer in Win32 exception context. +// + { + // this is the TWin32Exc structure + __asm push Win32ExcAddress // save return address followed by EBP first to help debugger + __asm push ebp + __asm mov ebp, esp + __asm push cs + __asm pushfd + __asm push gs + __asm push fs + __asm push es + __asm push ds + __asm push ss + __asm push edi + __asm push esi + __asm lea esi, [ebp+8] + __asm push esi // original esp + __asm push ebx + __asm push edx + __asm push ecx + __asm push eax + __asm push Win32ExcDataAddress + __asm push Win32ExcCode + __asm sub esp, 20 // struct init completed by NThread__HandleException() + + __asm call NThread__HandleException + + __asm add esp, 28 + __asm pop eax + __asm pop ecx + __asm pop edx + __asm pop ebx + __asm pop esi // original ESP - ignore + __asm pop esi + __asm pop edi + __asm pop ebp // original SS - ignore + __asm pop ds + __asm pop es + __asm pop fs + __asm pop gs + __asm popfd + __asm pop ebp // original CS - ignore + __asm pop ebp + __asm ret + } + +// From e32/commmon/win32/seh.cpp +extern DWORD CallFinalSEHHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext); + +extern void DivertHook(); + +DWORD NThread::ExceptionHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext) +// +// Win32 exception handler for EPOC threads +// +// This is the outermost wrapper, called from ExceptionFilter() of via manual +// interception of the Win32 exception mechanism if using a really old compiler +// + { + if (aException->ExceptionCode == EXCEPTION_BREAKPOINT) + { + // Hardcoded breakpoint + // + // Jump directly to NT's default unhandled exception handler which will + // either display a dialog, directly invoke the JIT debugger or do nothing + // dependent upon the AeDebug and ErrorMode registry settings. + // + // Note this handler is always installed on the SEH chain and is always + // the last handler on this chain, as it is installed by NT in kernel32.dll + // before invoking the Win32 thread function. + return CallFinalSEHHandler(aException, aContext); + } + + // deal with conflict between preemption and diversion + // the diversion will have been applied to the pre-exception context, not + // the current context, and thus will get 'lost'. Wake-up of a pre-empted + // thread with a diversion will not unlock the kernel, so we need to deal + // with the possibility that the kernel may be locked if a diversion exists + NThread& me = *static_cast(TheScheduler.iCurrentThread); + if (me.iDiverting && me.iDivertFn) + { + // The thread is being forced to exit - run the diversion outside of Win32 exception handler + __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); + aContext->Eip = (TUint32)&DivertHook; + } + else + { + if (me.iDiverting) + { + // The thread is being prodded to pick up its callbacks. This will happen when the + // exception handler calls LeaveKernel(), so we can remove the diversion + __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); + if (aException->ExceptionAddress == &DivertHook) + aException->ExceptionAddress = me.iDivertReturn; + me.iDivertReturn = NULL; + me.iDiverting = EFalse; + EnterKernel(TRUE); + } + else + { + EnterKernel(); + TheScheduler.iKernCSLocked = 1; // prevent pre-emption + } + + // If the kernel was already locked, this will be detected in the next stage handler + // (NThread::Exception()), which we arrange to run outside the Win32 exception context + Win32ExcAddress = aException->ExceptionAddress; + Win32ExcDataAddress = (TAny*)aException->ExceptionInformation[1]; + Win32ExcCode = aException->ExceptionCode; + aContext->Eip = (TUint32)&Exception; + } + + return ExceptionContinueExecution; + } + +LONG WINAPI NThread::ExceptionFilter(EXCEPTION_POINTERS* aExc) +// +// Filter wrapper for main Win32 exception handler +// + { + LONG ret = EXCEPTION_CONTINUE_SEARCH; + + switch (ExceptionHandler(aExc->ExceptionRecord, aExc->ContextRecord)) + { + case ExceptionContinueExecution: + ret = EXCEPTION_CONTINUE_EXECUTION; + break; + + case ExceptionContinueSearch: + default: + break; + } + + return ret; + } + + +DWORD WINAPI NThread::StartThread(LPVOID aParam) +// +// Win32 thread function for nKern threads. +// +// The thread first enters this function after the nScheduler has resumed +// it, following the context switch induced by the hand-off mutex. +// +// The parameter block for this thread needs to be copied into its +// own context, before releasing the mutex and handing control back to +// the creating thread. +// + { + SCreateThread* init = static_cast(aParam); + NThread& me = *static_cast(init->iHandoff.iHoldingThread); + me.iWinThreadId = GetCurrentThreadId(); + SchedulerRegister(me); +#ifdef BTRACE_FAST_MUTEX + BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexWait, &init->iHandoff); +#endif + NKern::Unlock(); + + // intercept win32 exceptions in a debuggabble way + __try + { + // save the thread entry point and parameter block + const SNThreadCreateInfo& info = *init->iInfo; + NThreadFunction threadFunction = info.iFunction; + TUint8 parameterBlock[KMaxParameterBlock]; + TAny* parameter = (TAny*)info.iParameterBlock; + + if (info.iParameterBlockSize) + { + __NK_ASSERT_DEBUG(TUint(info.iParameterBlockSize) <= TUint(KMaxParameterBlock)); + memcpy(parameterBlock, info.iParameterBlock, info.iParameterBlockSize); + parameter = parameterBlock; + } + + + // Calculate stack base + me.iUserStackBase = (((TLinAddr)¶meterBlock) + 0xfff) & ~0xfff; + + // some useful diagnostics for debugging + if (Win32TraceThreadId) + KPrintf("Thread %T created @ 0x%x - Win32 Thread ID 0x%x", init->iHandoff.iHoldingThread, init->iHandoff.iHoldingThread, GetCurrentThreadId()); + +#ifdef MONITOR_THREAD_CPU_TIME + me.iLastStartTime = 0; // Don't count NThread setup in cpu time +#endif + + // start-up complete, release the handoff mutex, which will re-suspend us + NKern::FMSignal(&init->iHandoff); + + // thread has been resumed: invoke the thread function + threadFunction(parameter); + } + __except (ExceptionFilter(GetExceptionInformation())) + { + // Do nothing - filter does all the work and hooks into EPOC + // h/w exception mechanism if necessary by thread diversion + } + + NKern::Exit(); + return 0; + } + + + /** * Set the Win32 thread priority based on the thread type. - * Interrupt/Event threads must be able to preempt normal nKern threads, - * so they get a higher priority. + * Interrupt/Event threads must be able to preempt normal + * nKern threads, so they get a higher (Win32) priority. */ static void SetPriority(HANDLE aThread, TEmulThreadType aType) { TInt p; + switch (aType) { default: FAULT(); + case EThreadEvent: p = THREAD_PRIORITY_ABOVE_NORMAL; break; + case EThreadNKern: p = THREAD_PRIORITY_NORMAL; break; } - __NK_ASSERT_ALWAYS(SetThreadPriority(aThread, p)); + CheckedSetThreadPriority(aThread, p); SetThreadPriorityBoost(aThread, TRUE); // disable priority boost (for NT) } - /** Create a Win32 thread for use in the emulator. @param aType Type of thread (Event or NKern) - determines Win32 priority @@ -91,115 +363,6 @@ return handle; } - -/** Set some global properties of the emulator - Called by the Win32 base port during boot. - - @param aTrace TRUE means trace Win32 thread ID for every thread created - @param aSingleCpu TRUE means lock the emulator process to a single CPU - - @internalTechnology - */ -EXPORT_C void NThread::SetProperties(TBool aTrace, TInt aSingleCpu) - { - Win32TraceThreadId = aTrace; - Win32SingleCpu = aSingleCpu; - } - -#if defined(__CW32__) && __MWERKS__ < 0x3200 -DWORD NThread__ExceptionHandler(EXCEPTION_RECORD* aException, TAny* /*aRegistrationRecord*/, CONTEXT* aContext) -// -// Hook into exception handling for old version of CW -// - { - return NThread::ExceptionHandler(aException, aContext); - } -#endif // old __CW32__ - -DWORD WINAPI NThread::StartThread(LPVOID aParam) -// -// Win32 thread function for nKern threads. -// -// The thread first enters this function after the nScheduler has resumed -// it, following the context switch induced by the hand-off mutex. -// -// The parameter block for this thread needs to be copied into its -// own context, before releasing the mutex and handing control back to -// the creating thread. -// - { - SCreateThread* init = static_cast(aParam); - NThread& me=*static_cast(init->iHandoff.iHoldingThread); - me.iWinThreadId = GetCurrentThreadId(); - SchedulerRegister(me); -#ifdef BTRACE_FAST_MUTEX - BTraceContext4(BTrace::EFastMutex,BTrace::EFastMutexWait,&init->iHandoff); -#endif - NKern::Unlock(); - -#if defined(__CW32__) && __MWERKS__ < 0x3200 - // intercept the win32 exception mechanism manually - asm { - push ebp - mov eax, -1 - push eax - push eax - push offset NThread__ExceptionHandler - push fs:[0] - mov fs:[0], esp - - // realign the stack - sub esp, 0x20 - and esp, ~0x1f - } -#else // ! old __CW32__ - // intercept win32 exceptions in a debuggabble way -__try { -#endif // old __CW32__ - - // save the thread entry point and parameter block - const SNThreadCreateInfo& info = *init->iInfo; - TUint8 parameterBlock[KMaxParameterBlock]; - TAny* parameter=(TAny*)info.iParameterBlock; - if (info.iParameterBlockSize) - { - __NK_ASSERT_DEBUG(TUint(info.iParameterBlockSize)<=TUint(KMaxParameterBlock)); - parameter=parameterBlock; - memcpy(parameterBlock,info.iParameterBlock,info.iParameterBlockSize); - } - NThreadFunction threadFunction=info.iFunction; - - // Calculate stack base - me.iUserStackBase = (((TLinAddr)¶meterBlock)+0xfff)&~0xfff; // base address of stack - - // some useful diagnostics for debugging - if (Win32TraceThreadId) - KPrintf("Thread %T created @ 0x%x - Win32 Thread ID 0x%x",init->iHandoff.iHoldingThread,init->iHandoff.iHoldingThread,GetCurrentThreadId()); - -#ifdef MONITOR_THREAD_CPU_TIME - me.iLastStartTime = 0; // Don't count NThread setup in cpu time -#endif - - // start-up complete, release the handoff mutex, which will re-suspend us - NKern::FMSignal(&init->iHandoff); - - // thread has been resumed: invoke the thread function - threadFunction(parameter); - -#if !defined(__CW32__) || __MWERKS__ >= 0x3200 - // handle win32 exceptions -} __except (ExceptionFilter(GetExceptionInformation())) { - // Do nothing - filter does all the work and hooks - // into EPOC h/w exception mechanism if necessary - // by thread diversion -} -#endif // !old __CW32__ - - NKern::Exit(); - - return 0; - } - static HANDLE InitThread() // // Set up the initial thread and return the thread handle @@ -207,7 +370,8 @@ { HANDLE p = GetCurrentProcess(); HANDLE me; - __NK_ASSERT_ALWAYS(DuplicateHandle(p, GetCurrentThread(), p, &me, 0, FALSE, DUPLICATE_SAME_ACCESS)); + DWORD r = DuplicateHandle(p, GetCurrentThread(), p, &me, 0, FALSE, DUPLICATE_SAME_ACCESS); + __NK_ASSERT_ALWAYS(r != 0); // r is zero on error SetPriority(me, EThreadNKern); return me; } @@ -218,11 +382,11 @@ iWinThreadId = 0; iScheduleLock = NULL; iInKernel = 1; - iDivert = NULL; + iDivertFn = NULL; iWakeup = aInitial ? ERelease : EResumeLocked; // mark new threads as created (=> win32 suspend) - TInt r=NThreadBase::Create(aInfo,aInitial); - if (r!=KErrNone) + TInt r = NThreadBase::Create(aInfo, aInitial); + if (r != KErrNone) return r; // the rest has to be all or nothing, we must complete it @@ -237,7 +401,7 @@ #ifdef MONITOR_THREAD_CPU_TIME iLastStartTime = NKern::FastCounter(); #endif - iUserStackBase = (((TLinAddr)&r)+0xfff)&~0xfff; // base address of stack + iUserStackBase = (((TLinAddr)&r) + 0xfff) & ~0xfff; // base address of stack SchedulerInit(*this); return KErrNone; } @@ -246,7 +410,6 @@ // SCreateThread start; start.iInfo = &aInfo; - iWinThread = CreateWin32Thread(EThreadNKern, &StartThread, &start, FALSE); if (iWinThread == NULL) { @@ -256,7 +419,7 @@ } #ifdef BTRACE_THREAD_IDENTIFICATION - BTrace4(BTrace::EThreadIdentification,BTrace::ENanoThreadCreate,this); + BTrace4(BTrace::EThreadIdentification, BTrace::ENanoThreadCreate, this); #endif // switch to the new thread to hand over the parameter block NKern::Lock(); @@ -273,202 +436,33 @@ return KErrNone; } -void NThread__HandleException(TWin32ExcInfo aExc) -// -// Final stage NKern exception handler. -// -// Check for a fatal exception when the kernel is locked -// -// Note that the parameter struct is passed by value, this allows for -// direct access to the exception context created on the call stack by -// NThread::Exception(). -// - { - if (TheScheduler.iKernCSLocked) - ExcFault(&aExc); - // Complete the exception data. Note that the call to EnterKernel() in - // ExceptionFilter() will have incremented iInKernel after the exception - // occurred. - NThread* me = static_cast(TheScheduler.iCurrentThread); - __NK_ASSERT_DEBUG(me->iInKernel); - aExc.iFlags = me->iInKernel == 1 ? 0 : TWin32ExcInfo::EExcInKernel; - aExc.iHandler = NULL; - - // run NThread exception handler in 'kernel' mode - me->iHandlers->iExceptionHandler(&aExc, me); - LeaveKernel(); - - // If a 'user' handler is set by the kernel handler, run it - if (aExc.iHandler) - aExc.iHandler(aExc.iParam[0], aExc.iParam[1]); - } - -void NKern__Unlock() -// -// CW asm ICE workaround -// - { - NKern::Unlock(); - } - -__NAKED__ void NThread::Exception() -// -// Trampoline to nKern exception handler -// must preserve all registers in the structure defined by TWin32Exc -// - { - // this is the TWin32Exc structure - __asm push Win32ExcAddress // save return address followed by EBP first to help debugger - __asm push ebp - __asm mov ebp, esp - __asm push cs - __asm pushfd - __asm push gs - __asm push fs - __asm push es - __asm push ds - __asm push ss - __asm push edi - __asm push esi - __asm lea esi, [ebp+8] - __asm push esi // original esp - __asm push ebx - __asm push edx - __asm push ecx - __asm push eax - __asm push Win32ExcDataAddress - __asm push Win32ExcCode - __asm sub esp, 20 // struct init completed by NThread__HandleException() - - __asm call NKern__Unlock - - __asm call NThread__HandleException - - __asm add esp, 28 - __asm pop eax - __asm pop ecx - __asm pop edx - __asm pop ebx - __asm pop esi // original ESP - ignore - __asm pop esi - __asm pop edi - __asm pop ebp // original SS - ignore - __asm pop ds - __asm pop es - __asm pop fs - __asm pop gs - __asm popfd - __asm pop ebp // original CS - ignore - __asm pop ebp - __asm ret - } - -LONG WINAPI NThread::ExceptionFilter(EXCEPTION_POINTERS* aExc) -// -// Filter wrapper for main Win32 exception handler -// - { - LONG ret = EXCEPTION_CONTINUE_SEARCH; - - switch (ExceptionHandler(aExc->ExceptionRecord, aExc->ContextRecord)) - { - case ExceptionContinueExecution: - { - ret = EXCEPTION_CONTINUE_EXECUTION; - } - break; - case ExceptionContinueSearch: - default: - { - } - break; - } - - return ret; - } - -// From e32/commmon/win32/seh.cpp -extern DWORD CallFinalSEHHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext); - -extern void DivertHook(); - -DWORD NThread::ExceptionHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext) -// -// Win32 exception handler for EPOC threads -// - { - if (aException->ExceptionCode == EXCEPTION_BREAKPOINT) - { - // Hardcoded breakpoint - // - // Jump directly to NT's default unhandled exception handler which will - // either display a dialog, directly invoke the JIT debugger or do nothing - // dependent upon the AeDebug and ErrorMode registry settings. - // - // Note this handler is always installed on the SEH chain and is always - // the last handler on this chain, as it is installed by NT in kernel32.dll - // before invoking the Win32 thread function. - return CallFinalSEHHandler(aException, aContext); - } - - // deal with conflict between preemption and diversion - // the diversion will have been applied to the pre-exception context, not - // the current context, and thus will get 'lost'. Wake-up of a pre-empted - // thread with a diversion will not unlock the kernel, so need to deal with - // the possibility that the kernel may be locked if a diversion exists - - NThread& me = *static_cast(TheScheduler.iCurrentThread); - if (me.iDiverted && me.iDivert) - { - // The thread is being forced to exit - run the diversion outside of Win32 exception handler - __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); - aContext->Eip = (TUint32)&DivertHook; - } - else - { - if (me.iDiverted) - { - // The thread is being prodded to pick up its callbacks. This will happen when the - // exception handler calls LeaveKernel(), so we can remove the diversion - __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); - if (aException->ExceptionAddress == &DivertHook) - aException->ExceptionAddress = me.iDivertReturn; - me.iDiverted = EFalse; - me.iDivertReturn = NULL; - EnterKernel(FALSE); - } - else - { - EnterKernel(); - TheScheduler.iKernCSLocked = 1; // prevent pre-emption - } - - // If the kernel was already locked, this will be detected in the next stage handler - // run 2nd stage handler outside of Win32 exception context - Win32ExcAddress = aException->ExceptionAddress; - Win32ExcDataAddress = (TAny*)aException->ExceptionInformation[1]; - Win32ExcCode = aException->ExceptionCode; - aContext->Eip = (TUint32)&Exception; - } - return ExceptionContinueExecution; - } void NThread::Diverted() // -// Forced diversion go through here, in order to 'enter' the kernel +// This function is called in the context of a thread that is being diverted. +// This can be for either of two reasons: if iDivertFn has been set, that +// function will be called and is not expected to return i.e. it should force +// the thread to exit. Otherwise, the thread will make a null trip through the +// kernel, causing it to run pending user-mode callbacks on the way out. +// +// On entry, the kernel is locked and interrupts enabled // { NThread& me = *static_cast(TheScheduler.iCurrentThread); __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); - __NK_ASSERT_ALWAYS(me.iDiverted); - NThread::TDivert divert = me.iDivert; - me.iDiverted = EFalse; - me.iDivert = NULL; + __NK_ASSERT_ALWAYS(me.iInKernel == 0); + __NK_ASSERT_ALWAYS(me.iDiverting); + NThread::TDivert divertFn = me.iDivertFn; + me.iDivertFn = NULL; me.iDivertReturn = NULL; - EnterKernel(FALSE); - if (divert) - divert(); // does not return + me.iDiverting = EFalse; + + EnterKernel(TRUE); + + if (divertFn) + divertFn(); // does not return + NKern::Unlock(); LeaveKernel(); } @@ -488,10 +482,10 @@ // | saved eax | // | saved ecx | // | saved edx | - // + // __asm push eax // reserve word for return address __asm push ebp - __asm mov ebp, esp + __asm mov ebp, esp __asm pushfd __asm push eax __asm push ecx @@ -510,28 +504,43 @@ void NThread::ApplyDiversion() +// +// Arrange that the thread will be diverted when next it runs. +// This can be for either of two reasons: if iDivertFn has been set, +// that function will be called and is not expected to return i.e. +// it should force the thread to exit. Otherwise, the thread will +// make a null trip through the kernel, causing it to run pending +// user-mode callbacks on the way out. +// +// This uses the Win32 CONTEXT functions to change the thread's PC +// so that execution restarts at DivertHook ... +// { // Called with interrupts disabled and kernel locked __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); - if (iDiverted) + __NK_ASSERT_ALWAYS(iDivertReturn == NULL || iDiverting); + + if (iDiverting) return; + CONTEXT c; - c.ContextFlags=CONTEXT_FULL; - GetThreadContext(iWinThread, &c); - __NK_ASSERT_ALWAYS(iDivertReturn == NULL); + c.ContextFlags = CONTEXT_CONTROL; + CheckedGetThreadContext(iWinThread, &c); iDivertReturn = (TAny*)c.Eip; - c.Eip=(TUint32)&DivertHook; - SetThreadContext(iWinThread, &c); - iDiverted = ETrue; + c.Eip = (TUint32)&DivertHook; + c.ContextFlags = CONTEXT_CONTROL; + CheckedSetThreadContext(iWinThread, &c); + iDiverting = ETrue; } -void NThread::Divert(TDivert aDivert) +void NThread::Divert(TDivert aDivertFn) // -// Divert the thread from its current path -// The diversion function is called with the kernel locked and interrupts enabled +// Arrange that the thread will exit by calling aDivertFn when next +// it runs. The diversion function will be called with the kernel +// locked and interrupts enabled. It is not expected to return. // { - iDivert = aDivert; + iDivertFn = aDivertFn; if (iWakeup == EResume) iWakeup = EResumeDiverted; else @@ -544,9 +553,9 @@ // On entry, kernel is locked, interrupts are enabled and we hold an interlock mutex // { - NThreadBase& me=*TheScheduler.iCurrentThread; + NThreadBase& me = *TheScheduler.iCurrentThread; me.iHeldFastMutex->Signal(); // release the interlock - me.iNState=EDead; // mark ourselves as dead which will take thread out of scheduler + me.iNState = EDead; // mark ourselves as dead which will take thread out of scheduler TheScheduler.Remove(&me); RescheduleNeeded(); TScheduler::Reschedule(); // this won't return @@ -558,7 +567,7 @@ // Called if the new thread creation was aborted - so it will not be killed in the usual manner // // This function needs to exit the thread synchronously as on return we will destroy the thread control block -// Thus wee need to use an interlock that ensure that the target thread runs the exit handler before we continue +// Thus we need to use an interlock that ensure that the target thread runs the exit handler before we continue // { // check if the Win32 thread was created @@ -570,8 +579,8 @@ ForceResume(); // create and assign mutex to stillborn thread NFastMutex interlock; - interlock.iHoldingThread=this; - iHeldFastMutex=&interlock; + interlock.iHoldingThread = this; + iHeldFastMutex = &interlock; interlock.Wait(); // interlock on thread exit handler interlock.Signal(); NKern::Unlock(); @@ -584,23 +593,14 @@ // { NThreadBase& me = *TheScheduler.iCurrentThread; + __NK_ASSERT_ALWAYS(static_cast(me).iInKernel > 0); me.iCsCount = 0; - __NK_ASSERT_ALWAYS(static_cast(me).iInKernel>0); me.Exit(); } -void NThreadBase::OnKill() - { - } - -void NThreadBase::OnExit() - { - } - inline void NThread::DoForceExit() { __NK_ASSERT_DEBUG(TheScheduler.iKernCSLocked); -// Divert(&ExitAsync); } @@ -612,39 +612,11 @@ static_cast(this)->DoForceExit(); } -// -// We need a global lock in the emulator to avoid scheduling reentrancy problems with the host -// in particular, some host API calls acquire host mutexes, preempting such services results -// in suspension of those threads which can cause deadlock if another thread requires that host -// mutex. -// -// Because thread dreaction and code loading also require the same underlying mutex (used -// by NT to protect DLL entrypoint calling), this would be rather complex with a fast mutex. -// For now, keep it simple and use the preemption lock. Note that this means that the -// MS timer DFC may be significantly delayed when loading large DLL trees, for example. -// - -void SchedulerLock() -// -// Acquire the global lock. May be called before scheduler running, so handle that case -// +void NThreadBase::OnExit() { - if (TheScheduler.iCurrentThread) - { - EnterKernel(); - NKern::Lock(); - } } -void SchedulerUnlock() -// -// Release the global lock. May be called before scheduler running, so handle that case -// +void NThreadBase::OnKill() { - if (TheScheduler.iCurrentThread) - { - NKern::Unlock(); - LeaveKernel(); - } }