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