os/kernelhwsrv/kernel/eka/nkern/dfcs.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
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\dfcs.cpp
sl@0
    15
// DFCs
sl@0
    16
// 
sl@0
    17
//
sl@0
    18
sl@0
    19
// NThreadBase member data
sl@0
    20
#define __INCLUDE_NTHREADBASE_DEFINES__
sl@0
    21
sl@0
    22
// TDfc member data
sl@0
    23
#define __INCLUDE_TDFC_DEFINES__
sl@0
    24
sl@0
    25
#include "nk_priv.h"
sl@0
    26
sl@0
    27
sl@0
    28
/** Construct an IDFC
sl@0
    29
sl@0
    30
	@param aFunction = function to call
sl@0
    31
	@param aPtr = parameter to be passed to function
sl@0
    32
 */
sl@0
    33
EXPORT_C TDfc::TDfc(TDfcFn aFunction, TAny* aPtr)
sl@0
    34
	: iPtr(aPtr), iFunction(aFunction), iDfcQ(NULL)
sl@0
    35
	{
sl@0
    36
	iPriority=0xff;
sl@0
    37
	iSpare1=0;
sl@0
    38
	iOnFinalQ=FALSE;
sl@0
    39
	iQueued=FALSE;
sl@0
    40
	}
sl@0
    41
sl@0
    42
sl@0
    43
/** Construct a DFC without specifying a DFC queue.
sl@0
    44
	The DFC queue must be set before the DFC may be queued.
sl@0
    45
sl@0
    46
	@param aFunction = function to call
sl@0
    47
	@param aPtr = parameter to be passed to function
sl@0
    48
	@param aPriority = priority of DFC within the queue (0 to 7, where 7 is highest)
sl@0
    49
 */
sl@0
    50
EXPORT_C TDfc::TDfc(TDfcFn aFunction, TAny* aPtr, TInt aPriority)
sl@0
    51
	: iPtr(aPtr), iFunction(aFunction), iDfcQ(NULL)
sl@0
    52
	{
sl@0
    53
	__NK_ASSERT_DEBUG((TUint)aPriority<(TUint)KNumDfcPriorities);
sl@0
    54
	iPriority=TUint8(aPriority);
sl@0
    55
	iSpare1=0;
sl@0
    56
	iOnFinalQ=FALSE;
sl@0
    57
	iQueued=FALSE;
sl@0
    58
	}
sl@0
    59
sl@0
    60
sl@0
    61
/** Construct a DFC specifying a DFC queue.
sl@0
    62
sl@0
    63
	@param aFunction = function to call
sl@0
    64
	@param aPtr = parameter to be passed to function
sl@0
    65
	@param aDfcQ = pointer to DFC queue which this DFC should use
sl@0
    66
	@param aPriority = priority of DFC within the queue (0-7)
sl@0
    67
 */
sl@0
    68
EXPORT_C TDfc::TDfc(TDfcFn aFunction, TAny* aPtr, TDfcQue* aDfcQ, TInt aPriority)
sl@0
    69
	: iPtr(aPtr), iFunction(aFunction), iDfcQ(aDfcQ)
sl@0
    70
	{
sl@0
    71
	__NK_ASSERT_DEBUG((TUint)aPriority<(TUint)KNumDfcPriorities);
sl@0
    72
	iPriority=TUint8(aPriority);
sl@0
    73
	iSpare1=0;
sl@0
    74
	iOnFinalQ=FALSE;
sl@0
    75
	iQueued=FALSE;
sl@0
    76
	}
sl@0
    77
sl@0
    78
sl@0
    79
/** Construct a DFC queue
sl@0
    80
	Kern::DfcQInit() should be called on the new DFC queue before it can be used.
sl@0
    81
 */
sl@0
    82
EXPORT_C TDfcQue::TDfcQue()
sl@0
    83
	: iThread(NULL)
sl@0
    84
	{}
sl@0
    85
sl@0
    86
sl@0
    87
#ifndef __DFC_MACHINE_CODED__
sl@0
    88
sl@0
    89
/** Queue an IDFC or a DFC from an ISR
sl@0
    90
sl@0
    91
	This function is the only way to queue an IDFC and is the only way to queue
sl@0
    92
	a DFC from an ISR. To queue a DFC from an IDFC or a thread either Enque()
sl@0
    93
	or DoEnque() should be used.
sl@0
    94
sl@0
    95
	This function does nothing if the IDFC/DFC is already queued.
sl@0
    96
sl@0
    97
	@pre Call only from ISR, IDFC or thread with preemption disabled.
sl@0
    98
	@pre Do not call from thread with preemption enabled.
sl@0
    99
	@return	TRUE if DFC was actually queued by this call
sl@0
   100
			FALSE if DFC was already queued on entry so this call did nothing
sl@0
   101
	@see TDfc::DoEnque()
sl@0
   102
	@see TDfc::Enque()
sl@0
   103
 */
sl@0
   104
EXPORT_C TBool TDfc::Add()
sl@0
   105
	{
sl@0
   106
	__ASSERT_WITH_MESSAGE_DEBUG(  NKern::CurrentContext()!=NKern::EThread  ||  TheScheduler.iKernCSLocked,"Do not call from thread with preemption enabled","TDfc::Add");
sl@0
   107
	__ASSERT_WITH_MESSAGE_DEBUG(  IsIDFC() || iDfcQ != NULL, "DFC queue not set", "TDfc::Add");
sl@0
   108
#ifdef __WINS__
sl@0
   109
	__NK_ASSERT_ALWAYS(Interrupt.InInterrupt() || TheScheduler.iKernCSLocked);
sl@0
   110
#endif
sl@0
   111
	return RawAdd();
sl@0
   112
	}
sl@0
   113
sl@0
   114
sl@0
   115
/** Queue an IDFC or a DFC from an ISR
sl@0
   116
sl@0
   117
	This function is identical to TDfc::Add() but no checks are performed for correct usage,
sl@0
   118
	and it contains no instrumentation code.
sl@0
   119
sl@0
   120
	@return	TRUE if DFC was actually queued by this call
sl@0
   121
			FALSE if DFC was already queued on entry so this call did nothing
sl@0
   122
	@see TDfc::DoEnque()
sl@0
   123
	@see TDfc::Enque()
sl@0
   124
	@see TDfc::Add()
sl@0
   125
 */
sl@0
   126
EXPORT_C TBool TDfc::RawAdd()
sl@0
   127
	{
sl@0
   128
	TInt irq=NKern::DisableAllInterrupts();
sl@0
   129
sl@0
   130
	// make sure DFC not already queued
sl@0
   131
	TBool ok = !TestAndSetQueued();
sl@0
   132
	if (ok)
sl@0
   133
		{
sl@0
   134
		TheScheduler.iDfcs.Add(this);
sl@0
   135
		TheScheduler.iDfcPendingFlag=1;
sl@0
   136
		}
sl@0
   137
sl@0
   138
	NKern::RestoreInterrupts(irq);
sl@0
   139
	return ok;
sl@0
   140
	}
sl@0
   141
sl@0
   142
sl@0
   143
/** Queue a DFC (not an IDFC) from an IDFC or thread with preemption disabled.
sl@0
   144
sl@0
   145
	This function is the preferred way to queue a DFC from an IDFC. It should not
sl@0
   146
	be used to queue an IDFC - use TDfc::Add() for this.
sl@0
   147
sl@0
   148
	This function does nothing if the DFC is already queued.
sl@0
   149
sl@0
   150
	@return	TRUE if DFC was actually queued by this call
sl@0
   151
			FALSE if DFC was already queued on entry so this call did nothing
sl@0
   152
	@pre Call only from IDFC or thread with preemption disabled.
sl@0
   153
	@pre Do not call from ISR or thread with preemption enabled.
sl@0
   154
sl@0
   155
	@see TDfc::Add()
sl@0
   156
	@see TDfc::Enque()
sl@0
   157
 */
sl@0
   158
EXPORT_C TBool TDfc::DoEnque()
sl@0
   159
	{
sl@0
   160
	__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
   161
	__NK_ASSERT_DEBUG(!IsIDFC());
sl@0
   162
	__ASSERT_WITH_MESSAGE_DEBUG(  iDfcQ, "DFC queue not set", "TDfc::Add");
sl@0
   163
sl@0
   164
	// Check not already queued and then mark queued to prevent ISRs touching this DFC
sl@0
   165
	TBool ok = !TestAndSetQueued();
sl@0
   166
	if (ok)
sl@0
   167
		DoEnqueFinal();
sl@0
   168
	return ok;
sl@0
   169
	}
sl@0
   170
sl@0
   171
void TDfc::DoEnqueFinal()
sl@0
   172
//
sl@0
   173
// Add a DFC to its final queue. Assumes DFC not currently queued.
sl@0
   174
// Enter and return with kernel locked.
sl@0
   175
//
sl@0
   176
	{
sl@0
   177
	iOnFinalQ=TRUE;
sl@0
   178
	iDfcQ->Add(this);
sl@0
   179
	NThreadBase* pT=iDfcQ->iThread;
sl@0
   180
	if (pT->iNState==NThreadBase::EWaitDfc)
sl@0
   181
		pT->CheckSuspendThenReady();
sl@0
   182
	}
sl@0
   183
sl@0
   184
void TDfcQue::ThreadFunction(TAny* aDfcQ)
sl@0
   185
	{
sl@0
   186
	TDfcQue& q=*(TDfcQue*)aDfcQ;
sl@0
   187
	NThreadBase* pC=TheScheduler.iCurrentThread;
sl@0
   188
	FOREVER
sl@0
   189
		{
sl@0
   190
		NKern::Lock();
sl@0
   191
		if (q.IsEmpty())
sl@0
   192
			{
sl@0
   193
			pC->iNState=NThreadBase::EWaitDfc;
sl@0
   194
			TheScheduler.Remove(pC);
sl@0
   195
			RescheduleNeeded();
sl@0
   196
			NKern::Unlock();
sl@0
   197
			}
sl@0
   198
		else
sl@0
   199
			{
sl@0
   200
			TDfc& d=*q.First();
sl@0
   201
			q.Remove(&d);
sl@0
   202
			d.iOnFinalQ=FALSE;
sl@0
   203
			d.iQueued=FALSE;
sl@0
   204
			NKern::Unlock();
sl@0
   205
			(*d.iFunction)(d.iPtr);
sl@0
   206
			}
sl@0
   207
		}
sl@0
   208
	}
sl@0
   209
sl@0
   210
sl@0
   211
/** Cancels an IDFC or DFC.
sl@0
   212
sl@0
   213
	This function does nothing if the IDFC or DFC is not queued.
sl@0
   214
sl@0
   215
	@return	TRUE	if the DFC was actually dequeued by this call. In that case
sl@0
   216
					it is guaranteed that the DFC will not execute until it is
sl@0
   217
					queued again.
sl@0
   218
			FALSE	if the DFC was not queued on entry to the call, or was in
sl@0
   219
					the process of being executed or cancelled. In this case
sl@0
   220
					it is possible that the DFC executes after this call
sl@0
   221
					returns.
sl@0
   222
sl@0
   223
	@post	However in either case it is safe to delete the DFC object on
sl@0
   224
			return from this call provided only that the DFC function does not
sl@0
   225
			refer to the DFC object itself.
sl@0
   226
sl@0
   227
	@pre IDFC or thread context. Do not call from ISRs.
sl@0
   228
sl@0
   229
	@pre If the DFC function accesses the DFC object itself, the user must ensure that
sl@0
   230
	     Cancel() cannot be called while the DFC function is running.
sl@0
   231
 */
sl@0
   232
EXPORT_C TBool TDfc::Cancel()
sl@0
   233
	{
sl@0
   234
	CHECK_PRECONDITIONS(MASK_NOT_ISR,"TDfc::Cancel");
sl@0
   235
	NKern::Lock();
sl@0
   236
	TBool ret = iQueued;
sl@0
   237
	if (iQueued)	// ISRs can't affect this test since they can't de-queue a DFC or IDFC
sl@0
   238
		{
sl@0
   239
		if (!iOnFinalQ)	// OK to check this with interrupts enabled since interrupts can't change it
sl@0
   240
			{
sl@0
   241
			// Must disable interrupts to protect the pending queue
sl@0
   242
			TInt irq=NKern::DisableAllInterrupts();
sl@0
   243
			SDblQueLink::Deque();
sl@0
   244
			NKern::RestoreInterrupts(irq);
sl@0
   245
			}
sl@0
   246
		else
sl@0
   247
			{
sl@0
   248
			// Final queues can't be modified by interrupts
sl@0
   249
			iDfcQ->Remove(this);
sl@0
   250
			iOnFinalQ=FALSE;
sl@0
   251
			}
sl@0
   252
		iQueued=FALSE;	// must be done last
sl@0
   253
		}
sl@0
   254
	NKern::Unlock();
sl@0
   255
	return ret;
sl@0
   256
	}
sl@0
   257
#endif
sl@0
   258
sl@0
   259
/** Queues a DFC (not an IDFC) from a thread.
sl@0
   260
sl@0
   261
	Does nothing if DFC is already queued.
sl@0
   262
sl@0
   263
    NOTE: Although this can be called in an IDFC context, it is more efficient to call
sl@0
   264
    DoEnque() in this case.
sl@0
   265
    
sl@0
   266
	@return	TRUE if DFC was actually queued by this call
sl@0
   267
			FALSE if DFC was already queued on entry so this call did nothing
sl@0
   268
    @pre    Call either in a thread or an IDFC context.
sl@0
   269
	@pre	Do not call from an ISR.
sl@0
   270
 */
sl@0
   271
EXPORT_C TBool TDfc::Enque()
sl@0
   272
	{
sl@0
   273
	CHECK_PRECONDITIONS(MASK_NOT_ISR,"TDfc::Enque()");		
sl@0
   274
	NKern::Lock();
sl@0
   275
	TBool ret = DoEnque();
sl@0
   276
	NKern::Unlock();
sl@0
   277
	return ret;
sl@0
   278
	}
sl@0
   279
sl@0
   280
sl@0
   281
/** Queue a DFC (not an IDFC) from a thread and also signals a fast mutex.
sl@0
   282
sl@0
   283
	The DFC is unaffected if it is already queued.
sl@0
   284
sl@0
   285
	The fast mutex is signalled before preemption is reenabled to avoid potential
sl@0
   286
	scheduler thrashing.
sl@0
   287
sl@0
   288
	@param	aMutex =	pointer to fast mutex to be signalled;
sl@0
   289
						NULL means system lock mutex.
sl@0
   290
	@return	TRUE if DFC was actually queued by this call
sl@0
   291
			FALSE if DFC was already queued on entry so this call did nothing
sl@0
   292
	@pre	Call in a thread context.
sl@0
   293
	@pre	Kernel must be unlocked.
sl@0
   294
	@pre	Do not call from an ISR.
sl@0
   295
	@pre    Do not call from an IDFC.
sl@0
   296
 */
sl@0
   297
EXPORT_C TBool TDfc::Enque(NFastMutex* aMutex)
sl@0
   298
	{
sl@0
   299
	CHECK_PRECONDITIONS(MASK_KERNEL_UNLOCKED|MASK_NOT_ISR|MASK_NOT_IDFC,"TDfc::Enque(NFastMutex* aMutex)");		
sl@0
   300
	if (!aMutex)
sl@0
   301
		aMutex=&TheScheduler.iLock;
sl@0
   302
	NKern::Lock();
sl@0
   303
	TBool ret = DoEnque();
sl@0
   304
	aMutex->Signal();
sl@0
   305
	NKern::Unlock();
sl@0
   306
	return ret;
sl@0
   307
	}
sl@0
   308
sl@0
   309
sl@0
   310
/** Returns a pointer to the thread on which a DFC runs
sl@0
   311
sl@0
   312
	@return	If this is a DFC and the DFC queue has been set, a pointer to the
sl@0
   313
			thread which will run the DFC.
sl@0
   314
			NULL if this is an IDFC or the DFC queue has not been set.
sl@0
   315
 */
sl@0
   316
EXPORT_C NThreadBase* TDfc::Thread()
sl@0
   317
	{
sl@0
   318
	if (IsIDFC())
sl@0
   319
		return 0;
sl@0
   320
	return iDfcQ ? iDfcQ->iThread : 0;
sl@0
   321
	}
sl@0
   322
sl@0
   323
sl@0
   324
/******************************************************************************
sl@0
   325
 * Idle notification
sl@0
   326
 ******************************************************************************/
sl@0
   327
sl@0
   328
/** Register an IDFC or a DFC to be called when the system goes idle
sl@0
   329
sl@0
   330
	This function does nothing if the IDFC/DFC is already queued.
sl@0
   331
sl@0
   332
	@return	TRUE if DFC was actually queued by this call
sl@0
   333
			FALSE if DFC was already queued on entry so this call did nothing
sl@0
   334
 */
sl@0
   335
EXPORT_C TBool TDfc::QueueOnIdle()
sl@0
   336
	{
sl@0
   337
	TInt irq=NKern::DisableAllInterrupts();
sl@0
   338
sl@0
   339
	// make sure DFC not already queued
sl@0
   340
	TBool ok = !TestAndSetQueued();
sl@0
   341
	if (ok)
sl@0
   342
		TheScheduler.iIdleDfcs.Add(this);
sl@0
   343
sl@0
   344
	NKern::RestoreInterrupts(irq);
sl@0
   345
	return ok;
sl@0
   346
	}
sl@0
   347
sl@0
   348
sl@0
   349
TUint32 NKern::IdleGenerationCount()
sl@0
   350
	{
sl@0
   351
	return TheScheduler.iIdleGenerationCount;
sl@0
   352
	}
sl@0
   353
sl@0
   354
sl@0
   355
void NKern::Idle()
sl@0
   356
	{
sl@0
   357
	TInt irq = NKern::DisableAllInterrupts();
sl@0
   358
#ifdef _DEBUG
sl@0
   359
	if (!TheScheduler.iIdleDfcs.IsEmpty() && TheScheduler.iDelayedQ.IsEmpty())
sl@0
   360
#else
sl@0
   361
	if (!TheScheduler.iIdleDfcs.IsEmpty())
sl@0
   362
#endif
sl@0
   363
		{
sl@0
   364
		++TheScheduler.iIdleGenerationCount;
sl@0
   365
		TheScheduler.iDfcs.MoveFrom(&TheScheduler.iIdleDfcs);
sl@0
   366
		TheScheduler.iDfcPendingFlag=1;
sl@0
   367
		NKern::RestoreInterrupts(irq);
sl@0
   368
		return;
sl@0
   369
		}
sl@0
   370
	NKern::RestoreInterrupts(irq);
sl@0
   371
	NKIdle(0);
sl@0
   372
	}
sl@0
   373
sl@0
   374
sl@0
   375
/******************************************************************************
sl@0
   376
 * Scheduler IDFC/DFC Processing
sl@0
   377
 ******************************************************************************/
sl@0
   378
sl@0
   379
#ifndef __SCHEDULER_MACHINE_CODED__
sl@0
   380
void TScheduler::QueueDfcs()
sl@0
   381
//
sl@0
   382
// Enter with interrupts off and kernel locked
sl@0
   383
// Leave with interrupts off and kernel locked
sl@0
   384
//
sl@0
   385
	{
sl@0
   386
	iInIDFC = TRUE;
sl@0
   387
	BTrace0(BTrace::ECpuUsage,BTrace::EIDFCStart);
sl@0
   388
	FOREVER
sl@0
   389
		{
sl@0
   390
		// remove from pending queue with interrupts disabled
sl@0
   391
		TDfc* d=(TDfc*)iDfcs.GetFirst();
sl@0
   392
		if (!d)
sl@0
   393
			break;
sl@0
   394
		NKern::EnableAllInterrupts();
sl@0
   395
		if (d->IsIDFC())
sl@0
   396
			{
sl@0
   397
			d->iQueued=FALSE;
sl@0
   398
			(*d->iFunction)(d->iPtr);
sl@0
   399
			}
sl@0
   400
		else
sl@0
   401
			d->DoEnqueFinal();
sl@0
   402
		NKern::DisableAllInterrupts();
sl@0
   403
		}
sl@0
   404
	iDfcPendingFlag = FALSE;
sl@0
   405
	BTrace0(BTrace::ECpuUsage,BTrace::EIDFCEnd);
sl@0
   406
	iInIDFC = FALSE;
sl@0
   407
	}
sl@0
   408
#endif
sl@0
   409
sl@0
   410
sl@0
   411
/******************************************************************************
sl@0
   412
 * Kernel-side asynchronous request DFCs
sl@0
   413
 ******************************************************************************/
sl@0
   414
sl@0
   415
EXPORT_C TAsyncRequest::TAsyncRequest(TDfcFn aFunction, TDfcQue* aDfcQ, TInt aPriority)
sl@0
   416
	: TDfc(aFunction, this, aDfcQ, aPriority), iCompletionObject(0), iCancel(0), iResult(0)
sl@0
   417
	{
sl@0
   418
	}
sl@0
   419
sl@0
   420
sl@0
   421
EXPORT_C void TAsyncRequest::Send(TDfc* aCompletionDfc)
sl@0
   422
	{
sl@0
   423
	__NK_ASSERT_DEBUG(!iCompletionObject);
sl@0
   424
	iCancel = EFalse;
sl@0
   425
	iCompletionObject = (TAny*)((TLinAddr)aCompletionDfc|1);
sl@0
   426
	TDfc::Enque();
sl@0
   427
	}
sl@0
   428
sl@0
   429
sl@0
   430
EXPORT_C void TAsyncRequest::Send(NFastSemaphore* aCompletionSemaphore)
sl@0
   431
	{
sl@0
   432
	__NK_ASSERT_DEBUG(!iCompletionObject);
sl@0
   433
	iCancel = EFalse;
sl@0
   434
	iCompletionObject = aCompletionSemaphore;
sl@0
   435
	TDfc::Enque();
sl@0
   436
	}
sl@0
   437
sl@0
   438
sl@0
   439
EXPORT_C TInt TAsyncRequest::SendReceive()
sl@0
   440
	{
sl@0
   441
	NFastSemaphore signal;
sl@0
   442
	NKern::FSSetOwner(&signal, 0);
sl@0
   443
	Send(&signal);
sl@0
   444
	NKern::FSWait(&signal);
sl@0
   445
	return iResult;
sl@0
   446
	}
sl@0
   447
sl@0
   448
sl@0
   449
EXPORT_C void TAsyncRequest::Cancel()
sl@0
   450
	{
sl@0
   451
	iCancel = ETrue;
sl@0
   452
	if(TDfc::Cancel())
sl@0
   453
		Complete(KErrCancel);
sl@0
   454
	}
sl@0
   455
sl@0
   456
sl@0
   457
EXPORT_C void TAsyncRequest::Complete(TInt aResult)
sl@0
   458
	{
sl@0
   459
	TLinAddr signal = (TLinAddr)__e32_atomic_swp_ord_ptr(&iCompletionObject, 0);
sl@0
   460
	if(signal)
sl@0
   461
		{
sl@0
   462
		iResult = aResult;
sl@0
   463
		if(signal&1)
sl@0
   464
			((TDfc*)(signal&~1))->Enque();
sl@0
   465
		else
sl@0
   466
			NKern::FSSignal((NFastSemaphore*)signal);
sl@0
   467
		}
sl@0
   468
	}