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)¶meterBlock)+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 +