os/kernelhwsrv/kernel/eka/nkern/win32/ncthrd.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0
     2
// All rights reserved.
sl@0
     3
// This component and the accompanying materials are made available
sl@0
     4
// under the terms of the License "Eclipse Public License v1.0"
sl@0
     5
// which accompanies this distribution, and is available
sl@0
     6
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0
     7
//
sl@0
     8
// Initial Contributors:
sl@0
     9
// Nokia Corporation - initial contribution.
sl@0
    10
//
sl@0
    11
// Contributors:
sl@0
    12
//
sl@0
    13
// Description:
sl@0
    14
// e32\nkern\win32\ncthrd.cpp
sl@0
    15
// 
sl@0
    16
//
sl@0
    17
sl@0
    18
// NThreadBase member data
sl@0
    19
#define __INCLUDE_NTHREADBASE_DEFINES__
sl@0
    20
sl@0
    21
#include "nk_priv.h"
sl@0
    22
#include <emulator.h>
sl@0
    23
sl@0
    24
extern "C" void ExcFault(TAny*);
sl@0
    25
sl@0
    26
// initial Win32 thread stack size
sl@0
    27
const TInt KInitialStackSize = 0x1000;
sl@0
    28
sl@0
    29
// maximum size of the parameter block passed to a new thread
sl@0
    30
const TInt KMaxParameterBlock = 512;
sl@0
    31
sl@0
    32
// data passed to new thread to enable hand-off of the parameter block
sl@0
    33
struct SCreateThread
sl@0
    34
	{
sl@0
    35
	const SNThreadCreateInfo* iInfo;
sl@0
    36
	NFastMutex iHandoff;
sl@0
    37
	};
sl@0
    38
sl@0
    39
/**
sl@0
    40
 * Set the Win32 thread priority based on the thread type.
sl@0
    41
 * Interrupt/Event threads must be able to preempt normal nKern threads,
sl@0
    42
 * so they get a higher priority.
sl@0
    43
 */
sl@0
    44
static void SetPriority(HANDLE aThread, TEmulThreadType aType)
sl@0
    45
	{
sl@0
    46
	TInt p;
sl@0
    47
	switch (aType)
sl@0
    48
		{
sl@0
    49
	default:
sl@0
    50
		FAULT();
sl@0
    51
	case EThreadEvent:
sl@0
    52
		p = THREAD_PRIORITY_ABOVE_NORMAL;
sl@0
    53
		break;
sl@0
    54
	case EThreadNKern:
sl@0
    55
		p = THREAD_PRIORITY_NORMAL;
sl@0
    56
		break;
sl@0
    57
		}
sl@0
    58
sl@0
    59
	__NK_ASSERT_ALWAYS(SetThreadPriority(aThread, p));
sl@0
    60
	SetThreadPriorityBoost(aThread, TRUE);		// disable priority boost (for NT)
sl@0
    61
	}
sl@0
    62
sl@0
    63
sl@0
    64
/**	Create a Win32 thread for use in the emulator.
sl@0
    65
sl@0
    66
	@param	aType Type of thread (Event or NKern) - determines Win32 priority
sl@0
    67
	@param	aThreadFunc Entry point of thread
sl@0
    68
	@param	aPtr Argument passed to entry point
sl@0
    69
	@param	aRun TRUE if thread should be resumed immediately
sl@0
    70
	@return	The Win32 handle to the thread, 0 if an error occurred
sl@0
    71
sl@0
    72
	@pre    Call either in thread context.
sl@0
    73
	@pre	Do not call from bare Win32 threads.
sl@0
    74
sl@0
    75
	@see TEmulThreadType
sl@0
    76
 */
sl@0
    77
EXPORT_C HANDLE CreateWin32Thread(TEmulThreadType aType, LPTHREAD_START_ROUTINE aThreadFunc, LPVOID aPtr, TBool aRun)
sl@0
    78
	{
sl@0
    79
	__NK_ASSERT_DEBUG(!TheScheduler.iCurrentThread || NKern::CurrentContext() == NKern::EThread);
sl@0
    80
sl@0
    81
	__LOCK_HOST;
sl@0
    82
	
sl@0
    83
	DWORD id;
sl@0
    84
	HANDLE handle = CreateThread(NULL , KInitialStackSize, aThreadFunc, aPtr, CREATE_SUSPENDED, &id);
sl@0
    85
	if (handle)
sl@0
    86
		{
sl@0
    87
		SetPriority(handle, aType);
sl@0
    88
		if (aRun)
sl@0
    89
			ResumeThread(handle);
sl@0
    90
		}
sl@0
    91
	return handle;
sl@0
    92
	}
sl@0
    93
sl@0
    94
sl@0
    95
/** Set some global properties of the emulator
sl@0
    96
	Called by the Win32 base port during boot.
sl@0
    97
sl@0
    98
	@param	aTrace TRUE means trace Win32 thread ID for every thread created
sl@0
    99
	@param	aSingleCpu TRUE means lock the emulator process to a single CPU
sl@0
   100
sl@0
   101
	@internalTechnology
sl@0
   102
 */
sl@0
   103
EXPORT_C void NThread::SetProperties(TBool aTrace, TInt aSingleCpu)
sl@0
   104
	{
sl@0
   105
	Win32TraceThreadId = aTrace;
sl@0
   106
	Win32SingleCpu = aSingleCpu;
sl@0
   107
	}
sl@0
   108
sl@0
   109
#if defined(__CW32__) && __MWERKS__ < 0x3200
sl@0
   110
DWORD NThread__ExceptionHandler(EXCEPTION_RECORD* aException, TAny* /*aRegistrationRecord*/, CONTEXT* aContext)
sl@0
   111
//
sl@0
   112
// Hook into exception handling for old version of CW
sl@0
   113
//
sl@0
   114
	{
sl@0
   115
	return NThread::ExceptionHandler(aException, aContext);
sl@0
   116
	}
sl@0
   117
#endif // old __CW32__
sl@0
   118
sl@0
   119
DWORD WINAPI NThread::StartThread(LPVOID aParam)
sl@0
   120
//
sl@0
   121
// Win32 thread function for nKern threads.
sl@0
   122
//
sl@0
   123
// The thread first enters this function after the nScheduler has resumed
sl@0
   124
// it, following the context switch induced by the hand-off mutex.
sl@0
   125
//
sl@0
   126
// The parameter block for this thread needs to be copied into its
sl@0
   127
// own context, before releasing the mutex and handing control back to
sl@0
   128
// the creating thread.
sl@0
   129
//
sl@0
   130
	{
sl@0
   131
	SCreateThread* init = static_cast<SCreateThread*>(aParam);
sl@0
   132
	NThread& me=*static_cast<NThread*>(init->iHandoff.iHoldingThread);
sl@0
   133
	me.iWinThreadId = GetCurrentThreadId();
sl@0
   134
	SchedulerRegister(me);
sl@0
   135
#ifdef BTRACE_FAST_MUTEX
sl@0
   136
	BTraceContext4(BTrace::EFastMutex,BTrace::EFastMutexWait,&init->iHandoff);
sl@0
   137
#endif
sl@0
   138
	NKern::Unlock();
sl@0
   139
sl@0
   140
#if defined(__CW32__) && __MWERKS__ < 0x3200
sl@0
   141
	// intercept the win32 exception mechanism manually
sl@0
   142
    asm {
sl@0
   143
    	push	ebp
sl@0
   144
    	mov		eax, -1
sl@0
   145
    	push	eax
sl@0
   146
    	push	eax
sl@0
   147
    	push	offset NThread__ExceptionHandler
sl@0
   148
    	push	fs:[0]
sl@0
   149
    	mov		fs:[0], esp
sl@0
   150
sl@0
   151
		// realign the stack
sl@0
   152
    	sub		esp, 0x20
sl@0
   153
    	and		esp, ~0x1f
sl@0
   154
		}
sl@0
   155
#else // ! old __CW32__
sl@0
   156
	// intercept win32 exceptions in a debuggabble way
sl@0
   157
__try {
sl@0
   158
#endif // old __CW32__
sl@0
   159
sl@0
   160
	// save the thread entry point and parameter block
sl@0
   161
	const SNThreadCreateInfo& info = *init->iInfo;
sl@0
   162
	TUint8 parameterBlock[KMaxParameterBlock];
sl@0
   163
	TAny* parameter=(TAny*)info.iParameterBlock;
sl@0
   164
	if (info.iParameterBlockSize)
sl@0
   165
		{
sl@0
   166
		__NK_ASSERT_DEBUG(TUint(info.iParameterBlockSize)<=TUint(KMaxParameterBlock));
sl@0
   167
		parameter=parameterBlock;
sl@0
   168
		memcpy(parameterBlock,info.iParameterBlock,info.iParameterBlockSize);
sl@0
   169
		}
sl@0
   170
	NThreadFunction threadFunction=info.iFunction;
sl@0
   171
sl@0
   172
	// Calculate stack base
sl@0
   173
	me.iUserStackBase = (((TLinAddr)&parameterBlock)+0xfff)&~0xfff; // base address of stack
sl@0
   174
sl@0
   175
	// some useful diagnostics for debugging
sl@0
   176
	if (Win32TraceThreadId)
sl@0
   177
		KPrintf("Thread %T created @ 0x%x - Win32 Thread ID 0x%x",init->iHandoff.iHoldingThread,init->iHandoff.iHoldingThread,GetCurrentThreadId());
sl@0
   178
sl@0
   179
#ifdef MONITOR_THREAD_CPU_TIME
sl@0
   180
	me.iLastStartTime = 0;  // Don't count NThread setup in cpu time
sl@0
   181
#endif
sl@0
   182
 
sl@0
   183
	// start-up complete, release the handoff mutex, which will re-suspend us
sl@0
   184
	NKern::FMSignal(&init->iHandoff);
sl@0
   185
sl@0
   186
	// thread has been resumed: invoke the thread function
sl@0
   187
	threadFunction(parameter);
sl@0
   188
sl@0
   189
#if !defined(__CW32__) || __MWERKS__ >= 0x3200
sl@0
   190
	// handle win32 exceptions
sl@0
   191
} __except (ExceptionFilter(GetExceptionInformation())) {
sl@0
   192
	// Do nothing - filter does all the work and hooks
sl@0
   193
	// into EPOC h/w exception mechanism if necessary
sl@0
   194
	// by thread diversion
sl@0
   195
}
sl@0
   196
#endif // !old __CW32__
sl@0
   197
sl@0
   198
	NKern::Exit();
sl@0
   199
sl@0
   200
	return 0;
sl@0
   201
	}
sl@0
   202
sl@0
   203
static HANDLE InitThread()
sl@0
   204
//
sl@0
   205
// Set up the initial thread and return the thread handle
sl@0
   206
//
sl@0
   207
	{
sl@0
   208
	HANDLE p = GetCurrentProcess();
sl@0
   209
	HANDLE me;
sl@0
   210
	__NK_ASSERT_ALWAYS(DuplicateHandle(p, GetCurrentThread(), p, &me, 0, FALSE, DUPLICATE_SAME_ACCESS));
sl@0
   211
	SetPriority(me, EThreadNKern);
sl@0
   212
	return me;
sl@0
   213
	}
sl@0
   214
sl@0
   215
TInt NThread::Create(SNThreadCreateInfo& aInfo, TBool aInitial)
sl@0
   216
	{
sl@0
   217
	iWinThread = NULL;
sl@0
   218
	iWinThreadId = 0;
sl@0
   219
	iScheduleLock = NULL;
sl@0
   220
	iInKernel = 1;
sl@0
   221
	iDivert = NULL;
sl@0
   222
	iWakeup = aInitial ? ERelease : EResumeLocked;	// mark new threads as created (=> win32 suspend)
sl@0
   223
sl@0
   224
	TInt r=NThreadBase::Create(aInfo,aInitial);
sl@0
   225
	if (r!=KErrNone)
sl@0
   226
		return r;
sl@0
   227
sl@0
   228
	// the rest has to be all or nothing, we must complete it
sl@0
   229
	iScheduleLock = CreateEventA(NULL, FALSE, FALSE, NULL);
sl@0
   230
	if (iScheduleLock == NULL)
sl@0
   231
		return Emulator::LastError();
sl@0
   232
sl@0
   233
	if (aInitial)
sl@0
   234
		{
sl@0
   235
		iWinThread = InitThread();
sl@0
   236
		FastCounterInit();
sl@0
   237
#ifdef MONITOR_THREAD_CPU_TIME
sl@0
   238
		iLastStartTime = NKern::FastCounter();
sl@0
   239
#endif
sl@0
   240
		iUserStackBase = (((TLinAddr)&r)+0xfff)&~0xfff; // base address of stack
sl@0
   241
		SchedulerInit(*this);
sl@0
   242
		return KErrNone;
sl@0
   243
		}
sl@0
   244
sl@0
   245
	// create the thread proper
sl@0
   246
	//
sl@0
   247
	SCreateThread start;
sl@0
   248
	start.iInfo = &aInfo;
sl@0
   249
sl@0
   250
	iWinThread = CreateWin32Thread(EThreadNKern, &StartThread, &start, FALSE);
sl@0
   251
	if (iWinThread == NULL)
sl@0
   252
		{
sl@0
   253
		r = Emulator::LastError();
sl@0
   254
		CloseHandle(iScheduleLock);
sl@0
   255
		return r;
sl@0
   256
		}
sl@0
   257
sl@0
   258
#ifdef BTRACE_THREAD_IDENTIFICATION
sl@0
   259
	BTrace4(BTrace::EThreadIdentification,BTrace::ENanoThreadCreate,this);
sl@0
   260
#endif
sl@0
   261
	// switch to the new thread to hand over the parameter block
sl@0
   262
	NKern::Lock();
sl@0
   263
	ForceResume();	// mark the thread as ready
sl@0
   264
	// give the thread ownership of the handoff mutex
sl@0
   265
	start.iHandoff.iHoldingThread = this;
sl@0
   266
	iHeldFastMutex = &start.iHandoff;
sl@0
   267
	Suspend(1);		// will defer as holding a fast mutex (implicit critical section)
sl@0
   268
	// do the hand-over
sl@0
   269
	start.iHandoff.Wait();
sl@0
   270
	start.iHandoff.Signal();
sl@0
   271
	NKern::Unlock();
sl@0
   272
sl@0
   273
	return KErrNone;
sl@0
   274
	}
sl@0
   275
sl@0
   276
void NThread__HandleException(TWin32ExcInfo aExc)
sl@0
   277
//
sl@0
   278
// Final stage NKern exception handler.
sl@0
   279
//
sl@0
   280
// Check for a fatal exception when the kernel is locked
sl@0
   281
//
sl@0
   282
// Note that the parameter struct is passed by value, this allows for
sl@0
   283
// direct access to the exception context created on the call stack by
sl@0
   284
// NThread::Exception().
sl@0
   285
//
sl@0
   286
	{
sl@0
   287
	if (TheScheduler.iKernCSLocked)
sl@0
   288
		ExcFault(&aExc);
sl@0
   289
sl@0
   290
	// Complete the exception data. Note that the call to EnterKernel() in
sl@0
   291
	// ExceptionFilter() will have incremented iInKernel after the exception
sl@0
   292
	// occurred.
sl@0
   293
	NThread* me = static_cast<NThread*>(TheScheduler.iCurrentThread);
sl@0
   294
	__NK_ASSERT_DEBUG(me->iInKernel);
sl@0
   295
	aExc.iFlags = me->iInKernel == 1 ? 0 : TWin32ExcInfo::EExcInKernel;
sl@0
   296
	aExc.iHandler = NULL;
sl@0
   297
sl@0
   298
	// run NThread exception handler in 'kernel' mode
sl@0
   299
	me->iHandlers->iExceptionHandler(&aExc, me);
sl@0
   300
	LeaveKernel();
sl@0
   301
sl@0
   302
	// If a 'user' handler is set by the kernel handler, run it
sl@0
   303
	if (aExc.iHandler)
sl@0
   304
		aExc.iHandler(aExc.iParam[0], aExc.iParam[1]);
sl@0
   305
	}
sl@0
   306
sl@0
   307
void NKern__Unlock()
sl@0
   308
//
sl@0
   309
// CW asm ICE workaround
sl@0
   310
//
sl@0
   311
	{
sl@0
   312
	NKern::Unlock();
sl@0
   313
	}
sl@0
   314
sl@0
   315
__NAKED__ void NThread::Exception()
sl@0
   316
//
sl@0
   317
// Trampoline to nKern exception handler
sl@0
   318
// must preserve all registers in the structure defined by TWin32Exc
sl@0
   319
//
sl@0
   320
	{
sl@0
   321
	// this is the TWin32Exc structure
sl@0
   322
	__asm push Win32ExcAddress			// save return address followed by EBP first to help debugger
sl@0
   323
	__asm push ebp
sl@0
   324
	__asm mov ebp, esp
sl@0
   325
	__asm push cs
sl@0
   326
	__asm pushfd
sl@0
   327
	__asm push gs
sl@0
   328
	__asm push fs
sl@0
   329
	__asm push es
sl@0
   330
	__asm push ds
sl@0
   331
	__asm push ss
sl@0
   332
	__asm push edi
sl@0
   333
	__asm push esi
sl@0
   334
	__asm lea esi, [ebp+8]
sl@0
   335
	__asm push esi		// original esp
sl@0
   336
	__asm push ebx
sl@0
   337
	__asm push edx
sl@0
   338
	__asm push ecx
sl@0
   339
	__asm push eax
sl@0
   340
	__asm push Win32ExcDataAddress
sl@0
   341
	__asm push Win32ExcCode
sl@0
   342
	__asm sub esp, 20	// struct init completed by NThread__HandleException()
sl@0
   343
sl@0
   344
	__asm call NKern__Unlock
sl@0
   345
sl@0
   346
	__asm call NThread__HandleException
sl@0
   347
sl@0
   348
	__asm add esp, 28
sl@0
   349
	__asm pop eax
sl@0
   350
	__asm pop ecx
sl@0
   351
	__asm pop edx
sl@0
   352
	__asm pop ebx
sl@0
   353
	__asm pop esi		// original ESP - ignore
sl@0
   354
	__asm pop esi
sl@0
   355
	__asm pop edi
sl@0
   356
	__asm pop ebp		// original SS - ignore
sl@0
   357
	__asm pop ds
sl@0
   358
	__asm pop es
sl@0
   359
	__asm pop fs
sl@0
   360
	__asm pop gs
sl@0
   361
	__asm popfd
sl@0
   362
	__asm pop ebp		// original CS - ignore
sl@0
   363
	__asm pop ebp
sl@0
   364
	__asm ret
sl@0
   365
	}
sl@0
   366
sl@0
   367
LONG WINAPI NThread::ExceptionFilter(EXCEPTION_POINTERS* aExc)
sl@0
   368
//
sl@0
   369
// Filter wrapper for main Win32 exception handler
sl@0
   370
//
sl@0
   371
	{
sl@0
   372
	LONG ret = EXCEPTION_CONTINUE_SEARCH;
sl@0
   373
sl@0
   374
	switch (ExceptionHandler(aExc->ExceptionRecord, aExc->ContextRecord))
sl@0
   375
		{
sl@0
   376
		case ExceptionContinueExecution:
sl@0
   377
			{
sl@0
   378
			ret = EXCEPTION_CONTINUE_EXECUTION;
sl@0
   379
			}
sl@0
   380
			break;
sl@0
   381
		case ExceptionContinueSearch:
sl@0
   382
		default:
sl@0
   383
			{
sl@0
   384
			}
sl@0
   385
			break;
sl@0
   386
		}
sl@0
   387
sl@0
   388
	return ret;
sl@0
   389
	}
sl@0
   390
sl@0
   391
// From e32/commmon/win32/seh.cpp
sl@0
   392
extern DWORD CallFinalSEHHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext);
sl@0
   393
sl@0
   394
extern void DivertHook();
sl@0
   395
sl@0
   396
DWORD NThread::ExceptionHandler(EXCEPTION_RECORD* aException, CONTEXT* aContext)
sl@0
   397
//
sl@0
   398
// Win32 exception handler for EPOC threads
sl@0
   399
//
sl@0
   400
	{
sl@0
   401
	if (aException->ExceptionCode == EXCEPTION_BREAKPOINT)
sl@0
   402
		{
sl@0
   403
		// Hardcoded breakpoint
sl@0
   404
		//
sl@0
   405
		// Jump directly to NT's default unhandled exception handler which will
sl@0
   406
		// either display a dialog, directly invoke the JIT debugger or do nothing
sl@0
   407
		// dependent upon the AeDebug and ErrorMode registry settings.
sl@0
   408
		//
sl@0
   409
		// Note this handler is always installed on the SEH chain and is always
sl@0
   410
		// the last handler on this chain, as it is installed by NT in kernel32.dll
sl@0
   411
		// before invoking the Win32 thread function.
sl@0
   412
		return CallFinalSEHHandler(aException, aContext);
sl@0
   413
		}
sl@0
   414
sl@0
   415
	// deal with conflict between preemption and diversion
sl@0
   416
	// the diversion will have been applied to the pre-exception context, not
sl@0
   417
	// the current context, and thus will get 'lost'. Wake-up of a pre-empted
sl@0
   418
	// thread with a diversion will not unlock the kernel, so need to deal with
sl@0
   419
	// the possibility that the kernel may be locked if a diversion exists
sl@0
   420
sl@0
   421
	NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread);
sl@0
   422
	if (me.iDiverted && me.iDivert)
sl@0
   423
		{
sl@0
   424
		// The thread is being forced to exit - run the diversion outside of Win32 exception handler
sl@0
   425
		__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
sl@0
   426
		aContext->Eip = (TUint32)&DivertHook;
sl@0
   427
		}
sl@0
   428
	else
sl@0
   429
		{
sl@0
   430
		if (me.iDiverted)
sl@0
   431
			{
sl@0
   432
			// The thread is being prodded to pick up its callbacks.  This will happen when the
sl@0
   433
			// exception handler calls LeaveKernel(), so we can remove the diversion
sl@0
   434
			__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
sl@0
   435
			if (aException->ExceptionAddress == &DivertHook)
sl@0
   436
				aException->ExceptionAddress = me.iDivertReturn;
sl@0
   437
			me.iDiverted = EFalse;
sl@0
   438
			me.iDivertReturn = NULL;
sl@0
   439
			EnterKernel(FALSE);
sl@0
   440
			}
sl@0
   441
		else
sl@0
   442
			{
sl@0
   443
			EnterKernel();
sl@0
   444
			TheScheduler.iKernCSLocked = 1;	// prevent pre-emption
sl@0
   445
			}
sl@0
   446
		
sl@0
   447
		// If the kernel was already locked, this will be detected in the next stage handler
sl@0
   448
		// run 2nd stage handler outside of Win32 exception context
sl@0
   449
		Win32ExcAddress = aException->ExceptionAddress;
sl@0
   450
		Win32ExcDataAddress = (TAny*)aException->ExceptionInformation[1];
sl@0
   451
		Win32ExcCode = aException->ExceptionCode;
sl@0
   452
		aContext->Eip = (TUint32)&Exception;
sl@0
   453
		}
sl@0
   454
	return ExceptionContinueExecution;
sl@0
   455
	}
sl@0
   456
sl@0
   457
void NThread::Diverted()
sl@0
   458
//
sl@0
   459
// Forced diversion go through here, in order to 'enter' the kernel
sl@0
   460
//
sl@0
   461
	{
sl@0
   462
	NThread& me = *static_cast<NThread*>(TheScheduler.iCurrentThread);
sl@0
   463
	__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
sl@0
   464
	__NK_ASSERT_ALWAYS(me.iDiverted);
sl@0
   465
	NThread::TDivert divert = me.iDivert;
sl@0
   466
	me.iDiverted = EFalse;
sl@0
   467
	me.iDivert = NULL;
sl@0
   468
	me.iDivertReturn = NULL;
sl@0
   469
	EnterKernel(FALSE);
sl@0
   470
	if (divert)
sl@0
   471
		divert();	// does not return
sl@0
   472
	NKern::Unlock();
sl@0
   473
	LeaveKernel();
sl@0
   474
	}
sl@0
   475
sl@0
   476
void NThread__Diverted()
sl@0
   477
	{
sl@0
   478
	NThread::Diverted();
sl@0
   479
	}
sl@0
   480
sl@0
   481
__NAKED__ void DivertHook()
sl@0
   482
	{
sl@0
   483
	// The stack frame is set up like this:
sl@0
   484
	//
sl@0
   485
	//		| return address |
sl@0
   486
	//		| frame pointer  |
sl@0
   487
	//		| flags			 |
sl@0
   488
	//		| saved eax		 |
sl@0
   489
	//		| saved ecx		 |
sl@0
   490
	//      | saved edx		 |
sl@0
   491
	//		
sl@0
   492
	__asm push eax					// reserve word for return address
sl@0
   493
	__asm push ebp
sl@0
   494
	__asm mov ebp, esp 
sl@0
   495
	__asm pushfd
sl@0
   496
	__asm push eax
sl@0
   497
	__asm push ecx
sl@0
   498
	__asm push edx
sl@0
   499
	__asm mov eax, [TheScheduler.iCurrentThread]
sl@0
   500
	__asm mov eax, [eax]NThread.iDivertReturn
sl@0
   501
	__asm mov [esp + 20], eax		// store return address
sl@0
   502
	__asm call NThread__Diverted
sl@0
   503
	__asm pop edx
sl@0
   504
	__asm pop ecx
sl@0
   505
	__asm pop eax
sl@0
   506
	__asm popfd
sl@0
   507
	__asm pop ebp
sl@0
   508
	__asm ret
sl@0
   509
	}
sl@0
   510
sl@0
   511
sl@0
   512
void NThread::ApplyDiversion()
sl@0
   513
	{
sl@0
   514
	// Called with interrupts disabled and kernel locked
sl@0
   515
	__NK_ASSERT_ALWAYS(TheScheduler.iKernCSLocked == 1);
sl@0
   516
	if (iDiverted)
sl@0
   517
		return;
sl@0
   518
	CONTEXT c;
sl@0
   519
	c.ContextFlags=CONTEXT_FULL;
sl@0
   520
	GetThreadContext(iWinThread, &c);
sl@0
   521
	__NK_ASSERT_ALWAYS(iDivertReturn == NULL);
sl@0
   522
	iDivertReturn = (TAny*)c.Eip;
sl@0
   523
	c.Eip=(TUint32)&DivertHook;
sl@0
   524
	SetThreadContext(iWinThread, &c);
sl@0
   525
	iDiverted = ETrue;
sl@0
   526
	}
sl@0
   527
sl@0
   528
void NThread::Divert(TDivert aDivert)
sl@0
   529
//
sl@0
   530
// Divert the thread from its current path
sl@0
   531
// The diversion function is called with the kernel locked and interrupts enabled
sl@0
   532
//
sl@0
   533
	{
sl@0
   534
	iDivert = aDivert;
sl@0
   535
	if (iWakeup == EResume)
sl@0
   536
		iWakeup = EResumeDiverted;
sl@0
   537
	else
sl@0
   538
		__NK_ASSERT_ALWAYS(iWakeup == ERelease);
sl@0
   539
	}
sl@0
   540
sl@0
   541
void NThread::ExitSync()
sl@0
   542
//
sl@0
   543
// Diversion used to terminate 'stillborn' threads.
sl@0
   544
// On entry, kernel is locked, interrupts are enabled and we hold an interlock mutex
sl@0
   545
//
sl@0
   546
	{
sl@0
   547
	NThreadBase& me=*TheScheduler.iCurrentThread;
sl@0
   548
	me.iHeldFastMutex->Signal();	// release the interlock
sl@0
   549
	me.iNState=EDead;				// mark ourselves as dead which will take thread out of scheduler
sl@0
   550
	TheScheduler.Remove(&me);
sl@0
   551
	RescheduleNeeded();
sl@0
   552
	TScheduler::Reschedule();	// this won't return
sl@0
   553
	FAULT();
sl@0
   554
	}
sl@0
   555
sl@0
   556
void NThread::Stillborn()
sl@0
   557
//
sl@0
   558
// Called if the new thread creation was aborted - so it will not be killed in the usual manner
sl@0
   559
//
sl@0
   560
// This function needs to exit the thread synchronously as on return we will destroy the thread control block
sl@0
   561
// Thus wee need to use an interlock that ensure that the target thread runs the exit handler before we continue
sl@0
   562
//
sl@0
   563
	{
sl@0
   564
	// check if the Win32 thread was created
sl@0
   565
	if (!iWinThread)
sl@0
   566
		return;
sl@0
   567
sl@0
   568
	NKern::Lock();
sl@0
   569
	Divert(&ExitSync);
sl@0
   570
	ForceResume();
sl@0
   571
	// create and assign mutex to stillborn thread
sl@0
   572
	NFastMutex interlock;
sl@0
   573
	interlock.iHoldingThread=this;
sl@0
   574
	iHeldFastMutex=&interlock;
sl@0
   575
	interlock.Wait();			// interlock on thread exit handler
sl@0
   576
	interlock.Signal();
sl@0
   577
	NKern::Unlock();
sl@0
   578
	}
sl@0
   579
sl@0
   580
void NThread::ExitAsync()
sl@0
   581
//
sl@0
   582
// Diversion used to terminate 'killed' threads.
sl@0
   583
// On entry, kernel is locked and interrupts are enabled
sl@0
   584
//
sl@0
   585
	{
sl@0
   586
	NThreadBase& me = *TheScheduler.iCurrentThread;
sl@0
   587
	me.iCsCount = 0;
sl@0
   588
	__NK_ASSERT_ALWAYS(static_cast<NThread&>(me).iInKernel>0);
sl@0
   589
	me.Exit();
sl@0
   590
	}
sl@0
   591
sl@0
   592
void NThreadBase::OnKill()
sl@0
   593
	{
sl@0
   594
	}
sl@0
   595
sl@0
   596
void NThreadBase::OnExit()
sl@0
   597
	{
sl@0
   598
	}
sl@0
   599
sl@0
   600
inline void NThread::DoForceExit()
sl@0
   601
	{
sl@0
   602
	__NK_ASSERT_DEBUG(TheScheduler.iKernCSLocked);
sl@0
   603
//
sl@0
   604
	Divert(&ExitAsync);
sl@0
   605
	}
sl@0
   606
sl@0
   607
void NThreadBase::ForceExit()
sl@0
   608
//
sl@0
   609
// Called to force the thread to exit when it resumes
sl@0
   610
//
sl@0
   611
	{
sl@0
   612
	static_cast<NThread*>(this)->DoForceExit();
sl@0
   613
	}
sl@0
   614
sl@0
   615
//
sl@0
   616
// We need a global lock in the emulator to avoid scheduling reentrancy problems with the host
sl@0
   617
// in particular, some host API calls acquire host mutexes, preempting such services results
sl@0
   618
// in suspension of those threads which can cause deadlock if another thread requires that host
sl@0
   619
// mutex.
sl@0
   620
//
sl@0
   621
// Because thread dreaction and code loading also require the same underlying mutex (used
sl@0
   622
// by NT to protect DLL entrypoint calling), this would be rather complex with a fast mutex.
sl@0
   623
// For now, keep it simple and use the preemption lock. Note that this means that the
sl@0
   624
// MS timer DFC may be significantly delayed when loading large DLL trees, for example.
sl@0
   625
//
sl@0
   626
sl@0
   627
void SchedulerLock()
sl@0
   628
//
sl@0
   629
// Acquire the global lock. May be called before scheduler running, so handle that case
sl@0
   630
//
sl@0
   631
	{
sl@0
   632
	if (TheScheduler.iCurrentThread)
sl@0
   633
		{
sl@0
   634
		EnterKernel();
sl@0
   635
		NKern::Lock();
sl@0
   636
		}
sl@0
   637
	}
sl@0
   638
sl@0
   639
void SchedulerUnlock()
sl@0
   640
//
sl@0
   641
// Release the global lock. May be called before scheduler running, so handle that case
sl@0
   642
//
sl@0
   643
	{
sl@0
   644
	if (TheScheduler.iCurrentThread)
sl@0
   645
		{
sl@0
   646
		NKern::Unlock();
sl@0
   647
		LeaveKernel();
sl@0
   648
		}
sl@0
   649
	}
sl@0
   650