An application based on libcw is event driven. All events that need queueing are dispatched from the main loop, meaning that during the processing of such an event all other events are blocked until the processing has finished and the application returns to the main loop. An advantage of this is that there is no need for locking, as is needed with threading. The disadvantage is that the accuracy of timing depends on the rather arbitrary duration of these event-handling cycles.
The main loop of the libcw application processes events in the following order:
timer_event_server_ct
Libcw treats timer events as events. The event server for timer events is a singleton, its instance can be accessed through the static method timer_event_server_ct::instance(). The timer event server accepts request data of type struct timeval.
As with all events, a busy interface and/or a cookie can be passed to the timerRequest call as needed (see also chapter Events §2.2.6).
The request data is a struct timeval: the interval to wait before using the callback function. The timer event will not occur before timer_event_server_ct::instance().get_now() plus the given interval. It is guaranteed that after that time no new IO events are handled before the timer event is delivered to the event client (or its busy interface). Timer events that are requested to expire at the same time are handled in an undefined order.
The event interface uses operator() to register event requests. Because the timer server is a singleton, one must use the rather obnoxious looking code (where Foo::foo is an event client and Foo::timed_out(timer_event_type_ct const&) the callback function)
struct timeval interval = { 5, 0 }; (timer_event_server_ct::instance())(interval, foo, &Foo::timed_out);
In order to make such expressions more readable, libcw defines the macro timerRequest
#define timerRequest (timer_event_server_ct::instance())
allowing the developer to request timer events with better looking code
timerRequest(interval, foo, &Foo::timed_out);
Read here why a macro is prefered over a function call in this case.
For example, suppose you wish to request member function template<typename COOKIE> void Foo::timed_out(timer_event_type_ct const& expired_at, COOKIE cookie); of object foo to be called with an int cookie, 60 seconds after now. Then you'd write:
Foo foo; int cookie = 123; struct timeval interval = { 60, 0 }; timerRequest(interval, foo, &Foo::timed_out<int>, cookie);
The event type reference that is passed as parameter to the callback function (the «Foo::timed_out» above), is derived from class timer_event_data_ct.
struct timeval timer_event_data_ct::get_expire_time(void) const
Returns the time at which this event should have expired. The real time (timer_event_server_ct::instance().get_now()) will always be larger.
This is the unique event type of timer_event_server_ct which is passed to the callback function. It is derived from timer_event_data_ct and doesn't have any extra data or methods.
For example, the callback function timed_out<typename COOKIE> from the previous example could be defined like:
template<typename COOKIE> void Foo::timed_out(timer_event_type_ct const& expired_at, COOKIE cookie) { Dout( dc::notice, "It should be a little past " << expired_at.get_expire_time() << " now. " "I got the cookie: " << cookie ); }