104 { |
54 { |
105 Win32TraceThreadId = aTrace; |
55 Win32TraceThreadId = aTrace; |
106 Win32SingleCpu = aSingleCpu; |
56 Win32SingleCpu = aSingleCpu; |
107 } |
57 } |
108 |
58 |
109 #if defined(__CW32__) && __MWERKS__ < 0x3200 |
59 |
110 DWORD NThread__ExceptionHandler(EXCEPTION_RECORD* aException, TAny* /*aRegistrationRecord*/, CONTEXT* aContext) |
60 |
111 // |
61 void NThread__HandleException(TWin32ExcInfo aExc) |
112 // Hook into exception handling for old version of CW |
62 // |
113 // |
63 // Final stage NKern exception handler. |
114 { |
64 // |
115 return NThread::ExceptionHandler(aException, aContext); |
65 // The first stage of exception processing (in ExceptionHandler()) entered the |
116 } |
66 // kernel and locked it, so we have to undo those two operations before returning. |
117 #endif // old __CW32__ |
67 // However, if the kernel was already locked when the exception occurred, it is |
118 |
68 // a fatal condition and the system will be faulted. |
119 DWORD WINAPI NThread::StartThread(LPVOID aParam) |
69 // |
120 // |
70 // Note that the parameter struct is passed by value, this allows for direct |
121 // Win32 thread function for nKern threads. |
71 // access to the exception context created on the call stack by NThread::Exception(). |
122 // |
72 // |
123 // The thread first enters this function after the nScheduler has resumed |
73 { |
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(); |
74 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) |
75 if (TheScheduler.iKernCSLocked) |
288 ExcFault(&aExc); |
76 ExcFault(&aExc); |
289 |
77 |
290 // Complete the exception data. Note that the call to EnterKernel() in |
78 // Complete the exception data. Note that the call to EnterKernel() in |
291 // ExceptionFilter() will have incremented iInKernel after the exception |
79 // ExceptionHandler() will have incremented iInKernel after the exception |
292 // occurred. |
80 // occurred. |
293 NThread* me = static_cast<NThread*>(TheScheduler.iCurrentThread); |
81 NThread* me = static_cast<NThread*>(TheScheduler.iCurrentThread); |
294 __NK_ASSERT_DEBUG(me->iInKernel); |
82 __NK_ASSERT_DEBUG(me->iInKernel); |
295 aExc.iFlags = me->iInKernel == 1 ? 0 : TWin32ExcInfo::EExcInKernel; |
83 aExc.iFlags = me->iInKernel == 1 ? 0 : TWin32ExcInfo::EExcInKernel; |
296 aExc.iHandler = NULL; |
84 aExc.iHandler = NULL; |
330 __asm push ds |
116 __asm push ds |
331 __asm push ss |
117 __asm push ss |
332 __asm push edi |
118 __asm push edi |
333 __asm push esi |
119 __asm push esi |
334 __asm lea esi, [ebp+8] |
120 __asm lea esi, [ebp+8] |
335 __asm push esi // original esp |
121 __asm push esi // original esp |
336 __asm push ebx |
122 __asm push ebx |
337 __asm push edx |
123 __asm push edx |
338 __asm push ecx |
124 __asm push ecx |
339 __asm push eax |
125 __asm push eax |
340 __asm push Win32ExcDataAddress |
126 __asm push Win32ExcDataAddress |
341 __asm push Win32ExcCode |
127 __asm push Win32ExcCode |
342 __asm sub esp, 20 // struct init completed by NThread__HandleException() |
128 __asm sub esp, 20 // struct init completed by NThread__HandleException() |
343 |
|
344 __asm call NKern__Unlock |
|
345 |
129 |
346 __asm call NThread__HandleException |
130 __asm call NThread__HandleException |
347 |
131 |
348 __asm add esp, 28 |
132 __asm add esp, 28 |
349 __asm pop eax |
133 __asm pop eax |
350 __asm pop ecx |
134 __asm pop ecx |
351 __asm pop edx |
135 __asm pop edx |
352 __asm pop ebx |
136 __asm pop ebx |
353 __asm pop esi // original ESP - ignore |
137 __asm pop esi // original ESP - ignore |
354 __asm pop esi |
138 __asm pop esi |
355 __asm pop edi |
139 __asm pop edi |
356 __asm pop ebp // original SS - ignore |
140 __asm pop ebp // original SS - ignore |
357 __asm pop ds |
141 __asm pop ds |
358 __asm pop es |
142 __asm pop es |
359 __asm pop fs |
143 __asm pop fs |
360 __asm pop gs |
144 __asm pop gs |
361 __asm popfd |
145 __asm popfd |
362 __asm pop ebp // original CS - ignore |
146 __asm pop ebp // original CS - ignore |
363 __asm pop ebp |
147 __asm pop ebp |
364 __asm ret |
148 __asm ret |
365 } |
149 } |
366 |
150 |
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 |
151 // From e32/commmon/win32/seh.cpp |
392 extern DWORD CallFinalSEHHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext); |
152 extern DWORD CallFinalSEHHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext); |
393 |
153 |
394 extern void DivertHook(); |
154 extern void DivertHook(); |
395 |
155 |
396 DWORD NThread::ExceptionHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext) |
156 DWORD NThread::ExceptionHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext) |
397 // |
157 // |
398 // Win32 exception handler for EPOC threads |
158 // Win32 exception handler for EPOC threads |
|
159 // |
|
160 // This is the outermost wrapper, called from ExceptionFilter() of via manual |
|
161 // interception of the Win32 exception mechanism if using a really old compiler |
399 // |
162 // |
400 { |
163 { |
401 if (aException->ExceptionCode == EXCEPTION_BREAKPOINT) |
164 if (aException->ExceptionCode == EXCEPTION_BREAKPOINT) |
402 { |
165 { |
403 // Hardcoded breakpoint |
166 // Hardcoded breakpoint |
413 } |
176 } |
414 |
177 |
415 // deal with conflict between preemption and diversion |
178 // deal with conflict between preemption and diversion |
416 // the diversion will have been applied to the pre-exception context, not |
179 // 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 |
180 // 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 |
181 // thread with a diversion will not unlock the kernel, so we need to deal |
419 // the possibility that the kernel may be locked if a diversion exists |
182 // with the possibility that the kernel may be locked if a diversion exists |
420 |
|
421 NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread); |
183 NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread); |
422 if (me.iDiverted && me.iDivert) |
184 if (me.iDiverting && me.iDivertFn) |
423 { |
185 { |
424 // The thread is being forced to exit - run the diversion outside of Win32 exception handler |
186 // The thread is being forced to exit - run the diversion outside of Win32 exception handler |
425 __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); |
187 __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); |
426 aContext->Eip = (TUint32)&DivertHook; |
188 aContext->Eip = (TUint32)&DivertHook; |
427 } |
189 } |
428 else |
190 else |
429 { |
191 { |
430 if (me.iDiverted) |
192 if (me.iDiverting) |
431 { |
193 { |
432 // The thread is being prodded to pick up its callbacks. This will happen when the |
194 // 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 |
195 // exception handler calls LeaveKernel(), so we can remove the diversion |
434 __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); |
196 __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); |
435 if (aException->ExceptionAddress == &DivertHook) |
197 if (aException->ExceptionAddress == &DivertHook) |
436 aException->ExceptionAddress = me.iDivertReturn; |
198 aException->ExceptionAddress = me.iDivertReturn; |
437 me.iDiverted = EFalse; |
|
438 me.iDivertReturn = NULL; |
199 me.iDivertReturn = NULL; |
439 EnterKernel(FALSE); |
200 me.iDiverting = EFalse; |
|
201 EnterKernel(TRUE); |
440 } |
202 } |
441 else |
203 else |
442 { |
204 { |
443 EnterKernel(); |
205 EnterKernel(); |
444 TheScheduler.iKernCSLocked = 1; // prevent pre-emption |
206 TheScheduler.iKernCSLocked = 1; // prevent pre-emption |
445 } |
207 } |
446 |
208 |
447 // If the kernel was already locked, this will be detected in the next stage handler |
209 // 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 |
210 // (NThread::Exception()), which we arrange to run outside the Win32 exception context |
449 Win32ExcAddress = aException->ExceptionAddress; |
211 Win32ExcAddress = aException->ExceptionAddress; |
450 Win32ExcDataAddress = (TAny*)aException->ExceptionInformation[1]; |
212 Win32ExcDataAddress = (TAny*)aException->ExceptionInformation[1]; |
451 Win32ExcCode = aException->ExceptionCode; |
213 Win32ExcCode = aException->ExceptionCode; |
452 aContext->Eip = (TUint32)&Exception; |
214 aContext->Eip = (TUint32)&Exception; |
453 } |
215 } |
|
216 |
454 return ExceptionContinueExecution; |
217 return ExceptionContinueExecution; |
455 } |
218 } |
456 |
219 |
|
220 LONG WINAPI NThread::ExceptionFilter(EXCEPTION_POINTERS* aExc) |
|
221 // |
|
222 // Filter wrapper for main Win32 exception handler |
|
223 // |
|
224 { |
|
225 LONG ret = EXCEPTION_CONTINUE_SEARCH; |
|
226 |
|
227 switch (ExceptionHandler(aExc->ExceptionRecord, aExc->ContextRecord)) |
|
228 { |
|
229 case ExceptionContinueExecution: |
|
230 ret = EXCEPTION_CONTINUE_EXECUTION; |
|
231 break; |
|
232 |
|
233 case ExceptionContinueSearch: |
|
234 default: |
|
235 break; |
|
236 } |
|
237 |
|
238 return ret; |
|
239 } |
|
240 |
|
241 |
|
242 DWORD WINAPI NThread::StartThread(LPVOID aParam) |
|
243 // |
|
244 // Win32 thread function for nKern threads. |
|
245 // |
|
246 // The thread first enters this function after the nScheduler has resumed |
|
247 // it, following the context switch induced by the hand-off mutex. |
|
248 // |
|
249 // The parameter block for this thread needs to be copied into its |
|
250 // own context, before releasing the mutex and handing control back to |
|
251 // the creating thread. |
|
252 // |
|
253 { |
|
254 SCreateThread* init = static_cast<SCreateThread*>(aParam); |
|
255 NThread& me = *static_cast<NThread*>(init->iHandoff.iHoldingThread); |
|
256 me.iWinThreadId = GetCurrentThreadId(); |
|
257 SchedulerRegister(me); |
|
258 #ifdef BTRACE_FAST_MUTEX |
|
259 BTraceContext4(BTrace::EFastMutex, BTrace::EFastMutexWait, &init->iHandoff); |
|
260 #endif |
|
261 NKern::Unlock(); |
|
262 |
|
263 // intercept win32 exceptions in a debuggabble way |
|
264 __try |
|
265 { |
|
266 // save the thread entry point and parameter block |
|
267 const SNThreadCreateInfo& info = *init->iInfo; |
|
268 NThreadFunction threadFunction = info.iFunction; |
|
269 TUint8 parameterBlock[KMaxParameterBlock]; |
|
270 TAny* parameter = (TAny*)info.iParameterBlock; |
|
271 |
|
272 if (info.iParameterBlockSize) |
|
273 { |
|
274 __NK_ASSERT_DEBUG(TUint(info.iParameterBlockSize) <= TUint(KMaxParameterBlock)); |
|
275 memcpy(parameterBlock, info.iParameterBlock, info.iParameterBlockSize); |
|
276 parameter = parameterBlock; |
|
277 } |
|
278 |
|
279 |
|
280 // Calculate stack base |
|
281 me.iUserStackBase = (((TLinAddr)¶meterBlock) + 0xfff) & ~0xfff; |
|
282 |
|
283 // some useful diagnostics for debugging |
|
284 if (Win32TraceThreadId) |
|
285 KPrintf("Thread %T created @ 0x%x - Win32 Thread ID 0x%x", init->iHandoff.iHoldingThread, init->iHandoff.iHoldingThread, GetCurrentThreadId()); |
|
286 |
|
287 #ifdef MONITOR_THREAD_CPU_TIME |
|
288 me.iLastStartTime = 0; // Don't count NThread setup in cpu time |
|
289 #endif |
|
290 |
|
291 // start-up complete, release the handoff mutex, which will re-suspend us |
|
292 NKern::FMSignal(&init->iHandoff); |
|
293 |
|
294 // thread has been resumed: invoke the thread function |
|
295 threadFunction(parameter); |
|
296 } |
|
297 __except (ExceptionFilter(GetExceptionInformation())) |
|
298 { |
|
299 // Do nothing - filter does all the work and hooks into EPOC |
|
300 // h/w exception mechanism if necessary by thread diversion |
|
301 } |
|
302 |
|
303 NKern::Exit(); |
|
304 return 0; |
|
305 } |
|
306 |
|
307 |
|
308 |
|
309 /** |
|
310 * Set the Win32 thread priority based on the thread type. |
|
311 * Interrupt/Event threads must be able to preempt normal |
|
312 * nKern threads, so they get a higher (Win32) priority. |
|
313 */ |
|
314 static void SetPriority(HANDLE aThread, TEmulThreadType aType) |
|
315 { |
|
316 TInt p; |
|
317 |
|
318 switch (aType) |
|
319 { |
|
320 default: |
|
321 FAULT(); |
|
322 |
|
323 case EThreadEvent: |
|
324 p = THREAD_PRIORITY_ABOVE_NORMAL; |
|
325 break; |
|
326 |
|
327 case EThreadNKern: |
|
328 p = THREAD_PRIORITY_NORMAL; |
|
329 break; |
|
330 } |
|
331 |
|
332 CheckedSetThreadPriority(aThread, p); |
|
333 SetThreadPriorityBoost(aThread, TRUE); // disable priority boost (for NT) |
|
334 } |
|
335 |
|
336 /** Create a Win32 thread for use in the emulator. |
|
337 |
|
338 @param aType Type of thread (Event or NKern) - determines Win32 priority |
|
339 @param aThreadFunc Entry point of thread |
|
340 @param aPtr Argument passed to entry point |
|
341 @param aRun TRUE if thread should be resumed immediately |
|
342 @return The Win32 handle to the thread, 0 if an error occurred |
|
343 |
|
344 @pre Call either in thread context. |
|
345 @pre Do not call from bare Win32 threads. |
|
346 |
|
347 @see TEmulThreadType |
|
348 */ |
|
349 EXPORT_C HANDLE CreateWin32Thread(TEmulThreadType aType, LPTHREAD_START_ROUTINE aThreadFunc, LPVOID aPtr, TBool aRun) |
|
350 { |
|
351 __NK_ASSERT_DEBUG(!TheScheduler.iCurrentThread || NKern::CurrentContext() == NKern::EThread); |
|
352 |
|
353 __LOCK_HOST; |
|
354 |
|
355 DWORD id; |
|
356 HANDLE handle = CreateThread(NULL , KInitialStackSize, aThreadFunc, aPtr, CREATE_SUSPENDED, &id); |
|
357 if (handle) |
|
358 { |
|
359 SetPriority(handle, aType); |
|
360 if (aRun) |
|
361 ResumeThread(handle); |
|
362 } |
|
363 return handle; |
|
364 } |
|
365 |
|
366 static HANDLE InitThread() |
|
367 // |
|
368 // Set up the initial thread and return the thread handle |
|
369 // |
|
370 { |
|
371 HANDLE p = GetCurrentProcess(); |
|
372 HANDLE me; |
|
373 DWORD r = DuplicateHandle(p, GetCurrentThread(), p, &me, 0, FALSE, DUPLICATE_SAME_ACCESS); |
|
374 __NK_ASSERT_ALWAYS(r != 0); // r is zero on error |
|
375 SetPriority(me, EThreadNKern); |
|
376 return me; |
|
377 } |
|
378 |
|
379 TInt NThread::Create(SNThreadCreateInfo& aInfo, TBool aInitial) |
|
380 { |
|
381 iWinThread = NULL; |
|
382 iWinThreadId = 0; |
|
383 iScheduleLock = NULL; |
|
384 iInKernel = 1; |
|
385 iDivertFn = NULL; |
|
386 iWakeup = aInitial ? ERelease : EResumeLocked; // mark new threads as created (=> win32 suspend) |
|
387 |
|
388 TInt r = NThreadBase::Create(aInfo, aInitial); |
|
389 if (r != KErrNone) |
|
390 return r; |
|
391 |
|
392 // the rest has to be all or nothing, we must complete it |
|
393 iScheduleLock = CreateEventA(NULL, FALSE, FALSE, NULL); |
|
394 if (iScheduleLock == NULL) |
|
395 return Emulator::LastError(); |
|
396 |
|
397 if (aInitial) |
|
398 { |
|
399 iWinThread = InitThread(); |
|
400 FastCounterInit(); |
|
401 #ifdef MONITOR_THREAD_CPU_TIME |
|
402 iLastStartTime = NKern::FastCounter(); |
|
403 #endif |
|
404 iUserStackBase = (((TLinAddr)&r) + 0xfff) & ~0xfff; // base address of stack |
|
405 SchedulerInit(*this); |
|
406 return KErrNone; |
|
407 } |
|
408 |
|
409 // create the thread proper |
|
410 // |
|
411 SCreateThread start; |
|
412 start.iInfo = &aInfo; |
|
413 iWinThread = CreateWin32Thread(EThreadNKern, &StartThread, &start, FALSE); |
|
414 if (iWinThread == NULL) |
|
415 { |
|
416 r = Emulator::LastError(); |
|
417 CloseHandle(iScheduleLock); |
|
418 return r; |
|
419 } |
|
420 |
|
421 #ifdef BTRACE_THREAD_IDENTIFICATION |
|
422 BTrace4(BTrace::EThreadIdentification, BTrace::ENanoThreadCreate, this); |
|
423 #endif |
|
424 // switch to the new thread to hand over the parameter block |
|
425 NKern::Lock(); |
|
426 ForceResume(); // mark the thread as ready |
|
427 // give the thread ownership of the handoff mutex |
|
428 start.iHandoff.iHoldingThread = this; |
|
429 iHeldFastMutex = &start.iHandoff; |
|
430 Suspend(1); // will defer as holding a fast mutex (implicit critical section) |
|
431 // do the hand-over |
|
432 start.iHandoff.Wait(); |
|
433 start.iHandoff.Signal(); |
|
434 NKern::Unlock(); |
|
435 |
|
436 return KErrNone; |
|
437 } |
|
438 |
|
439 |
|
440 |
457 void NThread::Diverted() |
441 void NThread::Diverted() |
458 // |
442 // |
459 // Forced diversion go through here, in order to 'enter' the kernel |
443 // This function is called in the context of a thread that is being diverted. |
|
444 // This can be for either of two reasons: if iDivertFn has been set, that |
|
445 // function will be called and is not expected to return i.e. it should force |
|
446 // the thread to exit. Otherwise, the thread will make a null trip through the |
|
447 // kernel, causing it to run pending user-mode callbacks on the way out. |
|
448 // |
|
449 // On entry, the kernel is locked and interrupts enabled |
460 // |
450 // |
461 { |
451 { |
462 NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread); |
452 NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread); |
463 __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); |
453 __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); |
464 __NK_ASSERT_ALWAYS(me.iDiverted); |
454 __NK_ASSERT_ALWAYS(me.iInKernel == 0); |
465 NThread::TDivert divert = me.iDivert; |
455 __NK_ASSERT_ALWAYS(me.iDiverting); |
466 me.iDiverted = EFalse; |
456 NThread::TDivert divertFn = me.iDivertFn; |
467 me.iDivert = NULL; |
457 me.iDivertFn = NULL; |
468 me.iDivertReturn = NULL; |
458 me.iDivertReturn = NULL; |
469 EnterKernel(FALSE); |
459 me.iDiverting = EFalse; |
470 if (divert) |
460 |
471 divert(); // does not return |
461 EnterKernel(TRUE); |
|
462 |
|
463 if (divertFn) |
|
464 divertFn(); // does not return |
|
465 |
472 NKern::Unlock(); |
466 NKern::Unlock(); |
473 LeaveKernel(); |
467 LeaveKernel(); |
474 } |
468 } |
475 |
469 |
476 void NThread__Diverted() |
470 void NThread__Diverted() |
508 __asm ret |
502 __asm ret |
509 } |
503 } |
510 |
504 |
511 |
505 |
512 void NThread::ApplyDiversion() |
506 void NThread::ApplyDiversion() |
|
507 // |
|
508 // Arrange that the thread will be diverted when next it runs. |
|
509 // This can be for either of two reasons: if iDivertFn has been set, |
|
510 // that function will be called and is not expected to return i.e. |
|
511 // it should force the thread to exit. Otherwise, the thread will |
|
512 // make a null trip through the kernel, causing it to run pending |
|
513 // user-mode callbacks on the way out. |
|
514 // |
|
515 // This uses the Win32 CONTEXT functions to change the thread's PC |
|
516 // so that execution restarts at DivertHook ... |
|
517 // |
513 { |
518 { |
514 // Called with interrupts disabled and kernel locked |
519 // Called with interrupts disabled and kernel locked |
515 __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); |
520 __NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1); |
516 if (iDiverted) |
521 __NK_ASSERT_ALWAYS(iDivertReturn == NULL || iDiverting); |
|
522 |
|
523 if (iDiverting) |
517 return; |
524 return; |
|
525 |
518 CONTEXT c; |
526 CONTEXT c; |
519 c.ContextFlags=CONTEXT_FULL; |
527 c.ContextFlags = CONTEXT_CONTROL; |
520 GetThreadContext(iWinThread, &c); |
528 CheckedGetThreadContext(iWinThread, &c); |
521 __NK_ASSERT_ALWAYS(iDivertReturn == NULL); |
|
522 iDivertReturn = (TAny*)c.Eip; |
529 iDivertReturn = (TAny*)c.Eip; |
523 c.Eip=(TUint32)&DivertHook; |
530 c.Eip = (TUint32)&DivertHook; |
524 SetThreadContext(iWinThread, &c); |
531 c.ContextFlags = CONTEXT_CONTROL; |
525 iDiverted = ETrue; |
532 CheckedSetThreadContext(iWinThread, &c); |
526 } |
533 iDiverting = ETrue; |
527 |
534 } |
528 void NThread::Divert(TDivert aDivert) |
535 |
529 // |
536 void NThread::Divert(TDivert aDivertFn) |
530 // Divert the thread from its current path |
537 // |
531 // The diversion function is called with the kernel locked and interrupts enabled |
538 // Arrange that the thread will exit by calling aDivertFn when next |
532 // |
539 // it runs. The diversion function will be called with the kernel |
533 { |
540 // locked and interrupts enabled. It is not expected to return. |
534 iDivert = aDivert; |
541 // |
|
542 { |
|
543 iDivertFn = aDivertFn; |
535 if (iWakeup == EResume) |
544 if (iWakeup == EResume) |
536 iWakeup = EResumeDiverted; |
545 iWakeup = EResumeDiverted; |
537 else |
546 else |
538 __NK_ASSERT_ALWAYS(iWakeup == ERelease); |
547 __NK_ASSERT_ALWAYS(iWakeup == ERelease); |
539 } |
548 } |