--- 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 <emulator.h>
+#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<NThread*>(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<NThread*>(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<SCreateThread*>(aParam);
+ NThread& me = *static_cast<NThread*>(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<SCreateThread*>(aParam);
- NThread& me=*static_cast<NThread*>(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<NThread*>(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<NThread*>(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<NThread*>(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<NThread&>(me).iInKernel > 0);
me.iCsCount = 0;
- __NK_ASSERT_ALWAYS(static_cast<NThread&>(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<NThread*>(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();
- }
}