Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

A homebrewed lightweight scheduler

2.44/5 (9 votes)
13 Feb 2008CPOL1 min read 1  
timer based task scheduler

Introduction

There are times when a task has to be executed at a predetermined time interval. The solution presented here is a set of C++ classes that provides a wrapper around the TimerQueue API.

Using the code

The scheduler consists of CTask class and a CScheduler class... The scheduler maintains a list of tasks which will be executed by the scheduler once the timer associated with the task expires. When the task is executed by the scheduler, the user defined callback function which is cached inside the CTask class gets executed. The scheduler provides interface to queue new tasks and cancel tasks enqued by the user.

Following is the class declaration for CTask class.

C++
//Unit of execution scheduled by CScheduler 
class CTask{

friend VOID CALLBACK CTimerScheduler::WaitOrTimerCallback(PVOID lpParameter,BOOLEAN TimerOrWaitFired);

private:
HANDLE hQueueTimer_; //handle to timerqueue for this task
void(*callback_)(void);//user defined callback function
CScheduler& sched_;//the scheduler

public: 
CTask(CScheduler& sched, void(*callback)(void)) :
hQueueTimer_(0), sched_(sched), callback_(callback){}
void setHandle(HANDLE hQueueTimer);
void finish();
};


Following is the class declaration for CScheduler

C++
/*Prototypes of functions to be pulled from kernel32.dll*/

typedef VOID (CALLBACK *WAITORTIMERCALLBACK)(PVOID lpParameter,BOOLEAN TimerOrWaitFired); 

typedef HANDLE (*CREATETIMERQUEUE)(void);

typedef BOOL (CALLBACK *CREATETIMERQUEUETIMER)(
PHANDLE phNewTimer,
HANDLE TimerQueue, 
WAITORTIMERCALLBACK callback,
PVOID Parameter,
DWORD DueTime,
DWORD Period,
ULONG Flags
);

typedef BOOL (CALLBACK *DELETETIMERQUEUETIMER)(
HANDLE TimerQueue,
HANDLE Timer,
HANDLE CompletionEvent
);

typedef BOOL (CALLBACK *DELETETIMERQUEUE)(
HANDLE TimerQueue
);

/**/

class CScheduler{ 
friend CTask;

friend VOID CALLBACK WaitOrTimerCallback(PVOID lpParameter,BOOLEAN TimerOrWaitFired);

private:
unsigned long interval_;
list<CTask*> tasks_;
HANDLE hTimer_; 
HMODULE hLibrary_;
static CRITICAL_SECTION cs_;

private:
static VOID CALLBACK WaitOrTimerCallback(PVOID lpParameter,BOOLEAN TimerOrWaitFired);

CREATETIMERQUEUE createTimer;

DELETETIMERQUEUE deleteTimerQueue;

CREATETIMERQUEUETIMER createTimerQueueTimer;

DELETETIMERQUEUETIMER deleteTimerQueueTimer;

public: 
~CScheduler();
void start();
void stop();
void enqueueTask(int interval, void (*callback)(void));
void removeTask(CTask* task);
void cancelTasks();
};

To use these classes in your code copy the code into respective files and include the same in your project.

C++
#include <windows.h>
#include "stdafx.h"
#include "Scheduler.h"

void UserSuppliedFunction(){
 static int x = 0; 
 printf("function called %ld\n", ++x);
}
int main(int argc, char* argv[])
{
 CScheduler sched;
 sched.start();
 unsigned int i = 0xFFFFFFFF;
 while( i-- ){
  sched.enqueueTask((i/10000000)*2, UserSuppliedFunction);  
  Sleep(5);
  if( (i%100) == 0 ){
   printf("cancelling....\n");
   sched.cancelTasks();
  }
 }
 sched.cancelTasks();
 sched.stop();
 Sleep(1000);
 return 0;
}

Following is the implementation of the classes

C++
#include "Scheduler.h"
#include <assert.h />

CRITICAL_SECTION CScheduler::cs_ ;


void
CScheduler::start(){
    hLibrary_   = LoadLibrary("kernel32");
    createTimer = 
          (CREATETIMERQUEUE)GetProcAddress(hLibrary_, "CreateTimerQueue");
    deleteTimerQueue = 
          (DELETETIMERQUEUE)GetProcAddress(hLibrary_, "DeleteTimerQueue");
    createTimerQueueTimer = 
     (CREATETIMERQUEUETIMER)GetProcAddress(hLibrary_, "CreateTimerQueueTimer");
    deleteTimerQueueTimer = 
     (DELETETIMERQUEUETIMER)GetProcAddress(hLibrary_, "DeleteTimerQueueTimer");

    InitializeCriticalSection(&cs_);
    hTimer_ = createTimer();
}

void
CScheduler::stop(){
    cancelTasks();
    deleteTimerQueue(hTimer_);    
    DeleteCriticalSection(&cs_);
    FreeLibrary(hLibrary_);
}

void
CScheduler::enqueueTask( int interval, void(*callback)(void) ){

    CTask* newTask = new CTask(*this, callback);    
    HANDLE hQueueTimer = 0;

        EnterCriticalSection(&cs_);
    createTimerQueueTimer(                        &hQueueTimer, hTimer_,                        (WAITORTIMERCALLBACK)WaitOrTimerCallback,                        (void*)newTask,                        interval, 0, WT_EXECUTEINPERSISTENTTHREAD | WT_EXECUTEONLYONCE );
    newTask->setHandle(hQueueTimer);
    tasks_.push_back(newTask);

       LeaveCriticalSection(&cs_);
}

void
CScheduler::cancelTasks(){

    EnterCriticalSection(&cs_);
    while(1){
        list<ctask* />::iterator iter = tasks_.begin();
        if(iter == tasks_.end()) break;
        iter.operator *()->finish();
    }
    LeaveCriticalSection(&cs_);
}

void
CScheduler::removeTask(CTask* task){    
    tasks_.remove(task);    
    delete task;    
}

VOID CALLBACK 
CScheduler::WaitOrTimerCallback(PVOID lpParameter,BOOLEAN TimerOrWaitFired){

    EnterCriticalSection(&CScheduler::cs_);
    CTask* task = static_cast<ctask* />(lpParameter);
    task->callback_();
    task->finish();
    LeaveCriticalSection(&CScheduler::cs_);
}

C++
void
CTask::setHandle(HANDLE hQueueTimer){
    hQueueTimer_ = hQueueTimer;
}

void
CTask::finish(){
    sched_.deleteTimerQueueTimer(sched_.hTimer_, hQueueTimer_, NULL);
    sched_.removeTask(this);
}

This is the first release of the software and following updation can be expected in the further versions... Presently the task can be executed only once which can be made to executed periodically... User defined function arguments can be cached in CTask class and can be passed as parameter to the callback function.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)