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\dfcs.cpp sl@0: // DFCs sl@0: // sl@0: // sl@0: sl@0: // NThreadBase member data sl@0: #define __INCLUDE_NTHREADBASE_DEFINES__ sl@0: sl@0: // TDfc member data sl@0: #define __INCLUDE_TDFC_DEFINES__ sl@0: sl@0: #include "nk_priv.h" sl@0: sl@0: sl@0: /** Construct an IDFC sl@0: sl@0: @param aFunction = function to call sl@0: @param aPtr = parameter to be passed to function sl@0: */ sl@0: EXPORT_C TDfc::TDfc(TDfcFn aFunction, TAny* aPtr) sl@0: : iPtr(aPtr), iFunction(aFunction), iDfcQ(NULL) sl@0: { sl@0: iPriority=0xff; sl@0: iSpare1=0; sl@0: iOnFinalQ=FALSE; sl@0: iQueued=FALSE; sl@0: } sl@0: sl@0: sl@0: /** Construct a DFC without specifying a DFC queue. sl@0: The DFC queue must be set before the DFC may be queued. sl@0: sl@0: @param aFunction = function to call sl@0: @param aPtr = parameter to be passed to function sl@0: @param aPriority = priority of DFC within the queue (0 to 7, where 7 is highest) sl@0: */ sl@0: EXPORT_C TDfc::TDfc(TDfcFn aFunction, TAny* aPtr, TInt aPriority) sl@0: : iPtr(aPtr), iFunction(aFunction), iDfcQ(NULL) sl@0: { sl@0: __NK_ASSERT_DEBUG((TUint)aPriority<(TUint)KNumDfcPriorities); sl@0: iPriority=TUint8(aPriority); sl@0: iSpare1=0; sl@0: iOnFinalQ=FALSE; sl@0: iQueued=FALSE; sl@0: } sl@0: sl@0: sl@0: /** Construct a DFC specifying a DFC queue. sl@0: sl@0: @param aFunction = function to call sl@0: @param aPtr = parameter to be passed to function sl@0: @param aDfcQ = pointer to DFC queue which this DFC should use sl@0: @param aPriority = priority of DFC within the queue (0-7) sl@0: */ sl@0: EXPORT_C TDfc::TDfc(TDfcFn aFunction, TAny* aPtr, TDfcQue* aDfcQ, TInt aPriority) sl@0: : iPtr(aPtr), iFunction(aFunction), iDfcQ(aDfcQ) sl@0: { sl@0: __NK_ASSERT_DEBUG((TUint)aPriority<(TUint)KNumDfcPriorities); sl@0: iPriority=TUint8(aPriority); sl@0: iSpare1=0; sl@0: iOnFinalQ=FALSE; sl@0: iQueued=FALSE; sl@0: } sl@0: sl@0: sl@0: /** Construct a DFC queue sl@0: Kern::DfcQInit() should be called on the new DFC queue before it can be used. sl@0: */ sl@0: EXPORT_C TDfcQue::TDfcQue() sl@0: : iThread(NULL) sl@0: {} sl@0: sl@0: sl@0: #ifndef __DFC_MACHINE_CODED__ sl@0: sl@0: /** Queue an IDFC or a DFC from an ISR sl@0: sl@0: This function is the only way to queue an IDFC and is the only way to queue sl@0: a DFC from an ISR. To queue a DFC from an IDFC or a thread either Enque() sl@0: or DoEnque() should be used. sl@0: sl@0: This function does nothing if the IDFC/DFC is already queued. sl@0: sl@0: @pre Call only from ISR, IDFC or thread with preemption disabled. sl@0: @pre Do not call from thread with preemption enabled. sl@0: @return TRUE if DFC was actually queued by this call sl@0: FALSE if DFC was already queued on entry so this call did nothing sl@0: @see TDfc::DoEnque() sl@0: @see TDfc::Enque() sl@0: */ sl@0: EXPORT_C TBool TDfc::Add() sl@0: { sl@0: __ASSERT_WITH_MESSAGE_DEBUG( NKern::CurrentContext()!=NKern::EThread || TheScheduler.iKernCSLocked,"Do not call from thread with preemption enabled","TDfc::Add"); sl@0: __ASSERT_WITH_MESSAGE_DEBUG( IsIDFC() || iDfcQ != NULL, "DFC queue not set", "TDfc::Add"); sl@0: #ifdef __WINS__ sl@0: __NK_ASSERT_ALWAYS(Interrupt.InInterrupt() || TheScheduler.iKernCSLocked); sl@0: #endif sl@0: return RawAdd(); sl@0: } sl@0: sl@0: sl@0: /** Queue an IDFC or a DFC from an ISR sl@0: sl@0: This function is identical to TDfc::Add() but no checks are performed for correct usage, sl@0: and it contains no instrumentation code. sl@0: sl@0: @return TRUE if DFC was actually queued by this call sl@0: FALSE if DFC was already queued on entry so this call did nothing sl@0: @see TDfc::DoEnque() sl@0: @see TDfc::Enque() sl@0: @see TDfc::Add() sl@0: */ sl@0: EXPORT_C TBool TDfc::RawAdd() sl@0: { sl@0: TInt irq=NKern::DisableAllInterrupts(); sl@0: sl@0: // make sure DFC not already queued sl@0: TBool ok = !TestAndSetQueued(); sl@0: if (ok) sl@0: { sl@0: TheScheduler.iDfcs.Add(this); sl@0: TheScheduler.iDfcPendingFlag=1; sl@0: } sl@0: sl@0: NKern::RestoreInterrupts(irq); sl@0: return ok; sl@0: } sl@0: sl@0: sl@0: /** Queue a DFC (not an IDFC) from an IDFC or thread with preemption disabled. sl@0: sl@0: This function is the preferred way to queue a DFC from an IDFC. It should not sl@0: be used to queue an IDFC - use TDfc::Add() for this. sl@0: sl@0: This function does nothing if the DFC is already queued. sl@0: sl@0: @return TRUE if DFC was actually queued by this call sl@0: FALSE if DFC was already queued on entry so this call did nothing sl@0: @pre Call only from IDFC or thread with preemption disabled. sl@0: @pre Do not call from ISR or thread with preemption enabled. sl@0: sl@0: @see TDfc::Add() sl@0: @see TDfc::Enque() sl@0: */ sl@0: EXPORT_C TBool TDfc::DoEnque() sl@0: { sl@0: __ASSERT_WITH_MESSAGE_DEBUG( (NKern::CurrentContext()==NKern::EIDFC )||( NKern::CurrentContext()==NKern::EThread && TheScheduler.iKernCSLocked),"Do not call from ISR or thread with preemption enabled","TDfc::DoEnque"); sl@0: __NK_ASSERT_DEBUG(!IsIDFC()); sl@0: __ASSERT_WITH_MESSAGE_DEBUG( iDfcQ, "DFC queue not set", "TDfc::Add"); sl@0: sl@0: // Check not already queued and then mark queued to prevent ISRs touching this DFC sl@0: TBool ok = !TestAndSetQueued(); sl@0: if (ok) sl@0: DoEnqueFinal(); sl@0: return ok; sl@0: } sl@0: sl@0: void TDfc::DoEnqueFinal() sl@0: // sl@0: // Add a DFC to its final queue. Assumes DFC not currently queued. sl@0: // Enter and return with kernel locked. sl@0: // sl@0: { sl@0: iOnFinalQ=TRUE; sl@0: iDfcQ->Add(this); sl@0: NThreadBase* pT=iDfcQ->iThread; sl@0: if (pT->iNState==NThreadBase::EWaitDfc) sl@0: pT->CheckSuspendThenReady(); sl@0: } sl@0: sl@0: void TDfcQue::ThreadFunction(TAny* aDfcQ) sl@0: { sl@0: TDfcQue& q=*(TDfcQue*)aDfcQ; sl@0: NThreadBase* pC=TheScheduler.iCurrentThread; sl@0: FOREVER sl@0: { sl@0: NKern::Lock(); sl@0: if (q.IsEmpty()) sl@0: { sl@0: pC->iNState=NThreadBase::EWaitDfc; sl@0: TheScheduler.Remove(pC); sl@0: RescheduleNeeded(); sl@0: NKern::Unlock(); sl@0: } sl@0: else sl@0: { sl@0: TDfc& d=*q.First(); sl@0: q.Remove(&d); sl@0: d.iOnFinalQ=FALSE; sl@0: d.iQueued=FALSE; sl@0: NKern::Unlock(); sl@0: (*d.iFunction)(d.iPtr); sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: /** Cancels an IDFC or DFC. sl@0: sl@0: This function does nothing if the IDFC or DFC is not queued. sl@0: sl@0: @return TRUE if the DFC was actually dequeued by this call. In that case sl@0: it is guaranteed that the DFC will not execute until it is sl@0: queued again. sl@0: FALSE if the DFC was not queued on entry to the call, or was in sl@0: the process of being executed or cancelled. In this case sl@0: it is possible that the DFC executes after this call sl@0: returns. sl@0: sl@0: @post However in either case it is safe to delete the DFC object on sl@0: return from this call provided only that the DFC function does not sl@0: refer to the DFC object itself. sl@0: sl@0: @pre IDFC or thread context. Do not call from ISRs. sl@0: sl@0: @pre If the DFC function accesses the DFC object itself, the user must ensure that sl@0: Cancel() cannot be called while the DFC function is running. sl@0: */ sl@0: EXPORT_C TBool TDfc::Cancel() sl@0: { sl@0: CHECK_PRECONDITIONS(MASK_NOT_ISR,"TDfc::Cancel"); sl@0: NKern::Lock(); sl@0: TBool ret = iQueued; sl@0: if (iQueued) // ISRs can't affect this test since they can't de-queue a DFC or IDFC sl@0: { sl@0: if (!iOnFinalQ) // OK to check this with interrupts enabled since interrupts can't change it sl@0: { sl@0: // Must disable interrupts to protect the pending queue sl@0: TInt irq=NKern::DisableAllInterrupts(); sl@0: SDblQueLink::Deque(); sl@0: NKern::RestoreInterrupts(irq); sl@0: } sl@0: else sl@0: { sl@0: // Final queues can't be modified by interrupts sl@0: iDfcQ->Remove(this); sl@0: iOnFinalQ=FALSE; sl@0: } sl@0: iQueued=FALSE; // must be done last sl@0: } sl@0: NKern::Unlock(); sl@0: return ret; sl@0: } sl@0: #endif sl@0: sl@0: /** Queues a DFC (not an IDFC) from a thread. sl@0: sl@0: Does nothing if DFC is already queued. sl@0: sl@0: NOTE: Although this can be called in an IDFC context, it is more efficient to call sl@0: DoEnque() in this case. sl@0: sl@0: @return TRUE if DFC was actually queued by this call sl@0: FALSE if DFC was already queued on entry so this call did nothing sl@0: @pre Call either in a thread or an IDFC context. sl@0: @pre Do not call from an ISR. sl@0: */ sl@0: EXPORT_C TBool TDfc::Enque() sl@0: { sl@0: CHECK_PRECONDITIONS(MASK_NOT_ISR,"TDfc::Enque()"); sl@0: NKern::Lock(); sl@0: TBool ret = DoEnque(); sl@0: NKern::Unlock(); sl@0: return ret; sl@0: } sl@0: sl@0: sl@0: /** Queue a DFC (not an IDFC) from a thread and also signals a fast mutex. sl@0: sl@0: The DFC is unaffected if it is already queued. sl@0: sl@0: The fast mutex is signalled before preemption is reenabled to avoid potential sl@0: scheduler thrashing. sl@0: sl@0: @param aMutex = pointer to fast mutex to be signalled; sl@0: NULL means system lock mutex. sl@0: @return TRUE if DFC was actually queued by this call sl@0: FALSE if DFC was already queued on entry so this call did nothing sl@0: @pre Call in a thread context. sl@0: @pre Kernel must be unlocked. sl@0: @pre Do not call from an ISR. sl@0: @pre Do not call from an IDFC. sl@0: */ sl@0: EXPORT_C TBool TDfc::Enque(NFastMutex* aMutex) sl@0: { sl@0: CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"TDfc::Enque(NFastMutex* aMutex)"); sl@0: if (!aMutex) sl@0: aMutex=&TheScheduler.iLock; sl@0: NKern::Lock(); sl@0: TBool ret = DoEnque(); sl@0: aMutex->Signal(); sl@0: NKern::Unlock(); sl@0: return ret; sl@0: } sl@0: sl@0: sl@0: /** Returns a pointer to the thread on which a DFC runs sl@0: sl@0: @return If this is a DFC and the DFC queue has been set, a pointer to the sl@0: thread which will run the DFC. sl@0: NULL if this is an IDFC or the DFC queue has not been set. sl@0: */ sl@0: EXPORT_C NThreadBase* TDfc::Thread() sl@0: { sl@0: if (IsIDFC()) sl@0: return 0; sl@0: return iDfcQ ? iDfcQ->iThread : 0; sl@0: } sl@0: sl@0: sl@0: /****************************************************************************** sl@0: * Idle notification sl@0: ******************************************************************************/ sl@0: sl@0: /** Register an IDFC or a DFC to be called when the system goes idle sl@0: sl@0: This function does nothing if the IDFC/DFC is already queued. sl@0: sl@0: @return TRUE if DFC was actually queued by this call sl@0: FALSE if DFC was already queued on entry so this call did nothing sl@0: */ sl@0: EXPORT_C TBool TDfc::QueueOnIdle() sl@0: { sl@0: TInt irq=NKern::DisableAllInterrupts(); sl@0: sl@0: // make sure DFC not already queued sl@0: TBool ok = !TestAndSetQueued(); sl@0: if (ok) sl@0: TheScheduler.iIdleDfcs.Add(this); sl@0: sl@0: NKern::RestoreInterrupts(irq); sl@0: return ok; sl@0: } sl@0: sl@0: sl@0: TUint32 NKern::IdleGenerationCount() sl@0: { sl@0: return TheScheduler.iIdleGenerationCount; sl@0: } sl@0: sl@0: sl@0: void NKern::Idle() sl@0: { sl@0: TInt irq = NKern::DisableAllInterrupts(); sl@0: #ifdef _DEBUG sl@0: if (!TheScheduler.iIdleDfcs.IsEmpty() && TheScheduler.iDelayedQ.IsEmpty()) sl@0: #else sl@0: if (!TheScheduler.iIdleDfcs.IsEmpty()) sl@0: #endif sl@0: { sl@0: ++TheScheduler.iIdleGenerationCount; sl@0: TheScheduler.iDfcs.MoveFrom(&TheScheduler.iIdleDfcs); sl@0: TheScheduler.iDfcPendingFlag=1; sl@0: NKern::RestoreInterrupts(irq); sl@0: return; sl@0: } sl@0: NKern::RestoreInterrupts(irq); sl@0: NKIdle(0); sl@0: } sl@0: sl@0: sl@0: /****************************************************************************** sl@0: * Scheduler IDFC/DFC Processing sl@0: ******************************************************************************/ sl@0: sl@0: #ifndef __SCHEDULER_MACHINE_CODED__ sl@0: void TScheduler::QueueDfcs() sl@0: // sl@0: // Enter with interrupts off and kernel locked sl@0: // Leave with interrupts off and kernel locked sl@0: // sl@0: { sl@0: iInIDFC = TRUE; sl@0: BTrace0(BTrace::ECpuUsage,BTrace::EIDFCStart); sl@0: FOREVER sl@0: { sl@0: // remove from pending queue with interrupts disabled sl@0: TDfc* d=(TDfc*)iDfcs.GetFirst(); sl@0: if (!d) sl@0: break; sl@0: NKern::EnableAllInterrupts(); sl@0: if (d->IsIDFC()) sl@0: { sl@0: d->iQueued=FALSE; sl@0: (*d->iFunction)(d->iPtr); sl@0: } sl@0: else sl@0: d->DoEnqueFinal(); sl@0: NKern::DisableAllInterrupts(); sl@0: } sl@0: iDfcPendingFlag = FALSE; sl@0: BTrace0(BTrace::ECpuUsage,BTrace::EIDFCEnd); sl@0: iInIDFC = FALSE; sl@0: } sl@0: #endif sl@0: sl@0: sl@0: /****************************************************************************** sl@0: * Kernel-side asynchronous request DFCs sl@0: ******************************************************************************/ sl@0: sl@0: EXPORT_C TAsyncRequest::TAsyncRequest(TDfcFn aFunction, TDfcQue* aDfcQ, TInt aPriority) sl@0: : TDfc(aFunction, this, aDfcQ, aPriority), iCompletionObject(0), iCancel(0), iResult(0) sl@0: { sl@0: } sl@0: sl@0: sl@0: EXPORT_C void TAsyncRequest::Send(TDfc* aCompletionDfc) sl@0: { sl@0: __NK_ASSERT_DEBUG(!iCompletionObject); sl@0: iCancel = EFalse; sl@0: iCompletionObject = (TAny*)((TLinAddr)aCompletionDfc|1); sl@0: TDfc::Enque(); sl@0: } sl@0: sl@0: sl@0: EXPORT_C void TAsyncRequest::Send(NFastSemaphore* aCompletionSemaphore) sl@0: { sl@0: __NK_ASSERT_DEBUG(!iCompletionObject); sl@0: iCancel = EFalse; sl@0: iCompletionObject = aCompletionSemaphore; sl@0: TDfc::Enque(); sl@0: } sl@0: sl@0: sl@0: EXPORT_C TInt TAsyncRequest::SendReceive() sl@0: { sl@0: NFastSemaphore signal; sl@0: NKern::FSSetOwner(&signal, 0); sl@0: Send(&signal); sl@0: NKern::FSWait(&signal); sl@0: return iResult; sl@0: } sl@0: sl@0: sl@0: EXPORT_C void TAsyncRequest::Cancel() sl@0: { sl@0: iCancel = ETrue; sl@0: if(TDfc::Cancel()) sl@0: Complete(KErrCancel); sl@0: } sl@0: sl@0: sl@0: EXPORT_C void TAsyncRequest::Complete(TInt aResult) sl@0: { sl@0: TLinAddr signal = (TLinAddr)__e32_atomic_swp_ord_ptr(&iCompletionObject, 0); sl@0: if(signal) sl@0: { sl@0: iResult = aResult; sl@0: if(signal&1) sl@0: ((TDfc*)(signal&~1))->Enque(); sl@0: else sl@0: NKern::FSSignal((NFastSemaphore*)signal); sl@0: } sl@0: }