sl@0: // Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32\nkern\nkerns.cpp sl@0: // sl@0: // sl@0: sl@0: // NThreadBase member data sl@0: #define __INCLUDE_NTHREADBASE_DEFINES__ sl@0: sl@0: #include sl@0: #include sl@0: #include "nk_priv.h" sl@0: sl@0: extern "C" void ExcFault(TAny*); sl@0: sl@0: /****************************************************************************** sl@0: * Thread sl@0: ******************************************************************************/ sl@0: sl@0: void InvalidExec() sl@0: { sl@0: FAULT(); sl@0: } sl@0: sl@0: static const SFastExecTable DefaultFastExecTable={0,{0}}; sl@0: static const SSlowExecTable DefaultSlowExecTable={0,(TLinAddr)InvalidExec,0,{{0,0}}}; sl@0: sl@0: const SNThreadHandlers NThread_Default_Handlers = sl@0: { sl@0: NTHREAD_DEFAULT_EXIT_HANDLER, sl@0: NTHREAD_DEFAULT_STATE_HANDLER, sl@0: NTHREAD_DEFAULT_EXCEPTION_HANDLER, sl@0: NTHREAD_DEFAULT_TIMEOUT_HANDLER sl@0: }; sl@0: sl@0: /** Create a fast mutex sl@0: sl@0: @publishedPartner sl@0: @released sl@0: */ sl@0: EXPORT_C NFastMutex::NFastMutex() sl@0: : iHoldingThread(0), iWaiting(0) sl@0: { sl@0: } sl@0: sl@0: /** Create a spin lock sl@0: sl@0: @internalComponent sl@0: */ sl@0: EXPORT_C TSpinLock::TSpinLock(TUint) sl@0: : iLock(0) sl@0: { sl@0: } sl@0: sl@0: /** Create a R/W spin lock sl@0: sl@0: @internalComponent sl@0: */ sl@0: EXPORT_C TRWSpinLock::TRWSpinLock(TUint) sl@0: : iLock(0) sl@0: { sl@0: } sl@0: sl@0: NThreadBase::NThreadBase() sl@0: { sl@0: // from TPriListLink sl@0: iPriority = 0; sl@0: iSpare1 = 0; sl@0: iSpare2 = 0; sl@0: iSpare3 = 0; sl@0: sl@0: iRequestSemaphore.iOwningThread=(NThreadBase*)this; sl@0: new (&iTimer) NTimer(TimerExpired,this); sl@0: iRequestSemaphore.iOwningThread = this; sl@0: sl@0: iHeldFastMutex = 0; sl@0: iWaitFastMutex = 0; sl@0: iAddressSpace = 0; sl@0: iTime = 0; sl@0: iTimeslice = 0; sl@0: iWaitObj = 0; sl@0: iSuspendCount = 0; sl@0: iCsCount = 0; sl@0: iCsFunction = 0; sl@0: iReturnValue = 0; sl@0: iStackBase = 0; sl@0: iStackSize = 0; sl@0: iHandlers = 0; sl@0: iFastExecTable = 0; sl@0: iSlowExecTable = 0; sl@0: iSavedSP = 0; sl@0: iExtraContext = 0; sl@0: iExtraContextSize = 0; sl@0: iLastStartTime = 0; sl@0: iTotalCpuTime = 0; sl@0: iTag = 0; sl@0: iVemsData = 0; sl@0: iUserModeCallbacks = 0; sl@0: iSpare7 = 0; sl@0: iSpare8 = 0; sl@0: } sl@0: sl@0: TInt NThreadBase::Create(SNThreadCreateInfo& aInfo, TBool aInitial) sl@0: { sl@0: if (aInfo.iPriority<0 || aInfo.iPriority>63) sl@0: return KErrArgument; sl@0: if (aInfo.iPriority==0 && !aInitial) sl@0: return KErrArgument; sl@0: new (this) NThreadBase; sl@0: iStackBase=(TLinAddr)aInfo.iStackBase; sl@0: iStackSize=aInfo.iStackSize; sl@0: iTimeslice=(aInfo.iTimeslice>0)?aInfo.iTimeslice:-1; sl@0: iTime=iTimeslice; sl@0: #ifdef _DEBUG sl@0: // When the crazy scheduler is active, refuse to set any priority higher than 1 sl@0: if (KCrazySchedulerEnabled()) sl@0: iPriority=TUint8(Min(1,aInfo.iPriority)); sl@0: else sl@0: #endif sl@0: { sl@0: iPriority=TUint8(aInfo.iPriority); sl@0: } sl@0: iHandlers = aInfo.iHandlers ? aInfo.iHandlers : &NThread_Default_Handlers; sl@0: iFastExecTable=aInfo.iFastExecTable?aInfo.iFastExecTable:&DefaultFastExecTable; sl@0: iSlowExecTable=(aInfo.iSlowExecTable?aInfo.iSlowExecTable:&DefaultSlowExecTable)->iEntries; sl@0: iSpare2=(TUint8)aInfo.iAttributes; // iSpare2 is NThread attributes sl@0: if (aInitial) sl@0: { sl@0: iNState=EReady; sl@0: iSuspendCount=0; sl@0: TheScheduler.Add(this); sl@0: TheScheduler.iCurrentThread=this; sl@0: TheScheduler.iKernCSLocked=0; // now that current thread is defined sl@0: } sl@0: else sl@0: { sl@0: iNState=ESuspended; sl@0: iSuspendCount=-1; sl@0: } sl@0: return KErrNone; sl@0: } sl@0: sl@0: void NThread_Default_State_Handler(NThread* __DEBUG_ONLY(aThread), TInt __DEBUG_ONLY(aOperation), TInt __DEBUG_ONLY(aParameter)) sl@0: { sl@0: __KTRACE_OPT(KPANIC,DEBUGPRINT("Unknown NState %d: thread %T op %08x par %08x",aThread,aThread->iNState,aOperation,aParameter)); sl@0: FAULT(); sl@0: } sl@0: sl@0: void NThread_Default_Exception_Handler(TAny* aContext, NThread*) sl@0: { sl@0: ExcFault(aContext); sl@0: } sl@0: sl@0: sl@0: /** Create a nanothread. sl@0: sl@0: This function is intended to be used by the EPOC kernel and by personality sl@0: layers. A nanothread may not use most of the functions available to normal sl@0: Symbian OS threads. Use Kern::ThreadCreate() to create a Symbian OS thread. sl@0: sl@0: @param aThread Pointer to control block for thread to create. sl@0: @param aInfo Information needed for creating the thread. sl@0: sl@0: @see SNThreadCreateInfo sl@0: @see Kern::ThreadCreate sl@0: sl@0: @pre Call in a thread context. sl@0: @pre Interrupts must be enabled. sl@0: @pre Kernel must be unlocked. sl@0: */ sl@0: EXPORT_C TInt NKern::ThreadCreate(NThread* aThread, SNThreadCreateInfo& aInfo) sl@0: { sl@0: CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_INTERRUPTS_ENABLED|MASK_NOT_ISR|MASK_NOT_IDFC,"NKern::ThreadCreate"); sl@0: return aThread->Create(aInfo,FALSE); sl@0: } sl@0: sl@0: // User-mode callbacks sl@0: sl@0: TUserModeCallback::TUserModeCallback(TUserModeCallbackFunc aFunc) : sl@0: iNext(KUserModeCallbackUnqueued), sl@0: iFunc(aFunc) sl@0: { sl@0: } sl@0: sl@0: TUserModeCallback::~TUserModeCallback() sl@0: { sl@0: __NK_ASSERT_DEBUG(iNext == KUserModeCallbackUnqueued); sl@0: } sl@0: sl@0: TInt NKern::QueueUserModeCallback(NThreadBase* aThread, TUserModeCallback* aCallback) sl@0: { sl@0: if (aCallback->iNext != KUserModeCallbackUnqueued) sl@0: return KErrInUse; sl@0: TInt r = KErrDied; sl@0: NKern::Lock(); sl@0: TUserModeCallback* listHead = aThread->iUserModeCallbacks; sl@0: if (((TLinAddr)listHead & 3) == 0) sl@0: { sl@0: aCallback->iNext = listHead; sl@0: aThread->iUserModeCallbacks = aCallback; sl@0: r = KErrNone; sl@0: } sl@0: NKern::Unlock(); sl@0: return r; sl@0: } sl@0: sl@0: // Called with interrupts disabled sl@0: // The vast majority of times this is called with zero or one callback pending sl@0: void NThreadBase::CallUserModeCallbacks() sl@0: { sl@0: while (iUserModeCallbacks != NULL) sl@0: { sl@0: // Remove first callback sl@0: TUserModeCallback* callback = iUserModeCallbacks; sl@0: iUserModeCallbacks = callback->iNext; sl@0: sl@0: // Enter critical section to ensure callback is called sl@0: NKern::ThreadEnterCS(); sl@0: sl@0: // Re-enable interrupts and call callback sl@0: NKern::EnableAllInterrupts(); sl@0: callback->iNext = KUserModeCallbackUnqueued; sl@0: callback->iFunc(callback, EUserModeCallbackRun); sl@0: sl@0: // Leave critical section: thread may die at this point sl@0: NKern::ThreadLeaveCS(); sl@0: sl@0: NKern::DisableAllInterrupts(); sl@0: } sl@0: } sl@0: sl@0: void NKern::CancelUserModeCallbacks() sl@0: { sl@0: // Call any queued callbacks with the EUserModeCallbackCancel reason code, in the current sl@0: // thread. sl@0: sl@0: NThreadBase* thread = NCurrentThread(); sl@0: NKern::Lock(); sl@0: TUserModeCallback* listHead = thread->iUserModeCallbacks; sl@0: thread->iUserModeCallbacks = NULL; sl@0: NKern::Unlock(); sl@0: sl@0: while (listHead != NULL) sl@0: { sl@0: TUserModeCallback* callback = listHead; sl@0: listHead = listHead->iNext; sl@0: callback->iNext = KUserModeCallbackUnqueued; sl@0: callback->iFunc(callback, EUserModeCallbackCancel); sl@0: } sl@0: } sl@0: sl@0: void NKern::MoveUserModeCallbacks(NThreadBase* aDestThread, NThreadBase* aSrcThread) sl@0: { sl@0: // Move all queued user-mode callbacks from the source thread to the destination thread, and sl@0: // prevent any more from being queued. Used by the kernel thread code so that callbacks get sl@0: // cancelled in another thread if the thread they were originally queued on dies. sl@0: sl@0: NKern::Lock(); sl@0: TUserModeCallback* sourceListStart = aSrcThread->iUserModeCallbacks; sl@0: aSrcThread->iUserModeCallbacks = (TUserModeCallback*)1; sl@0: NKern::Unlock(); sl@0: __NK_ASSERT_DEBUG(((TUint)sourceListStart & 3) == 0); // check this only gets called once per thread sl@0: sl@0: if (sourceListStart == NULL) sl@0: return; sl@0: sl@0: TUserModeCallback* sourceListEnd = sourceListStart; sl@0: while (sourceListEnd->iNext != NULL) sl@0: sourceListEnd = sourceListEnd->iNext; sl@0: sl@0: NKern::Lock(); sl@0: TUserModeCallback* destListStart = aDestThread->iUserModeCallbacks; sl@0: __NK_ASSERT_DEBUG(((TUint)destListStart & 3) == 0); sl@0: sourceListEnd->iNext = destListStart; sl@0: aDestThread->iUserModeCallbacks = sourceListStart; sl@0: NKern::Unlock(); sl@0: } sl@0: sl@0: /** Initialise the null thread sl@0: @internalComponent sl@0: */ sl@0: void NKern::Init(NThread* aThread, SNThreadCreateInfo& aInfo) sl@0: { sl@0: aInfo.iFunction=NULL; // irrelevant sl@0: aInfo.iPriority=0; // null thread has lowest priority sl@0: aInfo.iTimeslice=0; // null thread not timesliced sl@0: aInfo.iAttributes=0; // null thread does not require implicit locks sl@0: aThread->Create(aInfo,TRUE); // create the null thread sl@0: } sl@0: sl@0: extern "C" { sl@0: TUint32 CrashState; sl@0: } sl@0: sl@0: EXPORT_C TBool NKern::Crashed() sl@0: { sl@0: return CrashState!=0; sl@0: } sl@0: sl@0: sl@0: /** @internalTechnology */ sl@0: EXPORT_C void NKern::RecordIntLatency(TInt /*aLatency*/, TInt /*aIntMask*/) sl@0: { sl@0: } sl@0: sl@0: sl@0: /** @internalTechnology */ sl@0: EXPORT_C void NKern::RecordThreadLatency(TInt /*aLatency*/) sl@0: { sl@0: } sl@0: sl@0: /******************************************** sl@0: * Deterministic Priority List Implementation sl@0: ********************************************/ sl@0: sl@0: sl@0: /** Construct a priority list with the specified number of priorities sl@0: sl@0: @param aNumPriorities The number of priorities (must be 1-64). sl@0: */ sl@0: EXPORT_C TPriListBase::TPriListBase(TInt aNumPriorities) sl@0: { sl@0: memclr(this, sizeof(TPriListBase)+(aNumPriorities-1)*sizeof(SDblQueLink*) ); sl@0: } sl@0: sl@0: sl@0: /******************************************** sl@0: * Miscellaneous sl@0: ********************************************/ sl@0: sl@0: sl@0: /** Returns number of nanokernel timer ticks since system started. sl@0: @return tick count sl@0: @pre any context sl@0: */ sl@0: EXPORT_C TUint32 NKern::TickCount() sl@0: { sl@0: return NTickCount(); sl@0: } sl@0: sl@0: sl@0: TUint32 BTrace::BigTraceId = 0; sl@0: sl@0: TBool BTrace::DoOutBig(TUint32 a0, TUint32 a1, const TAny* aData, TInt aDataSize, TUint32 aContext, TUint32 aPc) sl@0: { sl@0: SBTraceData& traceData = BTraceData; sl@0: sl@0: // see if trace is small enough to fit in single record... sl@0: if(TUint(aDataSize)<=TUint(KMaxBTraceDataArray+4)) sl@0: { sl@0: a0 += aDataSize; sl@0: TUint32 a2 = 0; sl@0: TUint32 a3 = 0; sl@0: if(aDataSize) sl@0: { sl@0: a2 = *((TUint32*&)aData)++; // first 4 bytes into a2 sl@0: if(aDataSize>=4 && aDataSize<=8) sl@0: a3 = *(TUint32*)aData; // only 4 more bytes, so pass by value, not pointer sl@0: else sl@0: a3 = (TUint32)aData; sl@0: } sl@0: return traceData.iHandler(a0,0,aContext,a1,a2,a3,0,aPc); sl@0: } sl@0: sl@0: // adjust for header2, extra, and size word... sl@0: a0 |= BTrace::EHeader2Present<<(BTrace::EFlagsIndex*8)|BTrace::EExtraPresent<<(BTrace::EFlagsIndex*8); sl@0: a0 += 12; sl@0: sl@0: TUint32 traceId = __e32_atomic_add_ord32(&BigTraceId, 1); sl@0: TUint32 header2 = BTrace::EMultipartFirst; sl@0: TInt offset = 0; sl@0: do sl@0: { sl@0: TUint32 size = aDataSize-offset; sl@0: if(size>KMaxBTraceDataArray) sl@0: size = KMaxBTraceDataArray; sl@0: else sl@0: header2 = BTrace::EMultipartLast; sl@0: if(size<=4) sl@0: *(TUint32*)&aData = *(TUint32*)aData; // 4 bytes or less are passed by value, not pointer sl@0: sl@0: TBool result = traceData.iHandler(a0+size,header2,aContext,aDataSize,a1,(TUint32)aData,traceId,aPc); sl@0: if(!result) sl@0: return result; sl@0: sl@0: offset += size; sl@0: *(TUint8**)&aData += size; sl@0: sl@0: header2 = BTrace::EMultipartMiddle; sl@0: a1 = offset; sl@0: } sl@0: while(offset