Thursday, May 24, 2012

Windows timer


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 value
  • TimerQueue - Timer queue handle. For the default timer queue, NULL
  • Callback - Pointer to the callback function
  • Parameter - Value passed to the callback function
  • DueTime - Time (milliseconds), before the timer is set to the signaled state for the first time
  • Period - Timer period (milliseconds). If zero, timer is signaled only once
  • Flags - 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
The callback function is really pretty simple:
VOID CALLBACK WaitOrTimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired); 

Arguments

  • lpParameter - Pointer to user-defined data
  • TimerOrWaitFired - always TRUE for timer callbacks
To cancel a queue timer, use the function:
BOOL DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent); 

Arguments

  • TimerQueue - A handle to the (default) timer queue
  • Timer - A handle to the timer
  • CompletionEvent - A handle to an optional event to be signaled when the function is successful and all callback functions have completed. Can be NULL.

Return Value

  • Nonzero if the function succeeds
The example for queue timers is given below:
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:
  1. 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.
  2. If you want your application to work on every 32 bit Windows platform, and you need high precision, use the multimedia timer.
  3. 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.
  4. 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;
}








No comments:

Post a Comment