kernel/eka/nkernsmp/nk_timer.cpp
changeset 0 a41df078684a
child 90 947f0dc9f7a8
equal deleted inserted replaced
-1:000000000000 0:a41df078684a
       
     1 // Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of the License "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // e32\nkern\nk_timer.cpp
       
    15 // Fast Millisecond Timer Implementation
       
    16 // This file is just a template - you'd be mad not to machine code this
       
    17 // 
       
    18 //
       
    19 
       
    20 #include "nk_priv.h"
       
    21 
       
    22 #define i_NTimer_iState			i8888.iHState1
       
    23 #define i_NTimer_iCompleteInDfc	i8888.iHState2
       
    24 
       
    25 const TInt KTimerQDfcPriority=6;
       
    26 
       
    27 GLDEF_D NTimerQ TheTimerQ;
       
    28 
       
    29 extern "C" void send_irq_ipi(TSubScheduler*);
       
    30 
       
    31 #ifndef __MSTIM_MACHINE_CODED__
       
    32 #ifdef _DEBUG
       
    33 #define __DEBUG_CALLBACK(n)	{if (iDebugFn) (*iDebugFn)(iDebugPtr,n);}
       
    34 #else
       
    35 #define __DEBUG_CALLBACK(n)
       
    36 #endif
       
    37 
       
    38 /** Construct a nanokernel timer tied to a specified thread or group
       
    39 	
       
    40 
       
    41 	@param	aTied Pointer to the thread/group to which the timer should be tied
       
    42 	@param	aFunction Pointer to the function to call on timer expiry
       
    43 	@param	aPtr Parameter to pass to the expiry handler
       
    44 	
       
    45 	@pre	Any context
       
    46 
       
    47 	@publishedPartner
       
    48 	@prototype
       
    49  */
       
    50 EXPORT_C NTimer::NTimer(NSchedulable* aTied, NTimerFn aFunction, TAny* aPtr)
       
    51 	{
       
    52 	iPtr = aPtr;
       
    53 	iFn = aFunction;
       
    54 	iHType = EEventHandlerNTimer;
       
    55 //	i8888.iHState1 = EIdle;		done by NEventHandler constructor
       
    56 	if (aTied)
       
    57 		{
       
    58 		SetTied(aTied);
       
    59 		}
       
    60 	}
       
    61 
       
    62 
       
    63 /** Construct a nanokernel timer which mutates into and runs as a DFC on expiry
       
    64 	The DFC queue is not specified at object construction time, but must be set
       
    65 	using NTimer::SetDfcQ() before the timer is used.
       
    66 
       
    67 	@param	aFunction	Pointer to the function to call on timer expiry
       
    68 	@param	aPtr		Parameter to pass to the expiry handler
       
    69 	@param	aPriority	Priority of DFC within the queue (0 to 7, where 7 is highest)
       
    70 	
       
    71 	@pre	Any context
       
    72 
       
    73 	@publishedPartner
       
    74 	@prototype
       
    75  */
       
    76 EXPORT_C NTimer::NTimer(TDfcFn aFunction, TAny* aPtr, TInt aPriority)
       
    77 	{
       
    78 	iPtr = aPtr;
       
    79 	iFn = aFunction;
       
    80 	iTied = 0;
       
    81 	iHType = (TUint8)aPriority;
       
    82 //	i8888.iHState0 = 0;			done by NEventHandler constructor
       
    83 //	i8888.iHState1 = EIdle;		done by NEventHandler constructor
       
    84 //	i8888.iHState2 = 0;			done by NEventHandler constructor
       
    85 	}
       
    86 
       
    87 
       
    88 /** Construct a nanokernel timer which mutates into and runs as a DFC on expiry
       
    89 
       
    90 	@param	aFunction	Pointer to the function to call on timer expiry
       
    91 	@param	aPtr		Parameter to pass to the expiry handler
       
    92 	@param	aDfcQ		Pointer to DFC queue which this timer should use
       
    93 	@param	aPriority	Priority of DFC within the queue (0 to 7, where 7 is highest)
       
    94 	
       
    95 	@pre	Any context
       
    96 
       
    97 	@publishedPartner
       
    98 	@prototype
       
    99  */
       
   100 EXPORT_C NTimer::NTimer(TDfcFn aFunction, TAny* aPtr, TDfcQue* aDfcQ, TInt aPriority)
       
   101 	{
       
   102 	iPtr = aPtr;
       
   103 	iFn = aFunction;
       
   104 	iDfcQ = aDfcQ;
       
   105 	iHType = (TUint8)aPriority;
       
   106 //	i8888.iHState0 = 0;			done by NEventHandler constructor
       
   107 //	i8888.iHState1 = EIdle;		done by NEventHandler constructor
       
   108 //	i8888.iHState2 = 0;			done by NEventHandler constructor
       
   109 	}
       
   110 
       
   111 
       
   112 /** Set the DFC queue to be used by an NTimer constructed using a TDfcFn
       
   113 
       
   114 	@param	aDfcQ		Pointer to DFC queue which this timer should use
       
   115 
       
   116 	@pre	Timer cannot be in use
       
   117 	@pre	Any context
       
   118 
       
   119 	@publishedPartner
       
   120 	@prototype
       
   121  */
       
   122 EXPORT_C void NTimer::SetDfcQ(TDfcQue* aDfcQ)
       
   123 	{
       
   124 	__NK_ASSERT_ALWAYS(aDfcQ!=0);
       
   125 	__NK_ASSERT_ALWAYS(iHType < KNumDfcPriorities);
       
   126 	__NK_ASSERT_ALWAYS(i8816.iHState16==EIdle);
       
   127 	iDfcQ = aDfcQ;
       
   128 	}
       
   129 
       
   130 
       
   131 /** Tie a nanokernel timer to a thread or group
       
   132 
       
   133 	@param	aTied = pointer to thread or group to which IDFC should be tied
       
   134 	@return	KErrNone if successful
       
   135 	@return	KErrDied if thread has exited or group has been destroyed.
       
   136 
       
   137 	@pre Call in thread context, interrupts enabled
       
   138 	@pre Timer must not be queued or running
       
   139 	@pre Timer must not already be tied
       
   140 	@pre Must not be a mutating timer (constructed with TDfcFn)
       
   141 
       
   142 	@publishedPartner
       
   143 	@prototype
       
   144  */
       
   145 EXPORT_C TInt NTimer::SetTied(NSchedulable* aTied)
       
   146 	{
       
   147 	__NK_ASSERT_ALWAYS(!IsMutating());
       
   148 	__NK_ASSERT_ALWAYS(i8888.iHState1 == EIdle);
       
   149 	__NK_ASSERT_ALWAYS(aTied && !iTied);
       
   150 	NKern::Lock();
       
   151 	TInt r = aTied->AddTiedEvent(this);
       
   152 	__NK_ASSERT_ALWAYS(r==KErrNone || r==KErrDied);
       
   153 	NKern::Unlock();
       
   154 	return r;
       
   155 	}
       
   156 
       
   157 
       
   158 /** Destroy a nanokernel timer
       
   159 
       
   160 	@pre Call in thread context, interrupts enabled, preemption enabled
       
   161 	@pre Calling thread in critical section
       
   162 	@pre No fast mutex held
       
   163 
       
   164 	@publishedPartner
       
   165 	@prototype
       
   166  */
       
   167 EXPORT_C NTimer::~NTimer()
       
   168 	{
       
   169 	if (!IsMutating() && iTied)
       
   170 		{
       
   171 		NKern::Lock();
       
   172 		// remove from tied thread/group
       
   173 		NEventHandler::TiedLock.LockOnly();
       
   174 		NSchedulable* tied = iTied;
       
   175 		DoCancel(ECancelDestroy);
       
   176 		if (tied)	// might have been dequeued by thread/group termination
       
   177 			{
       
   178 			tied->AcqSLock();
       
   179 			if (iTiedLink.iNext)
       
   180 				{
       
   181 				iTiedLink.Deque();
       
   182 				iTiedLink.iNext = 0;
       
   183 				}
       
   184 			iTied = 0;
       
   185 			tied->RelSLock();
       
   186 			}
       
   187 		NEventHandler::TiedLock.UnlockOnly();
       
   188 		NKern::Unlock();
       
   189 		}
       
   190 	else if (IsMutating() && iDfcQ)
       
   191 		DoCancelMutating(ECancelDestroy);
       
   192 	else
       
   193 		DoCancel(ECancelDestroy);
       
   194 	}
       
   195 
       
   196 
       
   197 /** Starts a nanokernel timer in one-shot mode with ISR callback.
       
   198 	
       
   199 	Queues the timer to expire in the specified number of nanokernel ticks. The
       
   200 	actual wait time will be at least that much and may be up to one tick more.
       
   201 	The expiry handler will be called in ISR context.
       
   202 	
       
   203 	Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
       
   204 
       
   205 	@param	aTime Timeout in nanokernel ticks
       
   206 	
       
   207 	@return	KErrNone if no error; KErrInUse if timer is already active.
       
   208 	
       
   209 	@pre	Any context
       
   210 	
       
   211 	@see    NKern::TimerTicks()
       
   212  */
       
   213 EXPORT_C TInt NTimer::OneShot(TInt aTime)
       
   214 	{
       
   215 	return OneShot(aTime,FALSE);
       
   216 	}
       
   217 
       
   218 
       
   219 /** Starts a nanokernel timer in one-shot mode with ISR or DFC callback.
       
   220 	
       
   221 	Queues the timer to expire in the specified number of nanokernel ticks. The
       
   222 	actual wait time will be at least that much and may be up to one tick more.
       
   223 	For normal timers (constructed with NTimerFn) the expiry handler will be
       
   224 	called in either ISR context or in the context of the nanokernel timer
       
   225 	thread (DfcThread1). For mutating timers (constructed with TDfcFn) the
       
   226 	expiry handler is called in the context of the thread running the relevant
       
   227 	TDfcQue.
       
   228 
       
   229     Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
       
   230 
       
   231 	@param	aTime Timeout in nanokernel ticks
       
   232 	@param	aDfc TRUE if DFC callback required, FALSE if ISR callback required.
       
   233 			Note that this parameter is ignored for mutating timers.
       
   234 	
       
   235 	@return	KErrNone if no error
       
   236 	@return	KErrInUse if timer is already active.
       
   237 	@return	KErrDied if tied thread/group has exited
       
   238 	
       
   239 	@pre	Any context
       
   240 	
       
   241 	@see    NKern::TimerTicks()
       
   242  */
       
   243 EXPORT_C TInt NTimer::OneShot(TInt aTime, TBool aDfc)
       
   244 	{
       
   245 	__NK_ASSERT_DEBUG(aTime>=0);
       
   246 	/** iFn could be set to NULL after NTimer::OneShot(TInt, TDfc&) call.
       
   247 	Call-back mechanism cannot be changed in the life time of a timer. */
       
   248 	__NK_ASSERT_DEBUG(iFn!=NULL);
       
   249 
       
   250 	TInt irq = TheTimerQ.iTimerSpinLock.LockIrqSave();
       
   251 	if (!IsValid())
       
   252 		{
       
   253 		TheTimerQ.iTimerSpinLock.UnlockIrqRestore(irq);
       
   254 		return KErrDied;
       
   255 		}
       
   256 	TUint16 state = i8816.iHState16;
       
   257 	if (IsNormal())
       
   258 		state &= 0xFF;
       
   259 	else
       
   260 		aDfc = FALSE;	// mutating timers start as ISR completion
       
   261 	if (state!=EIdle)
       
   262 		{
       
   263 		TheTimerQ.iTimerSpinLock.UnlockIrqRestore(irq);
       
   264 		return KErrInUse;
       
   265 		}
       
   266 	mb();	// ensure that if we observe an idle state all accesses to the NTimer have also been observed
       
   267 	i_NTimer_iCompleteInDfc=TUint8(aDfc?1:0);
       
   268 	iTriggerTime=TheTimerQ.iMsCount+(TUint32)aTime;
       
   269 	TheTimerQ.Add(this);
       
   270 	TheTimerQ.iTimerSpinLock.UnlockIrqRestore(irq);
       
   271 	return KErrNone;
       
   272 	}
       
   273 
       
   274 
       
   275 /** Starts a nanokernel timer in one-shot mode with callback in dfc thread that provided DFC belongs to.
       
   276 	
       
   277 	Queues the timer to expire in the specified number of nanokernel ticks. The
       
   278 	actual wait time will be at least that much and may be up to one tick more.
       
   279 	On expiry aDfc will be queued in ISR context.
       
   280 
       
   281     Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
       
   282 
       
   283 	@param	aTime Timeout in nanokernel ticks
       
   284 	@param	aDfc - Dfc to be queued when the timer expires.
       
   285 	
       
   286 	@return	KErrNone if no error
       
   287 	@return	KErrInUse if timer is already active.
       
   288 	@return	KErrDied if tied thread/group has exited
       
   289 	
       
   290 	@pre	Any context
       
   291 	@pre	Must not be a mutating timer (constructed with TDfcFn)
       
   292 	
       
   293 	@see    NKern::TimerTicks()
       
   294  */
       
   295 EXPORT_C TInt NTimer::OneShot(TInt aTime, TDfc& aDfc)
       
   296 	{
       
   297 	__NK_ASSERT_DEBUG(!IsMutating());
       
   298 	__NK_ASSERT_DEBUG(aTime>=0);
       
   299 	TInt irq = TheTimerQ.iTimerSpinLock.LockIrqSave();
       
   300 	if (iHType != EEventHandlerNTimer)
       
   301 		{
       
   302 		TheTimerQ.iTimerSpinLock.UnlockIrqRestore(irq);
       
   303 		return KErrDied;
       
   304 		}
       
   305 	if (i_NTimer_iState!=EIdle)
       
   306 		{
       
   307 		TheTimerQ.iTimerSpinLock.UnlockIrqRestore(irq);
       
   308 		return KErrInUse;
       
   309 		}
       
   310 	mb();	// ensure that if we observe an idle state all accesses to the NTimer have also been observed
       
   311 	i_NTimer_iCompleteInDfc = 0;
       
   312 	iFn = NULL;
       
   313 	iPtr = (TAny*) &aDfc;
       
   314 	iTriggerTime=TheTimerQ.iMsCount+(TUint32)aTime;
       
   315 	TheTimerQ.Add(this);
       
   316 	TheTimerQ.iTimerSpinLock.UnlockIrqRestore(irq);
       
   317 	return KErrNone;
       
   318 	}
       
   319 
       
   320 
       
   321 /** Starts a nanokernel timer in zero-drift periodic mode with ISR or DFC callback.
       
   322 
       
   323 	Queues the timer to expire in the specified number of nanokernel ticks,
       
   324 	measured from the time at which it last expired. This allows exact periodic
       
   325 	timers to be implemented with no drift caused by delays in requeueing the
       
   326 	timer.
       
   327 
       
   328 	The expiry handler will be called in the same context as the previous timer
       
   329 	expiry. Generally the way this is used is that NTimer::OneShot() is used to start 
       
   330 	the first time interval and this specifies whether the callback is in ISR context 
       
   331 	or in the context of the nanokernel timer thread (DfcThread1) or other Dfc thread.
       
   332 	The expiry handler then uses NTimer::Again() to requeue the timer.
       
   333 
       
   334 	@param	aTime Timeout in nanokernel ticks
       
   335 
       
   336 	@return	KErrNone if no error
       
   337 	@return	KErrInUse if timer is already active;
       
   338 	@return	KErrArgument if the requested expiry time is in the past.
       
   339 	@return	KErrDied if tied thread/group has exited
       
   340 	        
       
   341 	@pre	Any context
       
   342  */
       
   343 EXPORT_C TInt NTimer::Again(TInt aTime)
       
   344 //
       
   345 // Wait aTime from last trigger time - used for periodic timers
       
   346 //
       
   347 	{
       
   348 	__NK_ASSERT_DEBUG(aTime>0);
       
   349 	TInt irq = TheTimerQ.iTimerSpinLock.LockIrqSave();
       
   350 	if (!IsValid())
       
   351 		{
       
   352 		TheTimerQ.iTimerSpinLock.UnlockIrqRestore(irq);
       
   353 		return KErrDied;
       
   354 		}
       
   355 	TUint16 state = i8816.iHState16;
       
   356 	if (IsNormal())
       
   357 		state &= 0xFF;
       
   358 	if (state!=EIdle)
       
   359 		{
       
   360 		TheTimerQ.iTimerSpinLock.UnlockIrqRestore(irq);
       
   361 		return KErrInUse;
       
   362 		}
       
   363 	mb();	// ensure that if we observe an idle state all accesses to the NTimer have also been observed
       
   364 	TUint32 nextTick=TheTimerQ.iMsCount;
       
   365 	TUint32 trigger=iTriggerTime+(TUint32)aTime;
       
   366 	TUint32 d=trigger-nextTick;
       
   367 	if (d>=0x80000000)
       
   368 		{
       
   369 		TheTimerQ.iTimerSpinLock.UnlockIrqRestore(irq);
       
   370 		return KErrArgument;		// requested time is in the past
       
   371 		}
       
   372 	iTriggerTime=trigger;
       
   373 	TheTimerQ.Add(this);
       
   374 	TheTimerQ.iTimerSpinLock.UnlockIrqRestore(irq);
       
   375 	return KErrNone;
       
   376 	}
       
   377 
       
   378 
       
   379 /** Cancels a nanokernel timer.
       
   380 
       
   381 	Removes this timer from the nanokernel timer queue. Does nothing if the
       
   382 	timer is inactive or has already expired.
       
   383 	Note that if the timer was queued and DFC callback requested it is possible
       
   384 	for the expiry handler to run even after Cancel() has been called. This will
       
   385 	occur in the case where DfcThread1 is preempted just before calling the
       
   386 	expiry handler for this timer and the preempting thread/ISR/IDFC calls
       
   387 	Cancel() on the timer.
       
   388 
       
   389 	@pre	Any context for a non-mutating NTimer (constructed with NTimerFn)
       
   390 	@pre	For mutating NTimer (constructed with TDfcFn), IDFC or thread context only.
       
   391 	@return	TRUE if timer was actually cancelled
       
   392 	@return	FALSE if timer was not cancelled - this could be because it was not
       
   393 				active or because its expiry handler was already running on
       
   394 				another CPU or in the timer DFC.
       
   395  */
       
   396 EXPORT_C TBool NTimer::Cancel()
       
   397 	{
       
   398 	if (IsMutating() && iDfcQ)
       
   399 		return DoCancelMutating(0);
       
   400 	return DoCancel(0)!=EIdle;
       
   401 	}
       
   402 
       
   403 void NTimer::DoCancel0(TUint aState)
       
   404 	{
       
   405 	if (aState>ETransferring && aState<=EFinal)	// idle or transferring timers are not on a queue
       
   406 		Deque();
       
   407 	switch (aState)
       
   408 		{
       
   409 		case ETransferring:	// signal DFC to abort this iteration
       
   410 			TheTimerQ.iTransferringCancelled=TRUE;
       
   411 			break;
       
   412 		case ECritical:		// signal DFC to abort this iteration
       
   413 			TheTimerQ.iCriticalCancelled=TRUE;
       
   414 			break;
       
   415 		case EFinal:
       
   416 			{
       
   417 			// Need to clear bit in iPresent if both final queues now empty
       
   418 			// NOTE: Timer might actually be on the completed queue rather than the final queue
       
   419 			//		 but the check is harmless in any case.
       
   420 			TInt i=iTriggerTime & NTimerQ::ETimerQMask;
       
   421 			NTimerQ::STimerQ& q=TheTimerQ.iTickQ[i];
       
   422 			if (q.iIntQ.IsEmpty() && q.iDfcQ.IsEmpty())
       
   423 				TheTimerQ.iPresent &= ~(1<<i);
       
   424 			break;
       
   425 			}
       
   426 		case EIdle:			// nothing to do
       
   427 		case EHolding:		// just deque
       
   428 		case EOrdered:		// just deque
       
   429 			break;
       
   430 		default:
       
   431 			__NK_ASSERT_ALWAYS(0);
       
   432 		}
       
   433 	}
       
   434 
       
   435 TUint NTimer::DoCancel(TUint aFlags)
       
   436 	{
       
   437 	NSchedulable* tied = 0;
       
   438 	TInt irq = NKern::DisableAllInterrupts();
       
   439 	TheTimerQ.iTimerSpinLock.LockOnly();
       
   440 	TUint state = i_NTimer_iState;
       
   441 	mb();
       
   442 	if (IsNormal() && state>=EEventQ)
       
   443 		{
       
   444 		// It's on a CPU's event handler queue
       
   445 		TInt cpu = state - EEventQ;
       
   446 		if (cpu < TheScheduler.iNumCpus)
       
   447 			{
       
   448 			TSubScheduler* ss = TheSubSchedulers + cpu;
       
   449 			ss->iEventHandlerLock.LockOnly();
       
   450 			state = i_NTimer_iState;
       
   451 			if (state != EIdle)
       
   452 				{
       
   453 				Deque();	// we got to it first
       
   454 				tied = iTied;
       
   455 				i_NTimer_iState = EIdle;
       
   456 				}
       
   457 			ss->iEventHandlerLock.UnlockOnly();
       
   458 			goto end;
       
   459 			}
       
   460 		}
       
   461 	DoCancel0(state);
       
   462 	if (IsMutating())
       
   463 		i8816.iHState16 = 0;
       
   464 	else
       
   465 		i_NTimer_iState=EIdle;
       
   466 end:
       
   467 	if (aFlags & ECancelDestroy)
       
   468 		iHType = EEventHandlerDummy;
       
   469 	TheTimerQ.iTimerSpinLock.UnlockOnly();
       
   470 	if (tied)
       
   471 		tied->EndTiedEvent();	// FIXME - Could be called in thread context
       
   472 	NKern::RestoreInterrupts(irq);
       
   473 	return state;
       
   474 	}
       
   475 
       
   476 TBool NTimer::DoCancelMutating(TUint aFlags)
       
   477 	{
       
   478 	CHECK_PRECONDITIONS(MASK_NOT_ISR,"NTimer::Cancel (mutating NTimer)");
       
   479 	TSubScheduler& ss0 = SubScheduler();
       
   480 	TBool wait = FALSE;
       
   481 	TInt cpu = -1;
       
   482 	TBool result = TRUE;
       
   483 	TDfc* d = (TDfc*)this;
       
   484 	NKern::Lock();
       
   485 	TDfcQue* q = iDfcQ;
       
   486 	NThreadBase* t = q->iThread;
       
   487 	t->AcqSLock();
       
   488 	TheTimerQ.iTimerSpinLock.LockIrq();
       
   489 
       
   490 	// 0000->0000, XX00->ZZ00, xxYY->zzYY
       
   491 	TUint state = d->CancelInitialStateChange();
       
   492 	if (state & 0xFF00)
       
   493 		{
       
   494 		// someone else cancelling at the same time - just wait for them to finish
       
   495 		// they can only be waiting for the cancel IPI
       
   496 		result = FALSE;
       
   497 		wait = TRUE;
       
   498 		goto end;
       
   499 		}
       
   500 	if (state == 0)	// timer was not active
       
   501 		{
       
   502 		result = FALSE;
       
   503 		goto end;
       
   504 		}
       
   505 	if (state>=ETransferring && state<=EFinal)
       
   506 		{
       
   507 		DoCancel0(state);
       
   508 		// cancel is complete
       
   509 		goto reset;
       
   510 		}
       
   511 	if (state==1)
       
   512 		{
       
   513 		// on DFC final queue
       
   514 		q->Remove((TPriListLink*)this);
       
   515 		goto reset;
       
   516 		}
       
   517 	// must be on IDFC queue - need to send cancel IPI
       
   518 	__NK_ASSERT_ALWAYS((state>>5)==4);
       
   519 	cpu = state & 0x1f;
       
   520 	if (TUint(cpu) == ss0.iCpuNum)
       
   521 		{
       
   522 		// it's on this CPU's IDFC queue so just dequeue it and finish
       
   523 		Deque();
       
   524 		cpu = -1;
       
   525 reset:
       
   526 		d->ResetState();	// release semantics
       
   527 		}
       
   528 end:
       
   529 	if (aFlags & ECancelDestroy)
       
   530 		iHType = EEventHandlerDummy;
       
   531 	TheTimerQ.iTimerSpinLock.UnlockIrq();
       
   532 	t->RelSLock();
       
   533 	if (cpu>=0)
       
   534 		{
       
   535 		TCancelIPI ipi;
       
   536 		ipi.Send(d, cpu);
       
   537 		ipi.WaitCompletion();
       
   538 		wait = TRUE;
       
   539 		}
       
   540 	if (wait)
       
   541 		{
       
   542 		TUint n = 0x01000000;
       
   543 		while ((i8816.iHState16>>8) & ss0.iCpuMask)
       
   544 			{
       
   545 			__chill();
       
   546 			if (!--n)
       
   547 				__crash();
       
   548 			}
       
   549 		}
       
   550 	NKern::Unlock();
       
   551 	return result;
       
   552 	}
       
   553 #endif
       
   554 
       
   555 
       
   556 /** Obtains the address of the nanokernel timer queue object.
       
   557 
       
   558 	Not intended for general use. Intended only for base ports in order to get
       
   559 	the address used to call NTimerQ::Tick() with.
       
   560 
       
   561 	@return	The address of the nanokernel timer queue object
       
   562 	@pre	Any context
       
   563  */
       
   564 EXPORT_C TAny* NTimerQ::TimerAddress()
       
   565 	{
       
   566 	return &TheTimerQ;
       
   567 	}
       
   568 
       
   569 NTimerQ::NTimerQ()
       
   570 	:	iDfc(NTimerQ::DfcFn,this,NULL,KTimerQDfcPriority),
       
   571 		iDfcCompleteCount(1),
       
   572 		iTimerSpinLock(TSpinLock::EOrderNTimerQ)
       
   573 	{
       
   574 	// NOTE: All other members are initialised to zero since the single instance
       
   575 	//		 of NTimerQ resides in .bss
       
   576 	}
       
   577 
       
   578 void NTimerQ::Init1(TInt aTickPeriod)
       
   579 	{
       
   580 	TheTimerQ.iTickPeriod=aTickPeriod;
       
   581 	__KTRACE_OPT(KBOOT,DEBUGPRINT("NTimerQ::Init1 - period %d us",aTickPeriod));
       
   582 	__KTRACE_OPT(KMEMTRACE, DEBUGPRINT("MT:P %d",aTickPeriod));
       
   583 	}
       
   584 
       
   585 void NTimerQ::Init3(TDfcQue* aDfcQ)
       
   586 	{
       
   587 	__KTRACE_OPT(KBOOT,DEBUGPRINT("NTimerQ::Init3 DFCQ at %08x",aDfcQ));
       
   588 	TheTimerQ.iDfc.SetDfcQ(aDfcQ);
       
   589 	}
       
   590 
       
   591 #ifndef __MSTIM_MACHINE_CODED__
       
   592 void NTimerQ::Add(NTimer* aTimer)
       
   593 //
       
   594 //	Internal function to add a timer to the queue.
       
   595 //	Enter and return with timer queue spin lock held.
       
   596 //
       
   597 	{
       
   598 	TInt t=TInt(aTimer->iTriggerTime-iMsCount);
       
   599 	if (t<ENumTimerQueues)
       
   600 		AddFinal(aTimer);
       
   601 	else
       
   602 		{
       
   603 		// >=32ms to expiry, so put on holding queue
       
   604 		aTimer->i_NTimer_iState=NTimer::EHolding;
       
   605 		iHoldingQ.Add(aTimer);
       
   606 		}
       
   607 	}
       
   608 
       
   609 void NTimerQ::AddFinal(NTimer* aTimer)
       
   610 //
       
   611 //	Internal function to add a timer to the corresponding final queue.
       
   612 //	Enter and return with timer queue spin lock held.
       
   613 //
       
   614 	{
       
   615 	TInt i=aTimer->iTriggerTime & ETimerQMask;
       
   616 	SDblQue* pQ;
       
   617 	if (aTimer->i_NTimer_iCompleteInDfc)
       
   618 		pQ=&iTickQ[i].iDfcQ;
       
   619 	else
       
   620 		pQ=&iTickQ[i].iIntQ;
       
   621 	iPresent |= (1<<i);
       
   622 	aTimer->i_NTimer_iState=NTimer::EFinal;
       
   623 	pQ->Add(aTimer);
       
   624 	}
       
   625 
       
   626 void NTimerQ::DfcFn(TAny* aPtr)
       
   627 	{
       
   628 	((NTimerQ*)aPtr)->Dfc();
       
   629 	}
       
   630 
       
   631 void NTimerQ::Dfc()
       
   632 //
       
   633 // Do deferred timer queue processing and/or DFC completions
       
   634 //
       
   635 	{
       
   636 	// First transfer entries on the Ordered queue to the Final queues
       
   637 	FOREVER
       
   638 		{
       
   639 		iTimerSpinLock.LockIrq();
       
   640 		if (iOrderedQ.IsEmpty())
       
   641 			break;
       
   642 		NTimer* pC=(NTimer*)iOrderedQ.First();
       
   643 		TInt remain=pC->iTriggerTime-iMsCount;
       
   644 		if (remain>=ENumTimerQueues)
       
   645 			break;
       
   646 
       
   647 		// If remaining time <32 ticks, add it to final queue;
       
   648 		// also if remain < 0 we've 'missed it' so add to final queue.
       
   649 		pC->Deque();
       
   650 		AddFinal(pC);
       
   651 		iTimerSpinLock.UnlockIrq();
       
   652 		__DEBUG_CALLBACK(0);
       
   653 		}
       
   654 	iTimerSpinLock.UnlockIrq();
       
   655 	__DEBUG_CALLBACK(1);
       
   656 
       
   657 	// Next transfer entries on the Holding queue to the Ordered queue or final queue
       
   658 	FOREVER
       
   659 		{
       
   660 		iTimerSpinLock.LockIrq();
       
   661 		if (iHoldingQ.IsEmpty())
       
   662 			break;
       
   663 		NTimer* pC=(NTimer*)iHoldingQ.First();
       
   664 		pC->Deque();
       
   665 		pC->i_NTimer_iState=NTimer::ETransferring;
       
   666 		iTransferringCancelled=FALSE;
       
   667 		TUint32 trigger=pC->iTriggerTime;
       
   668 		if (TInt(trigger-iMsCount)<ENumTimerQueues)
       
   669 			{
       
   670 			// <32ms remaining so put it on final queue
       
   671 			AddFinal(pC);
       
   672 			}
       
   673 		else
       
   674 			{
       
   675 			FOREVER
       
   676 				{
       
   677 				iTimerSpinLock.UnlockIrq();
       
   678 				__DEBUG_CALLBACK(2);
       
   679 
       
   680 				// we now need to walk ordered queue to find correct position for pC
       
   681 				SDblQueLink* anchor=&iOrderedQ.iA;
       
   682 				iCriticalCancelled=FALSE;
       
   683 				iTimerSpinLock.LockIrq();
       
   684 				NTimer* pN=(NTimer*)iOrderedQ.First();
       
   685 				while (pN!=anchor && !iTransferringCancelled)
       
   686 					{
       
   687 					if ((pN->iTriggerTime-trigger)<0x80000000u)
       
   688 						break;	// insert before pN
       
   689 					pN->i_NTimer_iState=NTimer::ECritical;
       
   690 					iTimerSpinLock.UnlockIrq();
       
   691 					__DEBUG_CALLBACK(3);
       
   692 					iTimerSpinLock.LockIrq();
       
   693 					if (iCriticalCancelled)
       
   694 						break;
       
   695 					pN->i_NTimer_iState=NTimer::EOrdered;
       
   696 					pN=(NTimer*)pN->iNext;
       
   697 					}
       
   698 
       
   699 				if (iTransferringCancelled)
       
   700 					break;		// this one has been cancelled, go on to next one
       
   701 				if (!iCriticalCancelled)
       
   702 					{
       
   703 					pC->InsertBefore(pN);
       
   704 					pC->i_NTimer_iState=NTimer::EOrdered;
       
   705 					break;		// done this one
       
   706 					}
       
   707 				}
       
   708 			}
       
   709 		iTimerSpinLock.UnlockIrq();
       
   710 		__DEBUG_CALLBACK(4);
       
   711 		}
       
   712 	iTimerSpinLock.UnlockIrq();
       
   713 	__DEBUG_CALLBACK(5);
       
   714 
       
   715 	// Finally do call backs for timers which requested DFC callback
       
   716 	FOREVER
       
   717 		{
       
   718 		iTimerSpinLock.LockIrq();
       
   719 		if (iCompletedQ.IsEmpty())
       
   720 			break;
       
   721 		NTimer* pC=(NTimer*)iCompletedQ.First();
       
   722 		pC->Deque();
       
   723 		pC->i_NTimer_iState=NTimer::EIdle;
       
   724 		TAny* p=pC->iPtr;
       
   725 		NTimerFn f=pC->iFn;
       
   726 		iTimerSpinLock.UnlockIrq();
       
   727 		__DEBUG_CALLBACK(7);
       
   728 		(*f)(p);
       
   729 		}
       
   730 	iTimerSpinLock.UnlockIrq();
       
   731 	__e32_atomic_add_rel32(&iDfcCompleteCount, 2);
       
   732 	}
       
   733 
       
   734 
       
   735 /** Tick over the nanokernel timer queue.
       
   736 	This function should be called by the base port in the system tick timer ISR.
       
   737 	It should not be called at any other time.
       
   738 	The value of 'this' to pass is the value returned by NTimerQ::TimerAddress().
       
   739 
       
   740 	@see NTimerQ::TimerAddress()
       
   741  */
       
   742 EXPORT_C void NTimerQ::Tick()
       
   743 	{
       
   744 	TInt irq = iTimerSpinLock.LockIrqSave();
       
   745 	TInt i=iMsCount & ETimerQMask;
       
   746 	iMsCount++;
       
   747 	STimerQ* pQ=iTickQ+i;
       
   748 	iPresent &= ~(1<<i);
       
   749 	TBool doDfc=FALSE;
       
   750 	if (!pQ->iDfcQ.IsEmpty())
       
   751 		{
       
   752 		// transfer DFC completions to completed queue and queue DFC
       
   753 		iCompletedQ.MoveFrom(&pQ->iDfcQ);
       
   754 		doDfc=TRUE;
       
   755 		}
       
   756 	if ((i&(ETimerQMask>>1))==0)
       
   757 		{
       
   758 		// Every 16 ticks we check if a DFC is required.
       
   759 		// This allows a DFC latency of up to 16 ticks before timers are missed.
       
   760 		if (!iHoldingQ.IsEmpty())
       
   761 			{
       
   762 			doDfc=TRUE;				// if holding queue nonempty, queue DFC to sort
       
   763 			}
       
   764 		else if (!iOrderedQ.IsEmpty())
       
   765 			{
       
   766 			// if first ordered queue entry expires in <32ms, queue the DFC to transfer
       
   767 			NTimer* pC=(NTimer*)iOrderedQ.First();
       
   768 			TUint x = pC->iTriggerTime - iMsCount;
       
   769 			if (x < (TUint)ENumTimerQueues)
       
   770 				{
       
   771 				doDfc=TRUE;
       
   772 				}
       
   773 			}
       
   774 		}
       
   775 	if (!pQ->iIntQ.IsEmpty())
       
   776 		{
       
   777 		// transfer ISR completions to a temporary queue
       
   778 		// careful here - other CPUs could dequeue timers!
       
   779 		SDblQue q(&pQ->iIntQ,0);
       
   780 		for (; !q.IsEmpty(); iTimerSpinLock.LockIrqSave())
       
   781 			{
       
   782 			NTimer* pC=(NTimer*)q.First();
       
   783 			pC->Deque();
       
   784 			if (pC->IsMutating())
       
   785 				{
       
   786 				pC->AddAsDFC();			//mutate NTimer into TDfc and Add() it
       
   787 				iTimerSpinLock.UnlockIrqRestore(irq);
       
   788 				continue;
       
   789 				}
       
   790 			if (!pC->iFn)
       
   791 				{
       
   792 				pC->i_NTimer_iState=NTimer::EIdle;
       
   793 				iTimerSpinLock.UnlockIrqRestore(irq);
       
   794 				((TDfc*)(pC->iPtr))->Add();
       
   795 				continue;
       
   796 				}
       
   797 			NSchedulable* tied = pC->iTied;
       
   798 			if (tied)
       
   799 				{
       
   800 				TInt cpu = tied->BeginTiedEvent();
       
   801 				if (cpu != NKern::CurrentCpu())
       
   802 					{
       
   803 					pC->i_NTimer_iState = TUint8(NTimer::EEventQ + cpu);
       
   804 					TSubScheduler* ss = TheSubSchedulers + cpu;
       
   805 					TBool kick = ss->QueueEvent(pC);
       
   806 					iTimerSpinLock.UnlockIrqRestore(irq);
       
   807 					if (kick)
       
   808 						send_irq_ipi(ss);
       
   809 					continue;
       
   810 					}
       
   811 				}
       
   812 			pC->i_NTimer_iState=NTimer::EIdle;
       
   813 			TAny* p = pC->iPtr;
       
   814 			NTimerFn f = pC->iFn;
       
   815 			iTimerSpinLock.UnlockIrqRestore(irq);
       
   816 			(*f)(p);
       
   817 			if (tied)
       
   818 				tied->EndTiedEvent();
       
   819 			}
       
   820 		}
       
   821 	iTimerSpinLock.UnlockIrqRestore(irq);
       
   822 	if (doDfc)
       
   823 		iDfc.Add();
       
   824 	}
       
   825 
       
   826 
       
   827 /** Mutate an NTimer into a DFC and Add() it
       
   828 
       
   829 If NTimer state is EFinal, change to DFC state 008n and add to endogenous IDFC
       
   830 queue for this CPU.
       
   831 
       
   832 Enter and return with IRQs disabled and timer spin lock held
       
   833 No need to worry about Cancel()s since timer spin lock is held
       
   834 Don't touch iHState0
       
   835 
       
   836 @internalComponent
       
   837 */
       
   838 void NTimer::AddAsDFC()
       
   839 	{
       
   840 	TSubScheduler& ss = SubScheduler();
       
   841 	i8816.iHState16 = (TUint16)(0x80|ss.iCpuNum);
       
   842 	ss.iDfcs.Add(this);
       
   843 	ss.iDfcPendingFlag = 1;
       
   844 	}
       
   845 
       
   846 
       
   847 /** Check if a nanokernel timer is pending or not
       
   848 
       
   849 	@return	TRUE if the timer is pending (OneShot() etc. would return KErrInUse)
       
   850 	@return FALSE if the timer is idle (OneShot() etc. would succeed)
       
   851 	@pre	Any context
       
   852 
       
   853 	@publishedPartner
       
   854 	@prototype
       
   855  */
       
   856 EXPORT_C TBool NTimer::IsPending()
       
   857 	{
       
   858 	TUint16 state = i8816.iHState16;
       
   859 	return state != EIdle;
       
   860 	}
       
   861 
       
   862 
       
   863 /** Return the number of ticks before the next nanokernel timer expiry.
       
   864 	May on occasion return a pessimistic estimate (i.e. too low).
       
   865 	Used by base port to disable the system tick interrupt when the system
       
   866 	is idle.
       
   867 
       
   868 	@return	The number of ticks before the next nanokernel timer expiry.
       
   869 	
       
   870 	@pre	Interrupts must be disabled.
       
   871 	
       
   872 	@post	Interrupts are disabled.
       
   873  */
       
   874 EXPORT_C TInt NTimerQ::IdleTime()
       
   875 	{
       
   876 	CHECK_PRECONDITIONS(MASK_INTERRUPTS_DISABLED,"NTimerQ::IdleTime");	
       
   877 	NTimerQ& m=TheTimerQ;
       
   878 	TUint32 next=m.iMsCount;	// number of next tick
       
   879 	TUint32 p=m.iPresent;
       
   880 	TInt r=KMaxTInt;
       
   881 	if (p)
       
   882 		{
       
   883 		// Final queues nonempty
       
   884 		TInt nx=next&0x1f;				// number of next tick modulo 32
       
   885 		p=(p>>nx)|(p<<(32-nx));			// rotate p right by nx (so lsb corresponds to next tick)
       
   886 		r=__e32_find_ls1_32(p);	// find number of zeros before LS 1
       
   887 		}
       
   888 	if (!m.iHoldingQ.IsEmpty())
       
   889 		{
       
   890 		// Sort operation required - need to process next tick divisible by 16
       
   891 		TInt nx=next&0x0f;				// number of next tick modulo 16
       
   892 		TInt r2=nx?(16-nx):0;			// number of ticks before next divisible by 16
       
   893 		if (r2<r)
       
   894 			r=r2;
       
   895 		}
       
   896 	if (!m.iOrderedQ.IsEmpty())
       
   897 		{
       
   898 		// Timers present on ordered queue
       
   899 		NTimer* pC=(NTimer*)m.iOrderedQ.First();
       
   900 		TUint32 tt=pC->iTriggerTime;
       
   901 		tt=(tt&~0x0f)-16;				// time at which transfer to final queue would occur
       
   902 		TInt r3=(TInt)(tt-next);
       
   903 		if (r3<r)
       
   904 			r=r3;
       
   905 		}
       
   906 	return r;
       
   907 	}
       
   908 #endif
       
   909 
       
   910 
       
   911 /** Advance the nanokernel timer queue by the specified number of ticks.
       
   912 	It is assumed that no timers expire as a result of this.
       
   913 	Used by base port when system comes out of idle mode after disabling the
       
   914 	system tick interrupt to bring the timer queue up to date.
       
   915 
       
   916 	@param	aTicks Number of ticks skipped due to tick suppression
       
   917 
       
   918 	@pre	Interrupts must be disabled.
       
   919 
       
   920 	@post	Interrupts are disabled.
       
   921  */
       
   922 EXPORT_C void NTimerQ::Advance(TInt aTicks)
       
   923 	{
       
   924 	CHECK_PRECONDITIONS(MASK_INTERRUPTS_DISABLED,"NTimerQ::Advance");	
       
   925 	TheTimerQ.iMsCount+=(TUint32)aTicks;
       
   926 	}
       
   927 
       
   928 
       
   929 /**	Returns the period of the nanokernel timer.
       
   930 	@return Period in microseconds
       
   931 	@pre any context
       
   932 	@see NTimer
       
   933  */
       
   934 EXPORT_C TInt NKern::TickPeriod()
       
   935 	{
       
   936 	return TheTimerQ.iTickPeriod;
       
   937 	}
       
   938 
       
   939 
       
   940 /**	Converts a time interval to timer ticks.
       
   941 
       
   942 	@param aMilliseconds time interval in milliseconds.
       
   943 	@return Number of nanokernel timer ticks.  Non-integral results are rounded up.
       
   944 
       
   945  	@pre aMilliseconds should be <=2147483 to avoid integer overflow.
       
   946 	@pre any context
       
   947  */
       
   948 EXPORT_C TInt NKern::TimerTicks(TInt aMilliseconds)
       
   949 	{
       
   950 	__ASSERT_WITH_MESSAGE_DEBUG(aMilliseconds<=2147483,"aMilliseconds should be <=2147483","NKern::TimerTicks");
       
   951 	TUint32 msp=TheTimerQ.iTickPeriod;
       
   952 	if (msp==1000)	// will be true except on pathological hardware
       
   953 		return aMilliseconds;
       
   954 	TUint32 us=(TUint32)aMilliseconds*1000;
       
   955 	return (us+msp-1)/msp;
       
   956 	}
       
   957