emulator/emulatorbsp/specific/timer.cpp
changeset 0 cec860690d41
equal deleted inserted replaced
-1:000000000000 0:cec860690d41
       
     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 "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 // wins\specific\timer.cpp
       
    15 // 
       
    16 //
       
    17 
       
    18 #include "variant.h"
       
    19 
       
    20 #define KWinsTimerPanicCategory "WinsTimer"
       
    21 
       
    22 enum TWinsTimerPanic
       
    23 	{
       
    24 	EReleaseSemaphoreError = 1,
       
    25 	ETimeSetEventError = 2
       
    26 	};
       
    27 
       
    28 void PanicFromWinsTimer(TWinsTimerPanic aReason)
       
    29 	{
       
    30 	Kern::Fault(KWinsTimerPanicCategory, aReason);
       
    31 	}
       
    32 
       
    33 
       
    34 
       
    35 inline Int64 FileTime2Milliseconds(const FILETIME& aFileTime)
       
    36 	{return ((Int64(aFileTime.dwHighDateTime)<<32) + aFileTime.dwLowDateTime)/10000;}
       
    37 
       
    38 
       
    39 void CALLBACK WinsTimer::Tick(UINT, UINT, DWORD aPtr, DWORD, DWORD) 
       
    40 	{
       
    41 
       
    42 	WinsTimer& timer=*(WinsTimer*)aPtr;
       
    43 	if (!ReleaseSemaphore(timer.iSemaphore,1,NULL))
       
    44 		{
       
    45 		__ASSERT_ALWAYS((GetLastError() == ERROR_TOO_MANY_POSTS), PanicFromWinsTimer(EReleaseSemaphoreError));
       
    46 		timeKillEvent(timer.iTimer);
       
    47 		// If WinsTimer::EventThread() has run since the above call to
       
    48 		// ReleaseSemaphore() (this has been observed to happen while 
       
    49 		// within the call to timeKillEvent()) then the semaphore may be 
       
    50 		// signallable again. If it is, restart the timer otherwise wait 
       
    51 		// for the event thread to restart it.
       
    52 		if (ReleaseSemaphore(timer.iSemaphore,1,NULL))
       
    53 			timer.Enable();
       
    54 		else
       
    55 			timer.iSuspend = timer.iMaxLagTicks;
       
    56 		}
       
    57 	}
       
    58 
       
    59 DWORD WINAPI WinsTimer::Thread(LPVOID aPtr)
       
    60 	{
       
    61 	static_cast<WinsTimer*>(aPtr)->EventThread(*(NTimerQ*)NTimerQ::TimerAddress());
       
    62 	return 0;
       
    63 	}
       
    64 
       
    65 void WinsTimer::EventThread(NTimerQ& aTimerQ)
       
    66 	{
       
    67 	for (;;)
       
    68 		{
       
    69 		WaitForSingleObject(iSemaphore,INFINITE);
       
    70 		if (iSuspend > 0)
       
    71 			{
       
    72 			// Emulator interrupted/suspended for too long, MM-callback has been suspended
       
    73 			//
       
    74 			// Rather than try and catch up now, just discard the lost ticks - this improves
       
    75 			// system behaviour particularly when debugging at the expense of losing sync
       
    76 			// between the Windows clock and the emulator RTC.
       
    77 			//
       
    78 			while (--iSuspend > 0)
       
    79 				WaitForSingleObject(iSemaphore, 0);	// absorb all signals
       
    80 			//
       
    81 			// un-nobble once we are debugging
       
    82 			if (iNobbleNanos && IsDebuggerPresent())
       
    83 				iNobbleNanos = 0;
       
    84 			//
       
    85 			// now restart the timer callbacks
       
    86 			Enable();
       
    87 			//
       
    88 			// don't deliver a tick until next callback
       
    89 			continue;
       
    90 			}
       
    91 		if (iNobbleNanos && iIdleThread != NKern::CurrentThread())
       
    92 			Kern::NanoWait(iNobbleNanos);
       
    93 		StartOfInterrupt();
       
    94 		iTime += iPeriod;
       
    95 		if (!iStandby)
       
    96 			aTimerQ.Tick();
       
    97 		EndOfInterrupt();
       
    98 		}
       
    99 	}
       
   100 
       
   101 
       
   102 WinsTimer::WinsTimer()
       
   103 	:iPeriod(0),iNobbleNanos(0),iMaxLagTicks(0),iSemaphore(NULL),iSuspend(0),iTime(0),iIdleThread(0)
       
   104 	{}
       
   105 
       
   106 void WinsTimer::Init(TUint aPeriod)
       
   107 	{
       
   108 	// calculate the y2k offset in seconds from Win32 'zero' FILETIME
       
   109 	// This initially synchronizes EPOC time with Windows time
       
   110 	const SYSTEMTIME KSystemTimeY2K = {2000,1,0,1,0,0,0,0};
       
   111 	FILETIME y2k, now;
       
   112 	SystemTimeToFileTime(&KSystemTimeY2K,&y2k);
       
   113 	GetSystemTimeAsFileTime(&now);
       
   114 	iTime = FileTime2Milliseconds(now) - FileTime2Milliseconds(y2k);
       
   115 
       
   116 	TIMECAPS caps;
       
   117 	timeGetDevCaps(&caps,sizeof(caps));
       
   118 
       
   119 	iPeriod = min(caps.wPeriodMax, max(caps.wPeriodMin, aPeriod));
       
   120 	TUint resolution = max(caps.wPeriodMin, iPeriod/2);
       
   121 	iMaxLagTicks = EMaxLag / iPeriod;
       
   122 
       
   123 	// limit 'catch-up' for when Win32 gets too busy for us to fire timer events
       
   124 	// making this too large causes delays when resuming from debug
       
   125 	iSemaphore = CreateSemaphoreA(NULL, 0, iMaxLagTicks, NULL);
       
   126 
       
   127 	timeBeginPeriod(resolution);
       
   128 
       
   129 	CreateWin32Thread(EThreadEvent, &WinsTimer::Thread, this, ETrue);
       
   130 	}
       
   131 
       
   132 void WinsTimer::Enable()
       
   133 	{
       
   134 	iTimer = timeSetEvent(iPeriod,0,&WinsTimer::Tick,DWORD(this),TIME_PERIODIC);
       
   135 	__ASSERT_ALWAYS(iTimer != NULL, PanicFromWinsTimer(ETimeSetEventError));
       
   136 	}
       
   137 
       
   138 TInt WinsTimer::SystemTime() const
       
   139 //
       
   140 // Return the time in seconds since y2k
       
   141 //
       
   142 	{
       
   143 	TInt irq = NKern::DisableAllInterrupts();
       
   144 	Int64 time = iTime;
       
   145 	NKern::RestoreInterrupts(irq);
       
   146 	if (time < 0)
       
   147 		time -= 999;	// we want rounding to -infinity for the division
       
   148 	return TInt(time/1000);
       
   149 	}
       
   150 
       
   151 void WinsTimer::SetSystemTime(TInt aTime)
       
   152 //
       
   153 // Set the time in seconds since y2k
       
   154 //
       
   155 	{
       
   156 	Int64 time=aTime;
       
   157 	time*=1000;
       
   158 	TInt irq = NKern::DisableAllInterrupts();
       
   159 	iTime = time;
       
   160 	NKern::RestoreInterrupts(irq);
       
   161 	}
       
   162 
       
   163 void WinsTimer::Standby()
       
   164 	{
       
   165 	iStandby = ETrue;
       
   166 	}
       
   167 
       
   168 void WinsTimer::Wakeup()
       
   169 	{
       
   170 	iStandby = EFalse;
       
   171 	// Busy wait for the next timer interrupt 
       
   172 	volatile Int64* t = &iTime;
       
   173 	Int64 time = *t;
       
   174 	while (time == *t)
       
   175 		{}
       
   176 	}