Author Topic: timeSetEvent and Sleep mode  (Read 2254 times)

0 Members and 1 Guest are viewing this topic.

Offline David Roberts

  • Newbie
  • *
  • Posts: 29
  • User-Rate: +0/-0
timeSetEvent and Sleep mode
« on: September 28, 2016, 11:07:35 AM »
A little while ago my power supply unit failed and sent a spike to my internal HDD and fried it. It seems that the HDD absorbed the lot sparing my internal SSD and motherboard. Not the end of the world and I only cried for a few days. <Ha>
 
My PC chap said cold booting was the worst trauma for a power supply unit so I decided to put my system to sleep instead of powering down. The fans still needed to be turned on and that must be a strain but I figured that on balance sleep was the way to go.
 
I have an app which gets the time from the internet every three hours, the system clock drifting about 250/300ms during that interval. I am going to call this three hours the major event delay. The corrections are logged and every now and again I check them. I noticed some large drifting, here and there, up to 800/900ms. I added a time stamp to the corrections and realised that Sleep mode really did mean Sleep mode. The app was frozen in it's track and timeSetEvent was not firing.
 
If I got a time update and went to bed one hour later manually putting the system to sleep and woke the machine up 7 hours, say, later then an update would not be requested for a further two hours giving a total of 10 hours between updates. That is where the 800/900ms was coming from.
 
What I needed then was an time update request on a system wake up after a long sleep on a periodic task. I did some reading and found no answer. It then occured to me that whilst almost everything was 'out for the count' there was one thing that did not sleep - the very thing that I was correcting - the system clock.
 
I was using timeSetEvent in one of the classical ways. The event delay was set at 60000ms, one minute, and the callback function incremented a counter which when it hit 180 a time update request was made. Therein lies the problem, during sleep mode incrementing ceases. I am going to call this event delay as the minor event delay.
 
I then thought if I made a note of the current time and added the schedule time to it then I would then have, obviously, a future time or target time. If the callback function got the current time and it exceeded the target time then a time update request could be made. During sleep mode the system clock keeps ticking.
 
That is the hypothesis, we now need to observe.
 
My watch gets a signal from a radio transmitter during the night and uses a quartz crystal during the day. I can force an update at anytime so did just that. I used the Windows facility to update the PC's clock from a local stratum 2 server.
 
I ran the following code and put the system to sleep after 38 seconds so the estimate of system wake up to 'Time up' is 22 seconds. I used my watch at the excutuion start and at the 38 second mark. I was banking on the delay from visual to clicking being closely matched for both clicks.

Code: [Select]
#Compile Exe
#Dim All
#Include "Win32API.inc"

Global qTargetDelay As Quad

Function PBMain() As Long
Local dwTimerHandle As Dword
Local hEvent As Dword

  GetSystemTimeAsFileTime( ByVal VarPtr(qTargetDelay) )
  qTargetDelay = qTargetDelay\10000000 + 60 * 3 ' Three minutes into the future

  hEvent = CreateEvent(ByVal 0,ByVal 1, ByVal 0, ByVal 0)
  dwTimerHandle = timeSetEvent( ByVal 30000, ByVal 0, CodePtr(DoISignal), ByVal hEvent, ByVal %TIME_PERIODIC)
  WaitForSingleObject(hEvent, ByVal %infinite)
  MsgBox "Time Up: " + Time$

  timeKillEvent dwTimerHandle
  CloseHandle hEvent

End Function

Sub DoISignal( ByVal uID As Dword, ByVal uMsg As Dword, _
ByVal dwUser As Dword, ByVal dw1 As Dword, ByVal dw2 As Dword)
Local qFileTimeNow As Quad

  GetSystemTimeAsFileTime( ByVal VarPtr(qFileTimeNow) )
  If qFileTimeNow\10000000 >= qTargetDelay Then SetEvent(dwUser)

End Sub

This is what I got:
 
Wake up time from Event viewer: 19:42:59
'Time up' time from MsgBox:     19:43:19
 
giving 20 seconds.
 
I ran the code again and put the system to sleep after 52 seconds so the estimate of system wake up to 'Time up' is 8 seconds.
 
Wake up time from Event viewer: 19:51:35
'Time up' time from MsgBox:     19:51:41
 
giving 6 seconds.
 
I ran the code again and put the system to sleep after 45 seconds so the estimate of system wake up to 'Time up' is 15 seconds.
 
Wake up time from Event viewer: 20:00:24
'Time up' time from MsgBox:     20:00:37
 
giving 13 seconds.
 
My estinmate was then +2 seconds for all three experiments. There are, obviously, factors which have not been taken into account but whatever they are they seem to be consistent.
 
I am taking the above as proof of concept.
 
We have two possible sleep scenarios: 'Wake time + Sleep time < Target time' and 'Wake time + Sleep time >= Target time'. In the first case nothing happens and the fact that our machine has been sleeping is irrelevant. In the second case our task will be undertaken on the system wake up and the time to start the task will vary between immediately and the minor event delay.
 
I am now using this in my application:
 
Code: [Select]
GetSystemTimeAsFileTime( ByVal Varptr( qTargetTime ) )
qTargetTime = qTargetTime\10000000 + 60 * ScheduleTime 'Seconds
hEvent = CreateEvent( BYVAL 0,BYVAL 1, BYVAL 0, BYVAL 0 )
ScheduleHandle = timeSetEvent ( BYVAL 60000, BYVAL 1000, CodePTR(DoISignal), BYVAL hEvent, BYVAL %TIME_PERIODIC )
WaitForSingleObject( hEvent, BYVAL %infinite )
CloseHandle hEvent
TimeKillEvent ScheduleHandle

where ScheduleTime is 180; got from a commandline.ini.
 
I don't need a zero resolution in timeSetEvent and have used 1000ms; just to take the pressure off.
 
It is working. With the old code it was possible to wait for three hours before a time update after a long sleep, now I get a time update within a minute following a long sleep. Of course, the system clock may be drifting all over the place during a long sleep but that does not concern me - I am only interested in a fairly accurate time when the system is awake. Effectively what I am getting after a long sleep is pretty much what I get after a Restart or Cold Boot.
 
Of course, this method is not restricted to an application which periodically gets the time from a time server.
« Last Edit: October 14, 2016, 05:59:35 PM by José Roca »