c00000fd
c00000fd

Reputation: 22273

Is there a delay timer for when system is operational?

For implementation of my Windows service, I need to be able to set a timer for a certain duration of system being in the working state. So I originally came up with the following code.

A) Setting up a waitable timer (error checks are omitted):

HANDLE hWTimer = ::CreateWaitableTimer(NULL, FALSE, NULL);

//As an example, set timer to wait for 40 minutes
int nWaitMins = 40;

LARGE_INTEGER li;
ULONGLONG uiWaitMs = (ULONGLONG)nWaitMins * 60LL * 1000LL;
li.QuadPart = -10000LL * uiWaitMs;  //Convert to 100 nanosecond intervals (must be negative for relative time)
::SetWaitableTimer(hWTimer, &li, 0, NULL, NULL, FALSE);     //Don't wake the system

B) Waiting for it (from a worker thread):

//Wait for timer to fire
::WaitForSingleObject(hWTimer, INFINITE);

This works really well with one caveat (if the system is not put into sleep mode or hibernated.) In that case what happens is best illustrated in this diagram:

enter image description here

when what I need it to do is this:

enter image description here

Is there a timer to do what I want here?

PS. Copies of my service submit reports to our web server from multiple workstations. With my technique above I try to randomize submission times to alleviate server workload. Since all workstations are put into sleep and then woken up roughly at the same time, my "randomization" technique doesn't work when machines wake up from sleep.

PS2. I need to point out that I need this to work under Windows XP.

Upvotes: 1

Views: 530

Answers (3)

selbie
selbie

Reputation: 104559

Updated.

If you want to wait based on a wall clock time (actual real time independent of system sleep/hibernate), you want to be using Win32 threadpool functions such as CreateThreadPoolTimer or CreateThreadpoolWait.

If you want to wait based on system time (time the computer was actually awake and not sleeping), then any of the Win32 wait functions will likely suffice. This includes WaitForSingleObject and Sleep.

Also, for what it's worth, the C++ condition_variable in Win32 has a method called wait_until, but it's actually implemented with system time.

Finally, many APIs have the possibility of "spurious wake up" or waking up too early. It's up to your code to decide if the conditions have been met. If you need to be precise, the best thing to do is keep track of time before and after your timer callback completed. Then measure the difference. For system time, use GetTickCount64 or GetTickCount. For wall clock time, use (ironically) the API called GetSystemTime. If you detect that you woke up too early, simply schedule the timer to wait the needed length of time again.

Upvotes: 0

Tim3880
Tim3880

Reputation: 2583

If you just want all your workstations send some report every 40 minutes (i can be wrong here), you don't need a Windows service which is difficult to program and debug. A simple scheduled task will do just fine.

To avoid all workstations sending reports in the same time, you just set a random waiting period in your report program, wait 0-20 minutes randomly before send the report. So even you schedule all jobs on each workstations in the same schedule,the reports will not be sent together.

Scheduled tasks can also wake your workstations to send the reports.

Upvotes: 0

zmbq
zmbq

Reputation: 39023

You need to be notified when the system is entering sleep mode, start here .

When you receive the WM_POWERBROADCAST with PBT_ABTSUSPEND, stop your timer and keep track of how much time left you have. When the PBT_ABTRESUMESUSPEND event is received, start the timer again with the time left.

Upvotes: 2

Related Questions