1. Links
Timers Tutorial
http://www.codeproject.com/Articles/1236/Timers-Tutorial
Queue Timers
The last kind of Windows - supported timers that we are going to read about in this article is queue timers. They were introduced with Windows 2000.Queue timers are lightweight kernel objects that reside in timer queues. Like most timers, they enable us to specify the callback function to be called when the specified due time arrives. In this case, the operation is performed by a thread in the Windows thread pool.
Here, for the sake of simplicity, we are not going to create our timer queues. Instead, we will put our queue timers into default timer queue, provided by the OS.
First, we need to create a timer and add it to the default timer queue. For this, we'll make a call to:
BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue ,
WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime,
DWORD Period, ULONG Flags);
Arguments
phNewTimer- Pointer to a handle; this is an out valueTimerQueue- Timer queue handle. For the default timer queue,NULLCallback- Pointer to the callback functionParameter- Value passed to the callback functionDueTime- Time (milliseconds), before the timer is set to the signaled state for the first timePeriod- Timer period (milliseconds). If zero, timer is signaled only onceFlags- One or more of the next values (table taken from MSDN):
WT_EXECUTEINTIMERTHREAD |
The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations. |
WT_EXECUTEINIOTHREAD |
The callback function is queued to an I/O worker thread. This flag should be used if the function should be executed in a thread that waits in an alertable state. The callback function is queued as an APC. Be sure to address reentrancy issues if the function performs an alertable wait operation. |
WT_EXECUTEINPERSISTENTTHREAD |
The callback function is queued to a thread that never terminates. This flag should be used only for short tasks or it could affect other timer operations. Note that currently no worker thread is persistent, although no worker thread will terminate if there are any pending I/O requests. |
WT_EXECUTELONGFUNCTION |
Specifies that the callback function can perform a long wait. This flag helps the system to decide if it should create a new thread. |
WT_EXECUTEONLYONCE |
The timer will be set to the signaled state only once. |
Return Value
- Nonzero if the function succeeds
VOID CALLBACK WaitOrTimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired);
Arguments
lpParameter- Pointer to user-defined dataTimerOrWaitFired- alwaysTRUEfor timer callbacks
BOOL DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent);
Arguments
TimerQueue- A handle to the (default) timer queueTimer- A handle to the timerCompletionEvent- A handle to an optional event to be signaled when the function is successful and all callback functions have completed. Can beNULL.
Return Value
- Nonzero if the function succeeds
void CTimersDlg::OnButtonBegin()
{
.
.
.
// create the timer
BOOL success = ::CreateTimerQueueTimer(
&m_timerHandle,
NULL,
TimerProc,
this,
0,
elTime,
WT_EXECUTEINTIMERTHREAD);
}
void CTimersDlg::OnButtonStop()
{
// destroy the timer
DeleteTimerQueueTimer(NULL, m_timerHandle, NULL);
CloseHandle (m_timerHandle);
}
void CTimersDlg::QueueTimerHandler() // called every elTime milliseconds
{
// do what you want to do, but quickly
.
.
.
}
void CALLBACK TimerProc(void* lpParametar,
BOOLEAN TimerOrWaitFired)
{
// This is used only to call QueueTimerHandler
// Typically, this function is static member of CTimersDlg
CTimersDlg* obj = (CTimersDlg*) lpParametar;
obj->QueueTimerHandler();
}
As you can see, queue timers are pretty easy to use. I can also add that they are very accurate, and "resource friendly". As I noted at the beginning of this chapter, queue timers are supported on Windows 2000 and later. If you do not want to support older Windows versions, they are perfect, and should be used instead of multimedia timers.
Conclusion
What's the moral of the whole story?When you decide that you need a timer in your application, choosing between the different timer variants should not be that hard. Follow these simple rules:
- If you want your application to work on every 32 bit Windows platform, you do not need high precision, and the callback operation is fast enough not to disrupt the UI responsiveness, use a standard Win32 timer.
- If you want your application to work on every 32 bit Windows platform, and you need high precision, use the multimedia timer.
- If you want your application to work on Windows 98/NT4 and later, you need low system overhead, and can afford to block the calling thread, use the waitable timer.
- If you want a high-precision, low-overhead, non-blocking timer that will work on Windows 2000 and later, use the queue timer.
// Trying with new implementation
void CALLBACK TimerProc(void* lpParametar, BOOLEAN TimerOrWaitFired){
// This is used only to call step()
// Typically, this function is static member of AbstractEngine
AbstractEngine* obj = (AbstractEngine*) lpParametar;
obj->step();
}
int AbstractEngine::activate(){
winACStepsRun = 0;
if (engineCanBeActivated) {
if(!modelThreadRunning){
startModelThread();
Logger::info("Engine activated, model thread started.");
if ( router->getTcpServer() ){
router->getTcpServer()->startServer();
} else {
Logger::warning("Cannot start TCP server.");
return 1;
}
BOOL success = ::CreateTimerQueueTimer(
&m_timerHandle,
NULL,
TimerProc,
this,
0,
10,
WT_EXECUTEINTIMERTHREAD);
}
} else {
Logger::warning("Engine could not be activated.");
return 2;
}
return 0;
}
// This is used only to call step()
// Typically, this function is static member of AbstractEngine
AbstractEngine* obj = (AbstractEngine*) lpParametar;
obj->step();
}
int AbstractEngine::activate(){
winACStepsRun = 0;
if (engineCanBeActivated) {
if(!modelThreadRunning){
startModelThread();
Logger::info("Engine activated, model thread started.");
if ( router->getTcpServer() ){
router->getTcpServer()->startServer();
} else {
Logger::warning("Cannot start TCP server.");
return 1;
}
BOOL success = ::CreateTimerQueueTimer(
&m_timerHandle,
NULL,
TimerProc,
this,
0,
10,
WT_EXECUTEINTIMERTHREAD);
}
} else {
Logger::warning("Engine could not be activated.");
return 2;
}
return 0;
}
No comments:
Post a Comment