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