kernel/eka/nkern/nk_timer.cpp
changeset 0 a41df078684a
child 36 538db54a451d
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 const TInt KTimerQDfcPriority=6;
       
    23 
       
    24 GLDEF_D NTimerQ TheTimerQ;
       
    25 
       
    26 #ifndef __MSTIM_MACHINE_CODED__
       
    27 #ifdef _DEBUG
       
    28 #define __DEBUG_CALLBACK(n)	{if (iDebugFn) (*iDebugFn)(iDebugPtr,n);}
       
    29 #else
       
    30 #define __DEBUG_CALLBACK(n)
       
    31 #endif
       
    32 
       
    33 
       
    34 /** Starts a nanokernel timer in one-shot mode with ISR callback.
       
    35 	
       
    36 	Queues the timer to expire in the specified number of nanokernel ticks. The
       
    37 	actual wait time will be at least that much and may be up to one tick more.
       
    38 	The expiry handler will be called in ISR context.
       
    39 	
       
    40 	Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
       
    41 
       
    42 	@param	aTime Timeout in nanokernel ticks
       
    43 	
       
    44 	@return	KErrNone if no error; KErrInUse if timer is already active.
       
    45 	
       
    46 	@pre	Any context
       
    47 	
       
    48 	@see    NKern::TimerTicks()
       
    49  */
       
    50 EXPORT_C TInt NTimer::OneShot(TInt aTime)
       
    51 	{
       
    52 	return OneShot(aTime,FALSE);
       
    53 	}
       
    54 
       
    55 
       
    56 /** Starts a nanokernel timer in one-shot mode with ISR or DFC callback.
       
    57 	
       
    58 	Queues the timer to expire in the specified number of nanokernel ticks. The
       
    59 	actual wait time will be at least that much and may be up to one tick more.
       
    60 	The expiry handler will be called in either ISR context or in the context
       
    61 	of the nanokernel timer thread (DfcThread1).
       
    62 
       
    63     Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
       
    64 
       
    65 	@param	aTime Timeout in nanokernel ticks
       
    66 	@param	aDfc TRUE if DFC callback required, FALSE if ISR callback required.
       
    67 	
       
    68 	@return	KErrNone if no error; KErrInUse if timer is already active.
       
    69 	
       
    70 	@pre	Any context
       
    71 	
       
    72 	@see    NKern::TimerTicks()
       
    73  */
       
    74 EXPORT_C TInt NTimer::OneShot(TInt aTime, TBool aDfc)
       
    75 	{
       
    76 	__NK_ASSERT_DEBUG(aTime>=0);
       
    77 
       
    78 	/** iFunction could be set to NULL after NTimer::OneShot(TInt, TDfc&) call.
       
    79 	Call-back mechanism cannot be changed in the life time of a timer. */
       
    80 	__NK_ASSERT_DEBUG(iFunction!=NULL); 
       
    81 
       
    82 	TInt irq=NKern::DisableAllInterrupts();
       
    83 	if (iState!=EIdle)
       
    84 		{
       
    85 		NKern::RestoreInterrupts(irq);
       
    86 		return KErrInUse;
       
    87 		}
       
    88 	iCompleteInDfc=TUint8(aDfc?1:0);
       
    89 	iTriggerTime=TheTimerQ.iMsCount+(TUint32)aTime;
       
    90 	TheTimerQ.Add(this);
       
    91 	NKern::RestoreInterrupts(irq);
       
    92 	return KErrNone;
       
    93 	}
       
    94 
       
    95 /** Starts a nanokernel timer in one-shot mode with callback in dfc thread that provided DFC belongs to.
       
    96 	
       
    97 	Queues the timer to expire in the specified number of nanokernel ticks. The
       
    98 	actual wait time will be at least that much and may be up to one tick more.
       
    99 	On expiry aDfc will be queued in ISR context.
       
   100 
       
   101     Note that NKern::TimerTicks() can be used to convert milliseconds to ticks.
       
   102 
       
   103 	@param	aTime Timeout in nanokernel ticks
       
   104 	@param	aDfc - Dfc to be queued when the timer expires.
       
   105 	
       
   106 	@return	KErrNone if no error; KErrInUse if timer is already active.
       
   107 	
       
   108 	@pre	Any context
       
   109 	
       
   110 	@see    NKern::TimerTicks()
       
   111  */
       
   112 EXPORT_C TInt NTimer::OneShot(TInt aTime, TDfc& aDfc)
       
   113 	{
       
   114 	__NK_ASSERT_DEBUG(aTime>=0);
       
   115 	TInt irq=NKern::DisableAllInterrupts();
       
   116 	if (iState!=EIdle)
       
   117 		{
       
   118 		NKern::RestoreInterrupts(irq);
       
   119 		return KErrInUse;
       
   120 		}
       
   121 	iCompleteInDfc = 0;
       
   122 	iFunction = NULL;
       
   123 	iPtr = (TAny*) &aDfc;
       
   124 	iTriggerTime=TheTimerQ.iMsCount+(TUint32)aTime;
       
   125 	TheTimerQ.Add(this);
       
   126 	NKern::RestoreInterrupts(irq);
       
   127 	return KErrNone;
       
   128 	}
       
   129 
       
   130 
       
   131 /** Starts a nanokernel timer in zero-drift periodic mode with ISR or DFC callback.
       
   132 
       
   133 	Queues the timer to expire in the specified number of nanokernel ticks,
       
   134 	measured from the time at which it last expired. This allows exact periodic
       
   135 	timers to be implemented with no drift caused by delays in requeueing the
       
   136 	timer.
       
   137 
       
   138 	The expiry handler will be called in the same context as the previous timer
       
   139 	expiry. Generally the way this is used is that NTimer::OneShot() is used to start 
       
   140 	the first time interval and this specifies whether the callback is in ISR context 
       
   141 	or in the context of the nanokernel timer thread (DfcThread1) or other Dfc thread.
       
   142 	The expiry handler then uses NTimer::Again() to requeue the timer.
       
   143 
       
   144 	@param	aTime Timeout in nanokernel ticks
       
   145 
       
   146 	@return	KErrNone if no error; KErrInUse if timer is already active;
       
   147 	        KErrArgument if the requested expiry time is in the past.
       
   148 	        
       
   149 	@pre	Any context
       
   150  */
       
   151 EXPORT_C TInt NTimer::Again(TInt aTime)
       
   152 //
       
   153 // Wait aTime from last trigger time - used for periodic timers
       
   154 //
       
   155 	{
       
   156 	__NK_ASSERT_DEBUG(aTime>0);
       
   157 	TInt irq=NKern::DisableAllInterrupts();
       
   158 	if (iState!=EIdle)
       
   159 		{
       
   160 		NKern::RestoreInterrupts(irq);
       
   161 		return KErrInUse;
       
   162 		}
       
   163 	TUint32 nextTick=TheTimerQ.iMsCount;
       
   164 	TUint32 trigger=iTriggerTime+(TUint32)aTime;
       
   165 	TUint32 d=trigger-nextTick;
       
   166 	if (d>=0x80000000)
       
   167 		{
       
   168 		NKern::RestoreInterrupts(irq);
       
   169 		return KErrArgument;		// requested time is in the past
       
   170 		}
       
   171 	iTriggerTime=trigger;
       
   172 	TheTimerQ.Add(this);
       
   173 	NKern::RestoreInterrupts(irq);
       
   174 	return KErrNone;
       
   175 	}
       
   176 
       
   177 
       
   178 /** Cancels a nanokernel timer.
       
   179 
       
   180 	Removes this timer from the nanokernel timer queue. Does nothing if the
       
   181 	timer is inactive or has already expired.
       
   182 	Note that if the timer was queued and DFC callback requested it is possible
       
   183 	for the expiry handler to run even after Cancel() has been called. This will
       
   184 	occur in the case where DfcThread1 is preempted just before calling the
       
   185 	expiry handler for this timer and the preempting thread/ISR/IDFC calls
       
   186 	Cancel() on the timer.
       
   187 
       
   188 	@pre	Any context
       
   189 	@return	TRUE if timer was actually cancelled
       
   190 	@return	FALSE if timer was not cancelled - this could be because it was not
       
   191 				active or because its expiry handler was already running on
       
   192 				another CPU or in the timer DFC.
       
   193  */
       
   194 EXPORT_C TBool NTimer::Cancel()
       
   195 	{
       
   196 	TBool result = TRUE;
       
   197 	TInt irq=NKern::DisableAllInterrupts();
       
   198 	if (iState>ETransferring)	// idle or transferring timers are not on a queue
       
   199 		Deque();
       
   200 	switch (iState)
       
   201 		{
       
   202 		case ETransferring:	// signal DFC to abort this iteration
       
   203 			TheTimerQ.iTransferringCancelled=TRUE;
       
   204 			break;
       
   205 		case ECritical:		// signal DFC to abort this iteration
       
   206 			TheTimerQ.iCriticalCancelled=TRUE;
       
   207 			break;
       
   208 		case EFinal:
       
   209 			{
       
   210 			// Need to clear bit in iPresent if both final queues now empty
       
   211 			// NOTE: Timer might actually be on the completed queue rather than the final queue
       
   212 			//		 but the check is harmless in any case.
       
   213 			TInt i=iTriggerTime & NTimerQ::ETimerQMask;
       
   214 			NTimerQ::STimerQ& q=TheTimerQ.iTickQ[i];
       
   215 			if (q.iIntQ.IsEmpty() && q.iDfcQ.IsEmpty())
       
   216 				TheTimerQ.iPresent &= ~(1<<i);
       
   217 			break;
       
   218 			}
       
   219 		case EIdle:			// nothing to do
       
   220 			result = FALSE;
       
   221 		case EHolding:		// just deque
       
   222 		case EOrdered:		// just deque
       
   223 			break;
       
   224 		}
       
   225 	iState=EIdle;
       
   226 	NKern::RestoreInterrupts(irq);
       
   227 	return result;
       
   228 	}
       
   229 #endif
       
   230 
       
   231 
       
   232 /** Check if a nanokernel timer is pending or not
       
   233 
       
   234 	@return	TRUE if the timer is pending (OneShot() etc. would return KErrInUse)
       
   235 	@return FALSE if the timer is idle (OneShot() etc. would succeed)
       
   236 	@pre	Any context
       
   237 
       
   238 	@publishedPartner
       
   239 	@prototype
       
   240  */
       
   241 EXPORT_C TBool NTimer::IsPending()
       
   242 	{
       
   243 	return iState != EIdle;
       
   244 	}
       
   245 
       
   246 
       
   247 /** Obtains the address of the nanokernel timer queue object.
       
   248 
       
   249 	Not intended for general use. Intended only for base ports in order to get
       
   250 	the address used to call NTimerQ::Tick() with.
       
   251 
       
   252 	@return	The address of the nanokernel timer queue object
       
   253 	@pre	Any context
       
   254  */
       
   255 EXPORT_C TAny* NTimerQ::TimerAddress()
       
   256 	{
       
   257 	return &TheTimerQ;
       
   258 	}
       
   259 
       
   260 NTimerQ::NTimerQ()
       
   261 	:	iDfc(NTimerQ::DfcFn,this,NULL,KTimerQDfcPriority)
       
   262 	{
       
   263 	// NOTE: All other members are initialised to zero since the single instance
       
   264 	//		 of NTimerQ resides in .bss
       
   265 	}
       
   266 
       
   267 void NTimerQ::Init1(TInt aTickPeriod)
       
   268 	{
       
   269 	TheTimerQ.iTickPeriod=aTickPeriod;
       
   270 	__KTRACE_OPT(KBOOT,DEBUGPRINT("NTimerQ::Init1 - period %d us",aTickPeriod));
       
   271 	__KTRACE_OPT(KMEMTRACE, DEBUGPRINT("MT:P %d",aTickPeriod));
       
   272 	}
       
   273 
       
   274 void NTimerQ::Init3(TDfcQue* aDfcQ)
       
   275 	{
       
   276 	__KTRACE_OPT(KBOOT,DEBUGPRINT("NTimerQ::Init3 DFCQ at %08x",aDfcQ));
       
   277 	TheTimerQ.iDfc.SetDfcQ(aDfcQ);
       
   278 	}
       
   279 
       
   280 #ifndef __MSTIM_MACHINE_CODED__
       
   281 void NTimerQ::Add(NTimer* aTimer)
       
   282 //
       
   283 //	Internal function to add a timer to the queue.
       
   284 //	Enter and return with all interrupts disabled.
       
   285 //
       
   286 	{
       
   287 	TInt t=TInt(aTimer->iTriggerTime-iMsCount);
       
   288 	if (t<ENumTimerQueues)
       
   289 		AddFinal(aTimer);
       
   290 	else
       
   291 		{
       
   292 		// >=32ms to expiry, so put on holding queue
       
   293 		aTimer->iState=NTimer::EHolding;
       
   294 		iHoldingQ.Add(aTimer);
       
   295 		}
       
   296 	}
       
   297 
       
   298 void NTimerQ::AddFinal(NTimer* aTimer)
       
   299 //
       
   300 //	Internal function to add a timer to the corresponding final queue.
       
   301 //	Enter and return with all interrupts disabled.
       
   302 //
       
   303 	{
       
   304 	TInt i=aTimer->iTriggerTime & ETimerQMask;
       
   305 	SDblQue* pQ;
       
   306 	if (aTimer->iCompleteInDfc)
       
   307 		pQ=&iTickQ[i].iDfcQ;
       
   308 	else
       
   309 		pQ=&iTickQ[i].iIntQ;
       
   310 	iPresent |= (1<<i);
       
   311 	aTimer->iState=NTimer::EFinal;
       
   312 	pQ->Add(aTimer);
       
   313 	}
       
   314 
       
   315 void NTimerQ::DfcFn(TAny* aPtr)
       
   316 	{
       
   317 	((NTimerQ*)aPtr)->Dfc();
       
   318 	}
       
   319 
       
   320 void NTimerQ::Dfc()
       
   321 //
       
   322 // Do deferred timer queue processing and/or DFC completions
       
   323 //
       
   324 	{
       
   325 	TInt irq;
       
   326 
       
   327 	// First transfer entries on the Ordered queue to the Final queues
       
   328 	FOREVER
       
   329 		{
       
   330 		irq=NKern::DisableAllInterrupts();
       
   331 		if (iOrderedQ.IsEmpty())
       
   332 			break;
       
   333 		NTimer* pC=(NTimer*)iOrderedQ.First();
       
   334 		TInt remain=pC->iTriggerTime-iMsCount;
       
   335 		if (remain>=ENumTimerQueues)
       
   336 			break;
       
   337 
       
   338 		// If remaining time <32 ticks, add it to final queue;
       
   339 		// also if remain < 0 we've 'missed it' so add to final queue.
       
   340 		pC->Deque();
       
   341 		AddFinal(pC);
       
   342 		NKern::RestoreInterrupts(irq);
       
   343 		__DEBUG_CALLBACK(0);
       
   344 		}
       
   345 	NKern::RestoreInterrupts(irq);
       
   346 	__DEBUG_CALLBACK(1);
       
   347 
       
   348 	// Next transfer entries on the Holding queue to the Ordered queue or final queue
       
   349 	FOREVER
       
   350 		{
       
   351 		irq=NKern::DisableAllInterrupts();
       
   352 		if (iHoldingQ.IsEmpty())
       
   353 			break;
       
   354 		NTimer* pC=(NTimer*)iHoldingQ.First();
       
   355 		pC->Deque();
       
   356 		pC->iState=NTimer::ETransferring;
       
   357 		iTransferringCancelled=FALSE;
       
   358 		TUint32 trigger=pC->iTriggerTime;
       
   359 		if (TInt(trigger-iMsCount)<ENumTimerQueues)
       
   360 			{
       
   361 			// <32ms remaining so put it on final queue
       
   362 			AddFinal(pC);
       
   363 			}
       
   364 		else
       
   365 			{
       
   366 			FOREVER
       
   367 				{
       
   368 				NKern::RestoreInterrupts(irq);
       
   369 				__DEBUG_CALLBACK(2);
       
   370 
       
   371 				// we now need to walk ordered queue to find correct position for pC
       
   372 				SDblQueLink* anchor=&iOrderedQ.iA;
       
   373 				iCriticalCancelled=FALSE;
       
   374 				irq=NKern::DisableAllInterrupts();
       
   375 				NTimer* pN=(NTimer*)iOrderedQ.First();
       
   376 				while (pN!=anchor && !iTransferringCancelled)
       
   377 					{
       
   378 					if ((pN->iTriggerTime-trigger)<0x80000000u)
       
   379 						break;	// insert before pN
       
   380 					pN->iState=NTimer::ECritical;
       
   381 					NKern::RestoreInterrupts(irq);
       
   382 					__DEBUG_CALLBACK(3);
       
   383 					irq=NKern::DisableAllInterrupts();
       
   384 					if (iCriticalCancelled)
       
   385 						break;
       
   386 					pN->iState=NTimer::EOrdered;
       
   387 					pN=(NTimer*)pN->iNext;
       
   388 					}
       
   389 
       
   390 				if (iTransferringCancelled)
       
   391 					break;		// this one has been cancelled, go on to next one
       
   392 				if (!iCriticalCancelled)
       
   393 					{
       
   394 					pC->InsertBefore(pN);
       
   395 					pC->iState=NTimer::EOrdered;
       
   396 					break;		// done this one
       
   397 					}
       
   398 				}
       
   399 			}
       
   400 		NKern::RestoreInterrupts(irq);
       
   401 		__DEBUG_CALLBACK(4);
       
   402 		}
       
   403 	NKern::RestoreInterrupts(irq);
       
   404 	__DEBUG_CALLBACK(5);
       
   405 
       
   406 	// Finally do call backs for timers which requested DFC callback
       
   407 	FOREVER
       
   408 		{
       
   409 		irq=NKern::DisableAllInterrupts();
       
   410 		if (iCompletedQ.IsEmpty())
       
   411 			break;
       
   412 		NTimer* pC=(NTimer*)iCompletedQ.First();
       
   413 		pC->Deque();
       
   414 		pC->iState=NTimer::EIdle;
       
   415 		TAny* p=pC->iPtr;
       
   416 		NTimerFn f=pC->iFunction;
       
   417 		NKern::RestoreInterrupts(irq);
       
   418 		__DEBUG_CALLBACK(7);
       
   419 		(*f)(p);
       
   420 		}
       
   421 	NKern::RestoreInterrupts(irq);
       
   422 	}
       
   423 
       
   424 
       
   425 /** Tick over the nanokernel timer queue.
       
   426 	This function should be called by the base port in the system tick timer ISR.
       
   427 	It should not be called at any other time.
       
   428 	The value of 'this' to pass is the value returned by NTimerQ::TimerAddress().
       
   429 
       
   430 	@see NTimerQ::TimerAddress()
       
   431  */
       
   432 EXPORT_C void NTimerQ::Tick()
       
   433 	{
       
   434 #ifdef _DEBUG
       
   435 	// If there are threads waiting to be released by the tick, enqueue the dfc
       
   436 	if (!TheScheduler.iDelayedQ.IsEmpty())
       
   437 		TheScheduler.iDelayDfc.Add();
       
   438 #endif
       
   439 	TheScheduler.TimesliceTick();
       
   440 	TInt irq=NKern::DisableAllInterrupts();
       
   441 	TInt i=iMsCount & ETimerQMask;
       
   442 	iMsCount++;
       
   443 	STimerQ* pQ=iTickQ+i;
       
   444 	iPresent &= ~(1<<i);
       
   445 	TBool doDfc=FALSE;
       
   446 	if (!pQ->iDfcQ.IsEmpty())
       
   447 		{
       
   448 		// transfer DFC completions to completed queue and queue DFC
       
   449 		iCompletedQ.MoveFrom(&pQ->iDfcQ);
       
   450 		doDfc=TRUE;
       
   451 		}
       
   452 	if ((i&(ETimerQMask>>1))==0)
       
   453 		{
       
   454 		// Every 16 ticks we check if a DFC is required.
       
   455 		// This allows a DFC latency of up to 16 ticks before timers are missed.
       
   456 		if (!iHoldingQ.IsEmpty())
       
   457 			doDfc=TRUE;				// if holding queue nonempty, queue DFC to sort
       
   458 		else if (!iOrderedQ.IsEmpty())
       
   459 			{
       
   460 			// if first ordered queue entry expires in <32ms, queue the DFC to transfer
       
   461 			NTimer* pC=(NTimer*)iOrderedQ.First();
       
   462 #ifdef __EPOC32__
       
   463 			__ASSERT_WITH_MESSAGE_DEBUG(iMsCount<=pC->iTriggerTime, "iMsCount has exceeded pC->iTriggerTime; function called later than expected ","NKTimer::Tick()");
       
   464 #endif
       
   465 			if (TInt(pC->iTriggerTime-iMsCount)<ENumTimerQueues)
       
   466 				doDfc=TRUE;
       
   467 			}
       
   468 		}
       
   469 	if (!pQ->iIntQ.IsEmpty())
       
   470 		{
       
   471 		// transfer ISR completions to a temporary queue
       
   472 		// careful here - higher priority interrupts could dequeue timers!
       
   473 		SDblQue q(&pQ->iIntQ,0);
       
   474 		while(!q.IsEmpty())
       
   475 			{
       
   476 			NTimer* pC=(NTimer*)q.First();
       
   477 			pC->Deque();
       
   478 			pC->iState=NTimer::EIdle;
       
   479 			NKern::RestoreInterrupts(irq);
       
   480 			if (pC->iFunction)
       
   481 				(*pC->iFunction)(pC->iPtr);
       
   482 			else
       
   483 				((TDfc*)(pC->iPtr))->Add();
       
   484 			irq=NKern::DisableAllInterrupts();
       
   485 			}
       
   486 		}
       
   487 	NKern::RestoreInterrupts(irq);
       
   488 	if (doDfc)
       
   489 		iDfc.Add();
       
   490 	}
       
   491 
       
   492 
       
   493 /** Return the number of ticks before the next nanokernel timer expiry.
       
   494 	May on occasion return a pessimistic estimate (i.e. too low).
       
   495 	Used by base port to disable the system tick interrupt when the system
       
   496 	is idle.
       
   497 
       
   498 	@return	The number of ticks before the next nanokernel timer expiry.
       
   499 	
       
   500 	@pre	Interrupts must be disabled.
       
   501 	
       
   502 	@post	Interrupts are disabled.
       
   503  */
       
   504 EXPORT_C TInt NTimerQ::IdleTime()
       
   505 	{
       
   506 	CHECK_PRECONDITIONS(MASK_INTERRUPTS_DISABLED,"NTimerQ::IdleTime");	
       
   507 #ifdef _DEBUG
       
   508 	// If there are threads waiting to be released by the tick we can't idle
       
   509 	if (!TheScheduler.iDelayedQ.IsEmpty())
       
   510 		return 1;
       
   511 #endif
       
   512 	NTimerQ& m=TheTimerQ;
       
   513 	TUint32 next=m.iMsCount;	// number of next tick
       
   514 	TUint32 p=m.iPresent;
       
   515 	TInt r=KMaxTInt;
       
   516 	if (p)
       
   517 		{
       
   518 		// Final queues nonempty
       
   519 		TInt nx=next&0x1f;				// number of next tick modulo 32
       
   520 		p=(p>>nx)|(p<<(32-nx));			// rotate p right by nx (so lsb corresponds to next tick)
       
   521 		r=__e32_find_ls1_32(p);			// find number of zeros before LS 1
       
   522 		}
       
   523 	if (!m.iHoldingQ.IsEmpty())
       
   524 		{
       
   525 		// Sort operation required - need to process next tick divisible by 16
       
   526 		TInt nx=next&0x0f;				// number of next tick modulo 16
       
   527 		TInt r2=nx?(16-nx):0;			// number of ticks before next divisible by 16
       
   528 		if (r2<r)
       
   529 			r=r2;
       
   530 		}
       
   531 	if (!m.iOrderedQ.IsEmpty())
       
   532 		{
       
   533 		// Timers present on ordered queue
       
   534 		NTimer* pC=(NTimer*)m.iOrderedQ.First();
       
   535 		TUint32 tt=pC->iTriggerTime;
       
   536 		tt=(tt&~0x0f)-16;				// time at which transfer to final queue would occur
       
   537 		TInt r3=(TInt)(tt-next);
       
   538 		if (r3<r)
       
   539 			r=r3;
       
   540 		}
       
   541 	return r;
       
   542 	}
       
   543 #endif
       
   544 
       
   545 
       
   546 /** Advance the nanokernel timer queue by the specified number of ticks.
       
   547 	It is assumed that no timers expire as a result of this.
       
   548 	Used by base port when system comes out of idle mode after disabling the
       
   549 	system tick interrupt to bring the timer queue up to date.
       
   550 
       
   551 	@param	aTicks Number of ticks skipped due to tick suppression
       
   552 
       
   553 	@pre	Interrupts must be disabled.
       
   554 
       
   555 	@post	Interrupts are disabled.
       
   556  */
       
   557 EXPORT_C void NTimerQ::Advance(TInt aTicks)
       
   558 	{
       
   559 	CHECK_PRECONDITIONS(MASK_INTERRUPTS_DISABLED,"NTimerQ::Advance");	
       
   560 	TheTimerQ.iMsCount+=(TUint32)aTicks;
       
   561 	}
       
   562 
       
   563 
       
   564 /**	Returns the period of the nanokernel timer.
       
   565 	@return Period in microseconds
       
   566 	@pre any context
       
   567 	@see NTimer
       
   568  */
       
   569 EXPORT_C TInt NKern::TickPeriod()
       
   570 	{
       
   571 	return TheTimerQ.iTickPeriod;
       
   572 	}
       
   573 
       
   574 
       
   575 /**	Converts a time interval to timer ticks.
       
   576 
       
   577 	@param aMilliseconds time interval in milliseconds.
       
   578 	@return Number of nanokernel timer ticks.  Non-integral results are rounded up.
       
   579 
       
   580  	@pre aMilliseconds should be <=2147483 to avoid integer overflow.
       
   581 	@pre any context
       
   582  */
       
   583 EXPORT_C TInt NKern::TimerTicks(TInt aMilliseconds)
       
   584 	{
       
   585 	__ASSERT_WITH_MESSAGE_DEBUG(aMilliseconds<=2147483,"aMilliseconds should be <=2147483","NKern::TimerTicks");
       
   586 	TUint32 msp=TheTimerQ.iTickPeriod;
       
   587 	if (msp==1000)	// will be true except on pathological hardware
       
   588 		return aMilliseconds;
       
   589 	TUint32 us=(TUint32)aMilliseconds*1000;
       
   590 	return (us+msp-1)/msp;
       
   591 	}
       
   592