Update contrib.
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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32\nkern\win32\ncthrd.cpp
18 // NThreadBase member data
19 #define __INCLUDE_NTHREADBASE_DEFINES__
24 extern "C" void ExcFault(TAny*);
26 // initial Win32 thread stack size
27 const TInt KInitialStackSize = 0x1000;
29 // maximum size of the parameter block passed to a new thread
30 const TInt KMaxParameterBlock = 512;
32 // data passed to new thread to enable hand-off of the parameter block
35 const SNThreadCreateInfo* iInfo;
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.
44 static void SetPriority(HANDLE aThread, TEmulThreadType aType)
52 p = THREAD_PRIORITY_ABOVE_NORMAL;
55 p = THREAD_PRIORITY_NORMAL;
59 __NK_ASSERT_ALWAYS(SetThreadPriority(aThread, p));
60 SetThreadPriorityBoost(aThread, TRUE); // disable priority boost (for NT)
64 /** Create a Win32 thread for use in the emulator.
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
72 @pre Call either in thread context.
73 @pre Do not call from bare Win32 threads.
77 EXPORT_C HANDLE CreateWin32Thread(TEmulThreadType aType, LPTHREAD_START_ROUTINE aThreadFunc, LPVOID aPtr, TBool aRun)
79 __NK_ASSERT_DEBUG(!TheScheduler.iCurrentThread || NKern::CurrentContext() == NKern::EThread);
84 HANDLE handle = CreateThread(NULL , KInitialStackSize, aThreadFunc, aPtr, CREATE_SUSPENDED, &id);
87 SetPriority(handle, aType);
95 /** Set some global properties of the emulator
96 Called by the Win32 base port during boot.
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
103 EXPORT_C void NThread::SetProperties(TBool aTrace, TInt aSingleCpu)
105 Win32TraceThreadId = aTrace;
106 Win32SingleCpu = aSingleCpu;
109 #if defined(__CW32__) && __MWERKS__ < 0x3200
110 DWORD NThread__ExceptionHandler(EXCEPTION_RECORD* aException, TAny* /*aRegistrationRecord*/, CONTEXT* aContext)
112 // Hook into exception handling for old version of CW
115 return NThread::ExceptionHandler(aException, aContext);
117 #endif // old __CW32__
119 DWORD WINAPI NThread::StartThread(LPVOID aParam)
121 // Win32 thread function for nKern threads.
123 // The thread first enters this function after the nScheduler has resumed
124 // it, following the context switch induced by the hand-off mutex.
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.
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);
140 #if defined(__CW32__) && __MWERKS__ < 0x3200
141 // intercept the win32 exception mechanism manually
147 push offset NThread__ExceptionHandler
155 #else // ! old __CW32__
156 // intercept win32 exceptions in a debuggabble way
158 #endif // old __CW32__
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)
166 __NK_ASSERT_DEBUG(TUint(info.iParameterBlockSize)<=TUint(KMaxParameterBlock));
167 parameter=parameterBlock;
168 memcpy(parameterBlock,info.iParameterBlock,info.iParameterBlockSize);
170 NThreadFunction threadFunction=info.iFunction;
172 // Calculate stack base
173 me.iUserStackBase = (((TLinAddr)¶meterBlock)+0xfff)&~0xfff; // base address of stack
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());
179 #ifdef MONITOR_THREAD_CPU_TIME
180 me.iLastStartTime = 0; // Don't count NThread setup in cpu time
183 // start-up complete, release the handoff mutex, which will re-suspend us
184 NKern::FMSignal(&init->iHandoff);
186 // thread has been resumed: invoke the thread function
187 threadFunction(parameter);
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
196 #endif // !old __CW32__
203 static HANDLE InitThread()
205 // Set up the initial thread and return the thread handle
208 HANDLE p = GetCurrentProcess();
210 __NK_ASSERT_ALWAYS(DuplicateHandle(p, GetCurrentThread(), p, &me, 0, FALSE, DUPLICATE_SAME_ACCESS));
211 SetPriority(me, EThreadNKern);
215 TInt NThread::Create(SNThreadCreateInfo& aInfo, TBool aInitial)
219 iScheduleLock = NULL;
222 iWakeup = aInitial ? ERelease : EResumeLocked; // mark new threads as created (=> win32 suspend)
224 TInt r=NThreadBase::Create(aInfo,aInitial);
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();
235 iWinThread = InitThread();
237 #ifdef MONITOR_THREAD_CPU_TIME
238 iLastStartTime = NKern::FastCounter();
240 iUserStackBase = (((TLinAddr)&r)+0xfff)&~0xfff; // base address of stack
241 SchedulerInit(*this);
245 // create the thread proper
248 start.iInfo = &aInfo;
250 iWinThread = CreateWin32Thread(EThreadNKern, &StartThread, &start, FALSE);
251 if (iWinThread == NULL)
253 r = Emulator::LastError();
254 CloseHandle(iScheduleLock);
258 #ifdef BTRACE_THREAD_IDENTIFICATION
259 BTrace4(BTrace::EThreadIdentification,BTrace::ENanoThreadCreate,this);
261 // switch to the new thread to hand over the parameter block
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)
269 start.iHandoff.Wait();
270 start.iHandoff.Signal();
276 void NThread__HandleException(TWin32ExcInfo aExc)
278 // Final stage NKern exception handler.
280 // Check for a fatal exception when the kernel is locked
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().
287 if (TheScheduler.iKernCSLocked)
290 // Complete the exception data. Note that the call to EnterKernel() in
291 // ExceptionFilter() will have incremented iInKernel after the exception
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;
298 // run NThread exception handler in 'kernel' mode
299 me->iHandlers->iExceptionHandler(&aExc, me);
302 // If a 'user' handler is set by the kernel handler, run it
304 aExc.iHandler(aExc.iParam[0], aExc.iParam[1]);
309 // CW asm ICE workaround
315 __NAKED__ void NThread::Exception()
317 // Trampoline to nKern exception handler
318 // must preserve all registers in the structure defined by TWin32Exc
321 // this is the TWin32Exc structure
322 __asm push Win32ExcAddress // save return address followed by EBP first to help debugger
334 __asm lea esi, [ebp+8]
335 __asm push esi // original esp
340 __asm push Win32ExcDataAddress
341 __asm push Win32ExcCode
342 __asm sub esp, 20 // struct init completed by NThread__HandleException()
344 __asm call NKern__Unlock
346 __asm call NThread__HandleException
353 __asm pop esi // original ESP - ignore
356 __asm pop ebp // original SS - ignore
362 __asm pop ebp // original CS - ignore
367 LONG WINAPI NThread::ExceptionFilter(EXCEPTION_POINTERS* aExc)
369 // Filter wrapper for main Win32 exception handler
372 LONG ret = EXCEPTION_CONTINUE_SEARCH;
374 switch (ExceptionHandler(aExc->ExceptionRecord, aExc->ContextRecord))
376 case ExceptionContinueExecution:
378 ret = EXCEPTION_CONTINUE_EXECUTION;
381 case ExceptionContinueSearch:
391 // From e32/commmon/win32/seh.cpp
392 extern DWORD CallFinalSEHHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext);
394 extern void DivertHook();
396 DWORD NThread::ExceptionHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext)
398 // Win32 exception handler for EPOC threads
401 if (aException->ExceptionCode == EXCEPTION_BREAKPOINT)
403 // Hardcoded breakpoint
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.
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);
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
421 NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread);
422 if (me.iDiverted && me.iDivert)
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;
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;
444 TheScheduler.iKernCSLocked = 1; // prevent pre-emption
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;
454 return ExceptionContinueExecution;
457 void NThread::Diverted()
459 // Forced diversion go through here, in order to 'enter' the kernel
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;
468 me.iDivertReturn = NULL;
471 divert(); // does not return
476 void NThread__Diverted()
481 __NAKED__ void DivertHook()
483 // The stack frame is set up like this:
485 // | return address |
492 __asm push eax // reserve word for return address
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
512 void NThread::ApplyDiversion()
514 // Called with interrupts disabled and kernel locked
515 __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
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);
528 void NThread::Divert(TDivert aDivert)
530 // Divert the thread from its current path
531 // The diversion function is called with the kernel locked and interrupts enabled
535 if (iWakeup == EResume)
536 iWakeup = EResumeDiverted;
538 __NK_ASSERT_ALWAYS(iWakeup == ERelease);
541 void NThread::ExitSync()
543 // Diversion used to terminate 'stillborn' threads.
544 // On entry, kernel is locked, interrupts are enabled and we hold an interlock mutex
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);
552 TScheduler::Reschedule(); // this won't return
556 void NThread::Stillborn()
558 // Called if the new thread creation was aborted - so it will not be killed in the usual manner
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
564 // check if the Win32 thread was created
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
580 void NThread::ExitAsync()
582 // Diversion used to terminate 'killed' threads.
583 // On entry, kernel is locked and interrupts are enabled
586 NThreadBase& me = *TheScheduler.iCurrentThread;
588 __NK_ASSERT_ALWAYS(static_cast<NThread&>(me).iInKernel>0);
592 void NThreadBase::OnKill()
596 void NThreadBase::OnExit()
600 inline void NThread::DoForceExit()
602 __NK_ASSERT_DEBUG(TheScheduler.iKernCSLocked);
607 void NThreadBase::ForceExit()
609 // Called to force the thread to exit when it resumes
612 static_cast<NThread*>(this)->DoForceExit();
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
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.
629 // Acquire the global lock. May be called before scheduler running, so handle that case
632 if (TheScheduler.iCurrentThread)
639 void SchedulerUnlock()
641 // Release the global lock. May be called before scheduler running, so handle that case
644 if (TheScheduler.iCurrentThread)