1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/kernelhwsrv/kernel/eka/nkern/dfcs.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,468 @@
1.4 +// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of the License "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +// e32\nkern\dfcs.cpp
1.18 +// DFCs
1.19 +//
1.20 +//
1.21 +
1.22 +// NThreadBase member data
1.23 +#define __INCLUDE_NTHREADBASE_DEFINES__
1.24 +
1.25 +// TDfc member data
1.26 +#define __INCLUDE_TDFC_DEFINES__
1.27 +
1.28 +#include "nk_priv.h"
1.29 +
1.30 +
1.31 +/** Construct an IDFC
1.32 +
1.33 + @param aFunction = function to call
1.34 + @param aPtr = parameter to be passed to function
1.35 + */
1.36 +EXPORT_C TDfc::TDfc(TDfcFn aFunction, TAny* aPtr)
1.37 + : iPtr(aPtr), iFunction(aFunction), iDfcQ(NULL)
1.38 + {
1.39 + iPriority=0xff;
1.40 + iSpare1=0;
1.41 + iOnFinalQ=FALSE;
1.42 + iQueued=FALSE;
1.43 + }
1.44 +
1.45 +
1.46 +/** Construct a DFC without specifying a DFC queue.
1.47 + The DFC queue must be set before the DFC may be queued.
1.48 +
1.49 + @param aFunction = function to call
1.50 + @param aPtr = parameter to be passed to function
1.51 + @param aPriority = priority of DFC within the queue (0 to 7, where 7 is highest)
1.52 + */
1.53 +EXPORT_C TDfc::TDfc(TDfcFn aFunction, TAny* aPtr, TInt aPriority)
1.54 + : iPtr(aPtr), iFunction(aFunction), iDfcQ(NULL)
1.55 + {
1.56 + __NK_ASSERT_DEBUG((TUint)aPriority<(TUint)KNumDfcPriorities);
1.57 + iPriority=TUint8(aPriority);
1.58 + iSpare1=0;
1.59 + iOnFinalQ=FALSE;
1.60 + iQueued=FALSE;
1.61 + }
1.62 +
1.63 +
1.64 +/** Construct a DFC specifying a DFC queue.
1.65 +
1.66 + @param aFunction = function to call
1.67 + @param aPtr = parameter to be passed to function
1.68 + @param aDfcQ = pointer to DFC queue which this DFC should use
1.69 + @param aPriority = priority of DFC within the queue (0-7)
1.70 + */
1.71 +EXPORT_C TDfc::TDfc(TDfcFn aFunction, TAny* aPtr, TDfcQue* aDfcQ, TInt aPriority)
1.72 + : iPtr(aPtr), iFunction(aFunction), iDfcQ(aDfcQ)
1.73 + {
1.74 + __NK_ASSERT_DEBUG((TUint)aPriority<(TUint)KNumDfcPriorities);
1.75 + iPriority=TUint8(aPriority);
1.76 + iSpare1=0;
1.77 + iOnFinalQ=FALSE;
1.78 + iQueued=FALSE;
1.79 + }
1.80 +
1.81 +
1.82 +/** Construct a DFC queue
1.83 + Kern::DfcQInit() should be called on the new DFC queue before it can be used.
1.84 + */
1.85 +EXPORT_C TDfcQue::TDfcQue()
1.86 + : iThread(NULL)
1.87 + {}
1.88 +
1.89 +
1.90 +#ifndef __DFC_MACHINE_CODED__
1.91 +
1.92 +/** Queue an IDFC or a DFC from an ISR
1.93 +
1.94 + This function is the only way to queue an IDFC and is the only way to queue
1.95 + a DFC from an ISR. To queue a DFC from an IDFC or a thread either Enque()
1.96 + or DoEnque() should be used.
1.97 +
1.98 + This function does nothing if the IDFC/DFC is already queued.
1.99 +
1.100 + @pre Call only from ISR, IDFC or thread with preemption disabled.
1.101 + @pre Do not call from thread with preemption enabled.
1.102 + @return TRUE if DFC was actually queued by this call
1.103 + FALSE if DFC was already queued on entry so this call did nothing
1.104 + @see TDfc::DoEnque()
1.105 + @see TDfc::Enque()
1.106 + */
1.107 +EXPORT_C TBool TDfc::Add()
1.108 + {
1.109 + __ASSERT_WITH_MESSAGE_DEBUG( NKern::CurrentContext()!=NKern::EThread || TheScheduler.iKernCSLocked,"Do not call from thread with preemption enabled","TDfc::Add");
1.110 + __ASSERT_WITH_MESSAGE_DEBUG( IsIDFC() || iDfcQ != NULL, "DFC queue not set", "TDfc::Add");
1.111 +#ifdef __WINS__
1.112 + __NK_ASSERT_ALWAYS(Interrupt.InInterrupt() || TheScheduler.iKernCSLocked);
1.113 +#endif
1.114 + return RawAdd();
1.115 + }
1.116 +
1.117 +
1.118 +/** Queue an IDFC or a DFC from an ISR
1.119 +
1.120 + This function is identical to TDfc::Add() but no checks are performed for correct usage,
1.121 + and it contains no instrumentation code.
1.122 +
1.123 + @return TRUE if DFC was actually queued by this call
1.124 + FALSE if DFC was already queued on entry so this call did nothing
1.125 + @see TDfc::DoEnque()
1.126 + @see TDfc::Enque()
1.127 + @see TDfc::Add()
1.128 + */
1.129 +EXPORT_C TBool TDfc::RawAdd()
1.130 + {
1.131 + TInt irq=NKern::DisableAllInterrupts();
1.132 +
1.133 + // make sure DFC not already queued
1.134 + TBool ok = !TestAndSetQueued();
1.135 + if (ok)
1.136 + {
1.137 + TheScheduler.iDfcs.Add(this);
1.138 + TheScheduler.iDfcPendingFlag=1;
1.139 + }
1.140 +
1.141 + NKern::RestoreInterrupts(irq);
1.142 + return ok;
1.143 + }
1.144 +
1.145 +
1.146 +/** Queue a DFC (not an IDFC) from an IDFC or thread with preemption disabled.
1.147 +
1.148 + This function is the preferred way to queue a DFC from an IDFC. It should not
1.149 + be used to queue an IDFC - use TDfc::Add() for this.
1.150 +
1.151 + This function does nothing if the DFC is already queued.
1.152 +
1.153 + @return TRUE if DFC was actually queued by this call
1.154 + FALSE if DFC was already queued on entry so this call did nothing
1.155 + @pre Call only from IDFC or thread with preemption disabled.
1.156 + @pre Do not call from ISR or thread with preemption enabled.
1.157 +
1.158 + @see TDfc::Add()
1.159 + @see TDfc::Enque()
1.160 + */
1.161 +EXPORT_C TBool TDfc::DoEnque()
1.162 + {
1.163 + __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");
1.164 + __NK_ASSERT_DEBUG(!IsIDFC());
1.165 + __ASSERT_WITH_MESSAGE_DEBUG( iDfcQ, "DFC queue not set", "TDfc::Add");
1.166 +
1.167 + // Check not already queued and then mark queued to prevent ISRs touching this DFC
1.168 + TBool ok = !TestAndSetQueued();
1.169 + if (ok)
1.170 + DoEnqueFinal();
1.171 + return ok;
1.172 + }
1.173 +
1.174 +void TDfc::DoEnqueFinal()
1.175 +//
1.176 +// Add a DFC to its final queue. Assumes DFC not currently queued.
1.177 +// Enter and return with kernel locked.
1.178 +//
1.179 + {
1.180 + iOnFinalQ=TRUE;
1.181 + iDfcQ->Add(this);
1.182 + NThreadBase* pT=iDfcQ->iThread;
1.183 + if (pT->iNState==NThreadBase::EWaitDfc)
1.184 + pT->CheckSuspendThenReady();
1.185 + }
1.186 +
1.187 +void TDfcQue::ThreadFunction(TAny* aDfcQ)
1.188 + {
1.189 + TDfcQue& q=*(TDfcQue*)aDfcQ;
1.190 + NThreadBase* pC=TheScheduler.iCurrentThread;
1.191 + FOREVER
1.192 + {
1.193 + NKern::Lock();
1.194 + if (q.IsEmpty())
1.195 + {
1.196 + pC->iNState=NThreadBase::EWaitDfc;
1.197 + TheScheduler.Remove(pC);
1.198 + RescheduleNeeded();
1.199 + NKern::Unlock();
1.200 + }
1.201 + else
1.202 + {
1.203 + TDfc& d=*q.First();
1.204 + q.Remove(&d);
1.205 + d.iOnFinalQ=FALSE;
1.206 + d.iQueued=FALSE;
1.207 + NKern::Unlock();
1.208 + (*d.iFunction)(d.iPtr);
1.209 + }
1.210 + }
1.211 + }
1.212 +
1.213 +
1.214 +/** Cancels an IDFC or DFC.
1.215 +
1.216 + This function does nothing if the IDFC or DFC is not queued.
1.217 +
1.218 + @return TRUE if the DFC was actually dequeued by this call. In that case
1.219 + it is guaranteed that the DFC will not execute until it is
1.220 + queued again.
1.221 + FALSE if the DFC was not queued on entry to the call, or was in
1.222 + the process of being executed or cancelled. In this case
1.223 + it is possible that the DFC executes after this call
1.224 + returns.
1.225 +
1.226 + @post However in either case it is safe to delete the DFC object on
1.227 + return from this call provided only that the DFC function does not
1.228 + refer to the DFC object itself.
1.229 +
1.230 + @pre IDFC or thread context. Do not call from ISRs.
1.231 +
1.232 + @pre If the DFC function accesses the DFC object itself, the user must ensure that
1.233 + Cancel() cannot be called while the DFC function is running.
1.234 + */
1.235 +EXPORT_C TBool TDfc::Cancel()
1.236 + {
1.237 + CHECK_PRECONDITIONS(MASK_NOT_ISR,"TDfc::Cancel");
1.238 + NKern::Lock();
1.239 + TBool ret = iQueued;
1.240 + if (iQueued) // ISRs can't affect this test since they can't de-queue a DFC or IDFC
1.241 + {
1.242 + if (!iOnFinalQ) // OK to check this with interrupts enabled since interrupts can't change it
1.243 + {
1.244 + // Must disable interrupts to protect the pending queue
1.245 + TInt irq=NKern::DisableAllInterrupts();
1.246 + SDblQueLink::Deque();
1.247 + NKern::RestoreInterrupts(irq);
1.248 + }
1.249 + else
1.250 + {
1.251 + // Final queues can't be modified by interrupts
1.252 + iDfcQ->Remove(this);
1.253 + iOnFinalQ=FALSE;
1.254 + }
1.255 + iQueued=FALSE; // must be done last
1.256 + }
1.257 + NKern::Unlock();
1.258 + return ret;
1.259 + }
1.260 +#endif
1.261 +
1.262 +/** Queues a DFC (not an IDFC) from a thread.
1.263 +
1.264 + Does nothing if DFC is already queued.
1.265 +
1.266 + NOTE: Although this can be called in an IDFC context, it is more efficient to call
1.267 + DoEnque() in this case.
1.268 +
1.269 + @return TRUE if DFC was actually queued by this call
1.270 + FALSE if DFC was already queued on entry so this call did nothing
1.271 + @pre Call either in a thread or an IDFC context.
1.272 + @pre Do not call from an ISR.
1.273 + */
1.274 +EXPORT_C TBool TDfc::Enque()
1.275 + {
1.276 + CHECK_PRECONDITIONS(MASK_NOT_ISR,"TDfc::Enque()");
1.277 + NKern::Lock();
1.278 + TBool ret = DoEnque();
1.279 + NKern::Unlock();
1.280 + return ret;
1.281 + }
1.282 +
1.283 +
1.284 +/** Queue a DFC (not an IDFC) from a thread and also signals a fast mutex.
1.285 +
1.286 + The DFC is unaffected if it is already queued.
1.287 +
1.288 + The fast mutex is signalled before preemption is reenabled to avoid potential
1.289 + scheduler thrashing.
1.290 +
1.291 + @param aMutex = pointer to fast mutex to be signalled;
1.292 + NULL means system lock mutex.
1.293 + @return TRUE if DFC was actually queued by this call
1.294 + FALSE if DFC was already queued on entry so this call did nothing
1.295 + @pre Call in a thread context.
1.296 + @pre Kernel must be unlocked.
1.297 + @pre Do not call from an ISR.
1.298 + @pre Do not call from an IDFC.
1.299 + */
1.300 +EXPORT_C TBool TDfc::Enque(NFastMutex* aMutex)
1.301 + {
1.302 + CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"TDfc::Enque(NFastMutex* aMutex)");
1.303 + if (!aMutex)
1.304 + aMutex=&TheScheduler.iLock;
1.305 + NKern::Lock();
1.306 + TBool ret = DoEnque();
1.307 + aMutex->Signal();
1.308 + NKern::Unlock();
1.309 + return ret;
1.310 + }
1.311 +
1.312 +
1.313 +/** Returns a pointer to the thread on which a DFC runs
1.314 +
1.315 + @return If this is a DFC and the DFC queue has been set, a pointer to the
1.316 + thread which will run the DFC.
1.317 + NULL if this is an IDFC or the DFC queue has not been set.
1.318 + */
1.319 +EXPORT_C NThreadBase* TDfc::Thread()
1.320 + {
1.321 + if (IsIDFC())
1.322 + return 0;
1.323 + return iDfcQ ? iDfcQ->iThread : 0;
1.324 + }
1.325 +
1.326 +
1.327 +/******************************************************************************
1.328 + * Idle notification
1.329 + ******************************************************************************/
1.330 +
1.331 +/** Register an IDFC or a DFC to be called when the system goes idle
1.332 +
1.333 + This function does nothing if the IDFC/DFC is already queued.
1.334 +
1.335 + @return TRUE if DFC was actually queued by this call
1.336 + FALSE if DFC was already queued on entry so this call did nothing
1.337 + */
1.338 +EXPORT_C TBool TDfc::QueueOnIdle()
1.339 + {
1.340 + TInt irq=NKern::DisableAllInterrupts();
1.341 +
1.342 + // make sure DFC not already queued
1.343 + TBool ok = !TestAndSetQueued();
1.344 + if (ok)
1.345 + TheScheduler.iIdleDfcs.Add(this);
1.346 +
1.347 + NKern::RestoreInterrupts(irq);
1.348 + return ok;
1.349 + }
1.350 +
1.351 +
1.352 +TUint32 NKern::IdleGenerationCount()
1.353 + {
1.354 + return TheScheduler.iIdleGenerationCount;
1.355 + }
1.356 +
1.357 +
1.358 +void NKern::Idle()
1.359 + {
1.360 + TInt irq = NKern::DisableAllInterrupts();
1.361 +#ifdef _DEBUG
1.362 + if (!TheScheduler.iIdleDfcs.IsEmpty() && TheScheduler.iDelayedQ.IsEmpty())
1.363 +#else
1.364 + if (!TheScheduler.iIdleDfcs.IsEmpty())
1.365 +#endif
1.366 + {
1.367 + ++TheScheduler.iIdleGenerationCount;
1.368 + TheScheduler.iDfcs.MoveFrom(&TheScheduler.iIdleDfcs);
1.369 + TheScheduler.iDfcPendingFlag=1;
1.370 + NKern::RestoreInterrupts(irq);
1.371 + return;
1.372 + }
1.373 + NKern::RestoreInterrupts(irq);
1.374 + NKIdle(0);
1.375 + }
1.376 +
1.377 +
1.378 +/******************************************************************************
1.379 + * Scheduler IDFC/DFC Processing
1.380 + ******************************************************************************/
1.381 +
1.382 +#ifndef __SCHEDULER_MACHINE_CODED__
1.383 +void TScheduler::QueueDfcs()
1.384 +//
1.385 +// Enter with interrupts off and kernel locked
1.386 +// Leave with interrupts off and kernel locked
1.387 +//
1.388 + {
1.389 + iInIDFC = TRUE;
1.390 + BTrace0(BTrace::ECpuUsage,BTrace::EIDFCStart);
1.391 + FOREVER
1.392 + {
1.393 + // remove from pending queue with interrupts disabled
1.394 + TDfc* d=(TDfc*)iDfcs.GetFirst();
1.395 + if (!d)
1.396 + break;
1.397 + NKern::EnableAllInterrupts();
1.398 + if (d->IsIDFC())
1.399 + {
1.400 + d->iQueued=FALSE;
1.401 + (*d->iFunction)(d->iPtr);
1.402 + }
1.403 + else
1.404 + d->DoEnqueFinal();
1.405 + NKern::DisableAllInterrupts();
1.406 + }
1.407 + iDfcPendingFlag = FALSE;
1.408 + BTrace0(BTrace::ECpuUsage,BTrace::EIDFCEnd);
1.409 + iInIDFC = FALSE;
1.410 + }
1.411 +#endif
1.412 +
1.413 +
1.414 +/******************************************************************************
1.415 + * Kernel-side asynchronous request DFCs
1.416 + ******************************************************************************/
1.417 +
1.418 +EXPORT_C TAsyncRequest::TAsyncRequest(TDfcFn aFunction, TDfcQue* aDfcQ, TInt aPriority)
1.419 + : TDfc(aFunction, this, aDfcQ, aPriority), iCompletionObject(0), iCancel(0), iResult(0)
1.420 + {
1.421 + }
1.422 +
1.423 +
1.424 +EXPORT_C void TAsyncRequest::Send(TDfc* aCompletionDfc)
1.425 + {
1.426 + __NK_ASSERT_DEBUG(!iCompletionObject);
1.427 + iCancel = EFalse;
1.428 + iCompletionObject = (TAny*)((TLinAddr)aCompletionDfc|1);
1.429 + TDfc::Enque();
1.430 + }
1.431 +
1.432 +
1.433 +EXPORT_C void TAsyncRequest::Send(NFastSemaphore* aCompletionSemaphore)
1.434 + {
1.435 + __NK_ASSERT_DEBUG(!iCompletionObject);
1.436 + iCancel = EFalse;
1.437 + iCompletionObject = aCompletionSemaphore;
1.438 + TDfc::Enque();
1.439 + }
1.440 +
1.441 +
1.442 +EXPORT_C TInt TAsyncRequest::SendReceive()
1.443 + {
1.444 + NFastSemaphore signal;
1.445 + NKern::FSSetOwner(&signal, 0);
1.446 + Send(&signal);
1.447 + NKern::FSWait(&signal);
1.448 + return iResult;
1.449 + }
1.450 +
1.451 +
1.452 +EXPORT_C void TAsyncRequest::Cancel()
1.453 + {
1.454 + iCancel = ETrue;
1.455 + if(TDfc::Cancel())
1.456 + Complete(KErrCancel);
1.457 + }
1.458 +
1.459 +
1.460 +EXPORT_C void TAsyncRequest::Complete(TInt aResult)
1.461 + {
1.462 + TLinAddr signal = (TLinAddr)__e32_atomic_swp_ord_ptr(&iCompletionObject, 0);
1.463 + if(signal)
1.464 + {
1.465 + iResult = aResult;
1.466 + if(signal&1)
1.467 + ((TDfc*)(signal&~1))->Enque();
1.468 + else
1.469 + NKern::FSSignal((NFastSemaphore*)signal);
1.470 + }
1.471 + }