os/kernelhwsrv/kernel/eka/nkern/nk_timer.cpp
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/kernelhwsrv/kernel/eka/nkern/nk_timer.cpp	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,592 @@
     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\nk_timer.cpp
    1.18 +// Fast Millisecond Timer Implementation
    1.19 +// This file is just a template - you'd be mad not to machine code this
    1.20 +// 
    1.21 +//
    1.22 +
    1.23 +#include "nk_priv.h"
    1.24 +
    1.25 +const TInt KTimerQDfcPriority=6;
    1.26 +
    1.27 +GLDEF_D NTimerQ TheTimerQ;
    1.28 +
    1.29 +#ifndef __MSTIM_MACHINE_CODED__
    1.30 +#ifdef _DEBUG
    1.31 +#define __DEBUG_CALLBACK(n)	{if (iDebugFn) (*iDebugFn)(iDebugPtr,n);}
    1.32 +#else
    1.33 +#define __DEBUG_CALLBACK(n)
    1.34 +#endif
    1.35 +
    1.36 +
    1.37 +/** Starts a nanokernel timer in one-shot mode with ISR callback.
    1.38 +	
    1.39 +	Queues the timer to expire in the specified number of nanokernel ticks. The
    1.40 +	actual wait time will be at least that much and may be up to one tick more.
    1.41 +	The expiry handler will be called in ISR context.
    1.42 +	
    1.43 +	Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
    1.44 +
    1.45 +	@param	aTime Timeout in nanokernel ticks
    1.46 +	
    1.47 +	@return	KErrNone if no error; KErrInUse if timer is already active.
    1.48 +	
    1.49 +	@pre	Any context
    1.50 +	
    1.51 +	@see    NKern::TimerTicks()
    1.52 + */
    1.53 +EXPORT_C TInt NTimer::OneShot(TInt aTime)
    1.54 +	{
    1.55 +	return OneShot(aTime,FALSE);
    1.56 +	}
    1.57 +
    1.58 +
    1.59 +/** Starts a nanokernel timer in one-shot mode with ISR or DFC callback.
    1.60 +	
    1.61 +	Queues the timer to expire in the specified number of nanokernel ticks. The
    1.62 +	actual wait time will be at least that much and may be up to one tick more.
    1.63 +	The expiry handler will be called in either ISR context or in the context
    1.64 +	of the nanokernel timer thread (DfcThread1).
    1.65 +
    1.66 +    Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
    1.67 +
    1.68 +	@param	aTime Timeout in nanokernel ticks
    1.69 +	@param	aDfc TRUE if DFC callback required, FALSE if ISR callback required.
    1.70 +	
    1.71 +	@return	KErrNone if no error; KErrInUse if timer is already active.
    1.72 +	
    1.73 +	@pre	Any context
    1.74 +	
    1.75 +	@see    NKern::TimerTicks()
    1.76 + */
    1.77 +EXPORT_C TInt NTimer::OneShot(TInt aTime, TBool aDfc)
    1.78 +	{
    1.79 +	__NK_ASSERT_DEBUG(aTime>=0);
    1.80 +
    1.81 +	/** iFunction could be set to NULL after NTimer::OneShot(TInt, TDfc&) call.
    1.82 +	Call-back mechanism cannot be changed in the life time of a timer. */
    1.83 +	__NK_ASSERT_DEBUG(iFunction!=NULL); 
    1.84 +
    1.85 +	TInt irq=NKern::DisableAllInterrupts();
    1.86 +	if (iState!=EIdle)
    1.87 +		{
    1.88 +		NKern::RestoreInterrupts(irq);
    1.89 +		return KErrInUse;
    1.90 +		}
    1.91 +	iCompleteInDfc=TUint8(aDfc?1:0);
    1.92 +	iTriggerTime=TheTimerQ.iMsCount+(TUint32)aTime;
    1.93 +	TheTimerQ.Add(this);
    1.94 +	NKern::RestoreInterrupts(irq);
    1.95 +	return KErrNone;
    1.96 +	}
    1.97 +
    1.98 +/** Starts a nanokernel timer in one-shot mode with callback in dfc thread that provided DFC belongs to.
    1.99 +	
   1.100 +	Queues the timer to expire in the specified number of nanokernel ticks. The
   1.101 +	actual wait time will be at least that much and may be up to one tick more.
   1.102 +	On expiry aDfc will be queued in ISR context.
   1.103 +
   1.104 +    Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
   1.105 +
   1.106 +	@param	aTime Timeout in nanokernel ticks
   1.107 +	@param	aDfc - Dfc to be queued when the timer expires.
   1.108 +	
   1.109 +	@return	KErrNone if no error; KErrInUse if timer is already active.
   1.110 +	
   1.111 +	@pre	Any context
   1.112 +	
   1.113 +	@see    NKern::TimerTicks()
   1.114 + */
   1.115 +EXPORT_C TInt NTimer::OneShot(TInt aTime, TDfc& aDfc)
   1.116 +	{
   1.117 +	__NK_ASSERT_DEBUG(aTime>=0);
   1.118 +	TInt irq=NKern::DisableAllInterrupts();
   1.119 +	if (iState!=EIdle)
   1.120 +		{
   1.121 +		NKern::RestoreInterrupts(irq);
   1.122 +		return KErrInUse;
   1.123 +		}
   1.124 +	iCompleteInDfc = 0;
   1.125 +	iFunction = NULL;
   1.126 +	iPtr = (TAny*) &aDfc;
   1.127 +	iTriggerTime=TheTimerQ.iMsCount+(TUint32)aTime;
   1.128 +	TheTimerQ.Add(this);
   1.129 +	NKern::RestoreInterrupts(irq);
   1.130 +	return KErrNone;
   1.131 +	}
   1.132 +
   1.133 +
   1.134 +/** Starts a nanokernel timer in zero-drift periodic mode with ISR or DFC callback.
   1.135 +
   1.136 +	Queues the timer to expire in the specified number of nanokernel ticks,
   1.137 +	measured from the time at which it last expired. This allows exact periodic
   1.138 +	timers to be implemented with no drift caused by delays in requeueing the
   1.139 +	timer.
   1.140 +
   1.141 +	The expiry handler will be called in the same context as the previous timer
   1.142 +	expiry. Generally the way this is used is that NTimer::OneShot() is used to start 
   1.143 +	the first time interval and this specifies whether the callback is in ISR context 
   1.144 +	or in the context of the nanokernel timer thread (DfcThread1) or other Dfc thread.
   1.145 +	The expiry handler then uses NTimer::Again() to requeue the timer.
   1.146 +
   1.147 +	@param	aTime Timeout in nanokernel ticks
   1.148 +
   1.149 +	@return	KErrNone if no error; KErrInUse if timer is already active;
   1.150 +	        KErrArgument if the requested expiry time is in the past.
   1.151 +	        
   1.152 +	@pre	Any context
   1.153 + */
   1.154 +EXPORT_C TInt NTimer::Again(TInt aTime)
   1.155 +//
   1.156 +// Wait aTime from last trigger time - used for periodic timers
   1.157 +//
   1.158 +	{
   1.159 +	__NK_ASSERT_DEBUG(aTime>0);
   1.160 +	TInt irq=NKern::DisableAllInterrupts();
   1.161 +	if (iState!=EIdle)
   1.162 +		{
   1.163 +		NKern::RestoreInterrupts(irq);
   1.164 +		return KErrInUse;
   1.165 +		}
   1.166 +	TUint32 nextTick=TheTimerQ.iMsCount;
   1.167 +	TUint32 trigger=iTriggerTime+(TUint32)aTime;
   1.168 +	TUint32 d=trigger-nextTick;
   1.169 +	if (d>=0x80000000)
   1.170 +		{
   1.171 +		NKern::RestoreInterrupts(irq);
   1.172 +		return KErrArgument;		// requested time is in the past
   1.173 +		}
   1.174 +	iTriggerTime=trigger;
   1.175 +	TheTimerQ.Add(this);
   1.176 +	NKern::RestoreInterrupts(irq);
   1.177 +	return KErrNone;
   1.178 +	}
   1.179 +
   1.180 +
   1.181 +/** Cancels a nanokernel timer.
   1.182 +
   1.183 +	Removes this timer from the nanokernel timer queue. Does nothing if the
   1.184 +	timer is inactive or has already expired.
   1.185 +	Note that if the timer was queued and DFC callback requested it is possible
   1.186 +	for the expiry handler to run even after Cancel() has been called. This will
   1.187 +	occur in the case where DfcThread1 is preempted just before calling the
   1.188 +	expiry handler for this timer and the preempting thread/ISR/IDFC calls
   1.189 +	Cancel() on the timer.
   1.190 +
   1.191 +	@pre	Any context
   1.192 +	@return	TRUE if timer was actually cancelled
   1.193 +	@return	FALSE if timer was not cancelled - this could be because it was not
   1.194 +				active or because its expiry handler was already running on
   1.195 +				another CPU or in the timer DFC.
   1.196 + */
   1.197 +EXPORT_C TBool NTimer::Cancel()
   1.198 +	{
   1.199 +	TBool result = TRUE;
   1.200 +	TInt irq=NKern::DisableAllInterrupts();
   1.201 +	if (iState>ETransferring)	// idle or transferring timers are not on a queue
   1.202 +		Deque();
   1.203 +	switch (iState)
   1.204 +		{
   1.205 +		case ETransferring:	// signal DFC to abort this iteration
   1.206 +			TheTimerQ.iTransferringCancelled=TRUE;
   1.207 +			break;
   1.208 +		case ECritical:		// signal DFC to abort this iteration
   1.209 +			TheTimerQ.iCriticalCancelled=TRUE;
   1.210 +			break;
   1.211 +		case EFinal:
   1.212 +			{
   1.213 +			// Need to clear bit in iPresent if both final queues now empty
   1.214 +			// NOTE: Timer might actually be on the completed queue rather than the final queue
   1.215 +			//		 but the check is harmless in any case.
   1.216 +			TInt i=iTriggerTime & NTimerQ::ETimerQMask;
   1.217 +			NTimerQ::STimerQ& q=TheTimerQ.iTickQ[i];
   1.218 +			if (q.iIntQ.IsEmpty() && q.iDfcQ.IsEmpty())
   1.219 +				TheTimerQ.iPresent &= ~(1<<i);
   1.220 +			break;
   1.221 +			}
   1.222 +		case EIdle:			// nothing to do
   1.223 +			result = FALSE;
   1.224 +		case EHolding:		// just deque
   1.225 +		case EOrdered:		// just deque
   1.226 +			break;
   1.227 +		}
   1.228 +	iState=EIdle;
   1.229 +	NKern::RestoreInterrupts(irq);
   1.230 +	return result;
   1.231 +	}
   1.232 +#endif
   1.233 +
   1.234 +
   1.235 +/** Check if a nanokernel timer is pending or not
   1.236 +
   1.237 +	@return	TRUE if the timer is pending (OneShot() etc. would return KErrInUse)
   1.238 +	@return FALSE if the timer is idle (OneShot() etc. would succeed)
   1.239 +	@pre	Any context
   1.240 +
   1.241 +	@publishedPartner
   1.242 +	@prototype
   1.243 + */
   1.244 +EXPORT_C TBool NTimer::IsPending()
   1.245 +	{
   1.246 +	return iState != EIdle;
   1.247 +	}
   1.248 +
   1.249 +
   1.250 +/** Obtains the address of the nanokernel timer queue object.
   1.251 +
   1.252 +	Not intended for general use. Intended only for base ports in order to get
   1.253 +	the address used to call NTimerQ::Tick() with.
   1.254 +
   1.255 +	@return	The address of the nanokernel timer queue object
   1.256 +	@pre	Any context
   1.257 + */
   1.258 +EXPORT_C TAny* NTimerQ::TimerAddress()
   1.259 +	{
   1.260 +	return &TheTimerQ;
   1.261 +	}
   1.262 +
   1.263 +NTimerQ::NTimerQ()
   1.264 +	:	iDfc(NTimerQ::DfcFn,this,NULL,KTimerQDfcPriority)
   1.265 +	{
   1.266 +	// NOTE: All other members are initialised to zero since the single instance
   1.267 +	//		 of NTimerQ resides in .bss
   1.268 +	}
   1.269 +
   1.270 +void NTimerQ::Init1(TInt aTickPeriod)
   1.271 +	{
   1.272 +	TheTimerQ.iTickPeriod=aTickPeriod;
   1.273 +	__KTRACE_OPT(KBOOT,DEBUGPRINT("NTimerQ::Init1 - period %d us",aTickPeriod));
   1.274 +	__KTRACE_OPT(KMEMTRACE, DEBUGPRINT("MT:P %d",aTickPeriod));
   1.275 +	}
   1.276 +
   1.277 +void NTimerQ::Init3(TDfcQue* aDfcQ)
   1.278 +	{
   1.279 +	__KTRACE_OPT(KBOOT,DEBUGPRINT("NTimerQ::Init3 DFCQ at %08x",aDfcQ));
   1.280 +	TheTimerQ.iDfc.SetDfcQ(aDfcQ);
   1.281 +	}
   1.282 +
   1.283 +#ifndef __MSTIM_MACHINE_CODED__
   1.284 +void NTimerQ::Add(NTimer* aTimer)
   1.285 +//
   1.286 +//	Internal function to add a timer to the queue.
   1.287 +//	Enter and return with all interrupts disabled.
   1.288 +//
   1.289 +	{
   1.290 +	TInt t=TInt(aTimer->iTriggerTime-iMsCount);
   1.291 +	if (t<ENumTimerQueues)
   1.292 +		AddFinal(aTimer);
   1.293 +	else
   1.294 +		{
   1.295 +		// >=32ms to expiry, so put on holding queue
   1.296 +		aTimer->iState=NTimer::EHolding;
   1.297 +		iHoldingQ.Add(aTimer);
   1.298 +		}
   1.299 +	}
   1.300 +
   1.301 +void NTimerQ::AddFinal(NTimer* aTimer)
   1.302 +//
   1.303 +//	Internal function to add a timer to the corresponding final queue.
   1.304 +//	Enter and return with all interrupts disabled.
   1.305 +//
   1.306 +	{
   1.307 +	TInt i=aTimer->iTriggerTime & ETimerQMask;
   1.308 +	SDblQue* pQ;
   1.309 +	if (aTimer->iCompleteInDfc)
   1.310 +		pQ=&iTickQ[i].iDfcQ;
   1.311 +	else
   1.312 +		pQ=&iTickQ[i].iIntQ;
   1.313 +	iPresent |= (1<<i);
   1.314 +	aTimer->iState=NTimer::EFinal;
   1.315 +	pQ->Add(aTimer);
   1.316 +	}
   1.317 +
   1.318 +void NTimerQ::DfcFn(TAny* aPtr)
   1.319 +	{
   1.320 +	((NTimerQ*)aPtr)->Dfc();
   1.321 +	}
   1.322 +
   1.323 +void NTimerQ::Dfc()
   1.324 +//
   1.325 +// Do deferred timer queue processing and/or DFC completions
   1.326 +//
   1.327 +	{
   1.328 +	TInt irq;
   1.329 +
   1.330 +	// First transfer entries on the Ordered queue to the Final queues
   1.331 +	FOREVER
   1.332 +		{
   1.333 +		irq=NKern::DisableAllInterrupts();
   1.334 +		if (iOrderedQ.IsEmpty())
   1.335 +			break;
   1.336 +		NTimer* pC=(NTimer*)iOrderedQ.First();
   1.337 +		TInt remain=pC->iTriggerTime-iMsCount;
   1.338 +		if (remain>=ENumTimerQueues)
   1.339 +			break;
   1.340 +
   1.341 +		// If remaining time <32 ticks, add it to final queue;
   1.342 +		// also if remain < 0 we've 'missed it' so add to final queue.
   1.343 +		pC->Deque();
   1.344 +		AddFinal(pC);
   1.345 +		NKern::RestoreInterrupts(irq);
   1.346 +		__DEBUG_CALLBACK(0);
   1.347 +		}
   1.348 +	NKern::RestoreInterrupts(irq);
   1.349 +	__DEBUG_CALLBACK(1);
   1.350 +
   1.351 +	// Next transfer entries on the Holding queue to the Ordered queue or final queue
   1.352 +	FOREVER
   1.353 +		{
   1.354 +		irq=NKern::DisableAllInterrupts();
   1.355 +		if (iHoldingQ.IsEmpty())
   1.356 +			break;
   1.357 +		NTimer* pC=(NTimer*)iHoldingQ.First();
   1.358 +		pC->Deque();
   1.359 +		pC->iState=NTimer::ETransferring;
   1.360 +		iTransferringCancelled=FALSE;
   1.361 +		TUint32 trigger=pC->iTriggerTime;
   1.362 +		if (TInt(trigger-iMsCount)<ENumTimerQueues)
   1.363 +			{
   1.364 +			// <32ms remaining so put it on final queue
   1.365 +			AddFinal(pC);
   1.366 +			}
   1.367 +		else
   1.368 +			{
   1.369 +			FOREVER
   1.370 +				{
   1.371 +				NKern::RestoreInterrupts(irq);
   1.372 +				__DEBUG_CALLBACK(2);
   1.373 +
   1.374 +				// we now need to walk ordered queue to find correct position for pC
   1.375 +				SDblQueLink* anchor=&iOrderedQ.iA;
   1.376 +				iCriticalCancelled=FALSE;
   1.377 +				irq=NKern::DisableAllInterrupts();
   1.378 +				NTimer* pN=(NTimer*)iOrderedQ.First();
   1.379 +				while (pN!=anchor && !iTransferringCancelled)
   1.380 +					{
   1.381 +					if ((pN->iTriggerTime-trigger)<0x80000000u)
   1.382 +						break;	// insert before pN
   1.383 +					pN->iState=NTimer::ECritical;
   1.384 +					NKern::RestoreInterrupts(irq);
   1.385 +					__DEBUG_CALLBACK(3);
   1.386 +					irq=NKern::DisableAllInterrupts();
   1.387 +					if (iCriticalCancelled)
   1.388 +						break;
   1.389 +					pN->iState=NTimer::EOrdered;
   1.390 +					pN=(NTimer*)pN->iNext;
   1.391 +					}
   1.392 +
   1.393 +				if (iTransferringCancelled)
   1.394 +					break;		// this one has been cancelled, go on to next one
   1.395 +				if (!iCriticalCancelled)
   1.396 +					{
   1.397 +					pC->InsertBefore(pN);
   1.398 +					pC->iState=NTimer::EOrdered;
   1.399 +					break;		// done this one
   1.400 +					}
   1.401 +				}
   1.402 +			}
   1.403 +		NKern::RestoreInterrupts(irq);
   1.404 +		__DEBUG_CALLBACK(4);
   1.405 +		}
   1.406 +	NKern::RestoreInterrupts(irq);
   1.407 +	__DEBUG_CALLBACK(5);
   1.408 +
   1.409 +	// Finally do call backs for timers which requested DFC callback
   1.410 +	FOREVER
   1.411 +		{
   1.412 +		irq=NKern::DisableAllInterrupts();
   1.413 +		if (iCompletedQ.IsEmpty())
   1.414 +			break;
   1.415 +		NTimer* pC=(NTimer*)iCompletedQ.First();
   1.416 +		pC->Deque();
   1.417 +		pC->iState=NTimer::EIdle;
   1.418 +		TAny* p=pC->iPtr;
   1.419 +		NTimerFn f=pC->iFunction;
   1.420 +		NKern::RestoreInterrupts(irq);
   1.421 +		__DEBUG_CALLBACK(7);
   1.422 +		(*f)(p);
   1.423 +		}
   1.424 +	NKern::RestoreInterrupts(irq);
   1.425 +	}
   1.426 +
   1.427 +
   1.428 +/** Tick over the nanokernel timer queue.
   1.429 +	This function should be called by the base port in the system tick timer ISR.
   1.430 +	It should not be called at any other time.
   1.431 +	The value of 'this' to pass is the value returned by NTimerQ::TimerAddress().
   1.432 +
   1.433 +	@see NTimerQ::TimerAddress()
   1.434 + */
   1.435 +EXPORT_C void NTimerQ::Tick()
   1.436 +	{
   1.437 +#ifdef _DEBUG
   1.438 +	// If there are threads waiting to be released by the tick, enqueue the dfc
   1.439 +	if (!TheScheduler.iDelayedQ.IsEmpty())
   1.440 +		TheScheduler.iDelayDfc.Add();
   1.441 +#endif
   1.442 +	TheScheduler.TimesliceTick();
   1.443 +	TInt irq=NKern::DisableAllInterrupts();
   1.444 +	TInt i=iMsCount & ETimerQMask;
   1.445 +	iMsCount++;
   1.446 +	STimerQ* pQ=iTickQ+i;
   1.447 +	iPresent &= ~(1<<i);
   1.448 +	TBool doDfc=FALSE;
   1.449 +	if (!pQ->iDfcQ.IsEmpty())
   1.450 +		{
   1.451 +		// transfer DFC completions to completed queue and queue DFC
   1.452 +		iCompletedQ.MoveFrom(&pQ->iDfcQ);
   1.453 +		doDfc=TRUE;
   1.454 +		}
   1.455 +	if ((i&(ETimerQMask>>1))==0)
   1.456 +		{
   1.457 +		// Every 16 ticks we check if a DFC is required.
   1.458 +		// This allows a DFC latency of up to 16 ticks before timers are missed.
   1.459 +		if (!iHoldingQ.IsEmpty())
   1.460 +			doDfc=TRUE;				// if holding queue nonempty, queue DFC to sort
   1.461 +		else if (!iOrderedQ.IsEmpty())
   1.462 +			{
   1.463 +			// if first ordered queue entry expires in <32ms, queue the DFC to transfer
   1.464 +			NTimer* pC=(NTimer*)iOrderedQ.First();
   1.465 +#ifdef __EPOC32__
   1.466 +			__ASSERT_WITH_MESSAGE_DEBUG(iMsCount<=pC->iTriggerTime, "iMsCount has exceeded pC->iTriggerTime; function called later than expected ","NTimerQ::Tick()");
   1.467 +#endif
   1.468 +			if (TInt(pC->iTriggerTime-iMsCount)<ENumTimerQueues)
   1.469 +				doDfc=TRUE;
   1.470 +			}
   1.471 +		}
   1.472 +	if (!pQ->iIntQ.IsEmpty())
   1.473 +		{
   1.474 +		// transfer ISR completions to a temporary queue
   1.475 +		// careful here - higher priority interrupts could dequeue timers!
   1.476 +		SDblQue q(&pQ->iIntQ,0);
   1.477 +		while(!q.IsEmpty())
   1.478 +			{
   1.479 +			NTimer* pC=(NTimer*)q.First();
   1.480 +			pC->Deque();
   1.481 +			pC->iState=NTimer::EIdle;
   1.482 +			NKern::RestoreInterrupts(irq);
   1.483 +			if (pC->iFunction)
   1.484 +				(*pC->iFunction)(pC->iPtr);
   1.485 +			else
   1.486 +				((TDfc*)(pC->iPtr))->Add();
   1.487 +			irq=NKern::DisableAllInterrupts();
   1.488 +			}
   1.489 +		}
   1.490 +	NKern::RestoreInterrupts(irq);
   1.491 +	if (doDfc)
   1.492 +		iDfc.Add();
   1.493 +	}
   1.494 +
   1.495 +
   1.496 +/** Return the number of ticks before the next nanokernel timer expiry.
   1.497 +	May on occasion return a pessimistic estimate (i.e. too low).
   1.498 +	Used by base port to disable the system tick interrupt when the system
   1.499 +	is idle.
   1.500 +
   1.501 +	@return	The number of ticks before the next nanokernel timer expiry.
   1.502 +	
   1.503 +	@pre	Interrupts must be disabled.
   1.504 +	
   1.505 +	@post	Interrupts are disabled.
   1.506 + */
   1.507 +EXPORT_C TInt NTimerQ::IdleTime()
   1.508 +	{
   1.509 +	CHECK_PRECONDITIONS(MASK_INTERRUPTS_DISABLED,"NTimerQ::IdleTime");	
   1.510 +#ifdef _DEBUG
   1.511 +	// If there are threads waiting to be released by the tick we can't idle
   1.512 +	if (!TheScheduler.iDelayedQ.IsEmpty())
   1.513 +		return 1;
   1.514 +#endif
   1.515 +	NTimerQ& m=TheTimerQ;
   1.516 +	TUint32 next=m.iMsCount;	// number of next tick
   1.517 +	TUint32 p=m.iPresent;
   1.518 +	TInt r=KMaxTInt;
   1.519 +	if (p)
   1.520 +		{
   1.521 +		// Final queues nonempty
   1.522 +		TInt nx=next&0x1f;				// number of next tick modulo 32
   1.523 +		p=(p>>nx)|(p<<(32-nx));			// rotate p right by nx (so lsb corresponds to next tick)
   1.524 +		r=__e32_find_ls1_32(p);			// find number of zeros before LS 1
   1.525 +		}
   1.526 +	if (!m.iHoldingQ.IsEmpty())
   1.527 +		{
   1.528 +		// Sort operation required - need to process next tick divisible by 16
   1.529 +		TInt nx=next&0x0f;				// number of next tick modulo 16
   1.530 +		TInt r2=nx?(16-nx):0;			// number of ticks before next divisible by 16
   1.531 +		if (r2<r)
   1.532 +			r=r2;
   1.533 +		}
   1.534 +	if (!m.iOrderedQ.IsEmpty())
   1.535 +		{
   1.536 +		// Timers present on ordered queue
   1.537 +		NTimer* pC=(NTimer*)m.iOrderedQ.First();
   1.538 +		TUint32 tt=pC->iTriggerTime;
   1.539 +		tt=(tt&~0x0f)-16;				// time at which transfer to final queue would occur
   1.540 +		TInt r3=(TInt)(tt-next);
   1.541 +		if (r3<r)
   1.542 +			r=r3;
   1.543 +		}
   1.544 +	return r;
   1.545 +	}
   1.546 +#endif
   1.547 +
   1.548 +
   1.549 +/** Advance the nanokernel timer queue by the specified number of ticks.
   1.550 +	It is assumed that no timers expire as a result of this.
   1.551 +	Used by base port when system comes out of idle mode after disabling the
   1.552 +	system tick interrupt to bring the timer queue up to date.
   1.553 +
   1.554 +	@param	aTicks Number of ticks skipped due to tick suppression
   1.555 +
   1.556 +	@pre	Interrupts must be disabled.
   1.557 +
   1.558 +	@post	Interrupts are disabled.
   1.559 + */
   1.560 +EXPORT_C void NTimerQ::Advance(TInt aTicks)
   1.561 +	{
   1.562 +	CHECK_PRECONDITIONS(MASK_INTERRUPTS_DISABLED,"NTimerQ::Advance");	
   1.563 +	TheTimerQ.iMsCount+=(TUint32)aTicks;
   1.564 +	}
   1.565 +
   1.566 +
   1.567 +/**	Returns the period of the nanokernel timer.
   1.568 +	@return Period in microseconds
   1.569 +	@pre any context
   1.570 +	@see NTimer
   1.571 + */
   1.572 +EXPORT_C TInt NKern::TickPeriod()
   1.573 +	{
   1.574 +	return TheTimerQ.iTickPeriod;
   1.575 +	}
   1.576 +
   1.577 +
   1.578 +/**	Converts a time interval to timer ticks.
   1.579 +
   1.580 +	@param aMilliseconds time interval in milliseconds.
   1.581 +	@return Number of nanokernel timer ticks.  Non-integral results are rounded up.
   1.582 +
   1.583 + 	@pre aMilliseconds should be <=2147483 to avoid integer overflow.
   1.584 +	@pre any context
   1.585 + */
   1.586 +EXPORT_C TInt NKern::TimerTicks(TInt aMilliseconds)
   1.587 +	{
   1.588 +	__ASSERT_WITH_MESSAGE_DEBUG(aMilliseconds<=2147483,"aMilliseconds should be <=2147483","NKern::TimerTicks");
   1.589 +	TUint32 msp=TheTimerQ.iTickPeriod;
   1.590 +	if (msp==1000)	// will be true except on pathological hardware
   1.591 +		return aMilliseconds;
   1.592 +	TUint32 us=(TUint32)aMilliseconds*1000;
   1.593 +	return (us+msp-1)/msp;
   1.594 +	}
   1.595 +