The Event Scheduler

With the openWNS event scheduler you can schedule anything that is callable in your simulation. A callable can be a function pointer, a function object or anything else that implements the call operator, requires no arguments and has a return value of void. This page shows you how to use boost::bind to create such callables in a uniform way and use the resulting function objects with the scheduler.

Scheduling free functions

We will start with a very simple example. Suppose there exists a free function freeFunction within your simulation and you want to schedule calls to this function at later points in time. The example function we use here looks like this:

int globalVariable = 0;

void freeFunction()
{
    wns::logger::Logger log("tests", "SchedulerBestPractices");

    MESSAGE_BEGIN(NORMAL, log, m, "freeFunction is called.");
    m << "Setting globalVariable to 101";
    MESSAGE_END();

    globalVariable = 101;
}

Every time it is called it sets a global variable to the value 101. To schedule a call to this function at some later point in (simulation) time you could simply do:

wns::events::scheduler::Interface* scheduler = NULL;

scheduler = wns::simulator::getEventScheduler();

wns::events::scheduler::Callable c = &freeFunction;

scheduler->scheduleDelay(c, 10.0);

This tells the scheduler to call the function freeFunction after 10 seconds of simulation time have passed. The argument you pass to the scheduleDelay function is a Callable that carries a pointer to freeFunction and the delay after which the call to this function should be made. This is quite simple if you deal with free functions, i.e. functions that do not belong to a class and therefore do not have a context. If you want to schedule calls to member functions (which you probably want to do frequently within an object oriented simulator) you need to be able to tell the scheduler on which object the scheduler should make the call to a member function, you want to bind the function pointer to an instance of an object. This is when you want to use boost::bind. Let us just stick with the last example for a short moment and see how you could use boost::bind to create a Callable for freeFunction.

wns::events::scheduler::Interface* scheduler = NULL;

scheduler = wns::simulator::getEventScheduler();

wns::events::scheduler::Callable c = boost::bind(&freeFunction);

scheduler->scheduleNow(c);

Pretty simple to do that. Simply call boost::bind() and pass the function pointer as the argument. This creates an object that is compatible with wns::events::scheduler::Callable. However, this does not have any immediate benefit for us. We will see later that by using boost::bind we get a consistent syntax for all Callables which greatly improves readability.

Scheduling Member Functions

So let us have a look at member functions now. Suppose you have the following class:

class ClassWithCallback
{
    int memberVariable_;

    wns::logger::Logger logger_;

public:
    ClassWithCallback() :
        memberVariable_(0),
        logger_("tests", "SchedulerBestPractices")
        {
        }

    void
    callback()
        {
            MESSAGE_BEGIN(NORMAL, logger_, m, "");
            m << "ClassWithCallback::callback is called."
              << "Setting globalVariable to 101";
            MESSAGE_END();
            this->memberVariable_ = 101;
        }

    int
    getMemberVariable()
        {
            return memberVariable_;
        }

    void
    setMemberVariable(int value)
        {
            memberVariable_ = value;
        }
};

The behaviour of ClassWithCallback::callback is pretty much the same as the freeFunction of our last example, but now the scope is not global but limited to the scope of the class instance.

Scheduling Member Functions by Pointer

When we pass an instance of some type to a boost::bind expression it is copied. This is fine and yields the expected results if we work with pointers, but it does not necessarily yield the expected results if you pass your arguments by value (see next section). To schedule a member function of an object where you have a pointer to can be done like this:

wns::events::scheduler::Interface* scheduler = NULL;

scheduler = wns::simulator::getEventScheduler();

// Create a Smart Pointer to a new ClassWithCallback instance
ClassWithCallback* classWithCallbackPtr = new ClassWithCallback();

wns::events::scheduler::Callable c =
    boost::bind(&ClassWithCallback::callback,
                classWithCallbackPtr);

scheduler->scheduleDelay(c, 10.0);

Scheduling Member Functions by Value

The code below illustrates the usage of boost::bind if you have references to your callback objects. To avoid copying of your callback object when you pass it as an argument to boost::bind, just wrap in an boost::ref or boost::cref (wrapper for const references) object. Thereby, only the reference wrapper is copied but not the reference itself. You could also have simply taken the address (& operator) of your callback object and pass it to boost::bind to enforce pointer semantics.

wns::events::scheduler::Interface* scheduler = NULL;

scheduler = wns::simulator::getEventScheduler();

ClassWithCallback classWithCallbackInstance;

wns::events::scheduler::Callable c =
    boost::bind(&ClassWithCallback::callback,
                boost::ref(classWithCallbackInstance));

scheduler->scheduleDelay(c, 10.0);

Scheduling functions that take arguments

The examples above lack a very important feature. None of the above callbacks can take an argument. Most often within your simulation you want to schedule calls that take parameters. Boost::bind offers partial binding of parameters, too. If you bind all parameters in an boost::bind expression, the result is a nullary function object that you can the pass the the scheduler. The follwing example illustrates this:

wns::events::scheduler::Interface* scheduler = NULL;

scheduler = wns::simulator::getEventScheduler();

ClassWithCallback* classWithCallbackPtr = new ClassWithCallback();

wns::events::scheduler::Callable c1 =
    boost::bind(
        &ClassWithCallback::setMemberVariable,
        classWithCallbackPtr,
        201);

wns::events::scheduler::Callable c2 =
    boost::bind(
        &ClassWithCallback::setMemberVariable,
        classWithCallbackPtr,
        302);

scheduler->scheduleDelay(c1, 11.0);

scheduler->scheduleDelay(c2, 10.0);

This concludes the scheduler best practices lesson.

Cancelling Events

Whenever you schedule an event by using either the schedule or scheduleDelay method of the eventschdeduler these methods return an instance of IEventPtr . This is a handle for your scheduled event and can be used to remove it from the scheduler queue again. This is demonstrated in the next example.

wns::events::scheduler::Callable timeout = &freeFunction;

// Remember a handle of your event
IEventPtr timeoutHandle = scheduler->scheduleDelay(timeout, 15.0);

// Use the handle to cancel an event that was already scheduled
scheduler->cancelEvent(timeoutHandle);