Introduction
In a
previous article, I had discussed a C++ wrapper around the
WaitForMultipleObjects
API that provides a generic interface to the API
which eliminates the need to write the rather ubiquitous switch-case statement to
process its return value and transfer control to the appropriate method or function. The
wrapper allows you to dynamically add and remove wait handles associating a function object
as the handler function for a wait handle. In this article I'll be adding Win32
timer support to this class so that class clients can register function objects
to be called when a preset time interval has elapsed.
Background
It is typical of most software to have routine processing tasks. Examples for such tasks are
housekeeping functions that perform clean up of unused resources or a function to monitor the
health of connected clients in a network application. A common programming idiom used to implement
this sort of mechanism is the timer which involves invoking functions when a preset time
interval has elapsed. Timers can be fully self managed where a worker thread, amongst its other
duties, keeps routinely checking if one of the required timer tasks ought to be invoked. If the
program has GUI, Windows provides timer messages that will be sent to the specified window at
a preset interval where the required processing can be undertaken. Yet another alternative is
if the underlying operating system provides the necessary API, that can be used to register
callbacks which will be invoked when the specific time interval has elapsed.
Windows SDK provides a few functions that implement the last style of timer mechanism.
The Windows SDK timer APIs provide for two types of timers -- manual-reset and synchronization
timers. The former type exhibits a behavior that is quite similar to Win32 events in that they can
be waited upon in a wait function to be signalled. When the preset time elapses their state changes
to signalled and the wait function which is monitoring the handle returns. The latter uses
Asynchronous Procedure Calls (APC) to implement their functionality and requires the thread
that is managing the timers to be in, what Microsoft defines as an alertable wait state. When
the timer interval has elapsed an APC is queued to the thread APC queue which is serviced when
the thread enters an alertable wait state.
Using Windows SDK provided timers are inheretntly more efficient as they are built into the
scheduler. Using them also leads to somewhat simpler and cleaner code as the operating system
will do the hard work of keeping track of the elapsed interval and when to invoke the timer
function (or signal the timer object). Furthermore, multiple timer objects can be created and
managed independently which helps achieve better encapsulation.
For integrating timer functionality into WFMOHandler, I'll be using the manual-reset timers.
I chose this as they are closer to waitable objects in behavior which made the
implementation similar to that of WaitHandler<>
. Furthermore, from the MSDN documentation
for SetWaitableTimer,
this approach seemed to provide better guarantee of timer delivery than synchronous timers.
The down-side is that every timer would occupy a slot in the limited WaitForMultipleObjects
handle array thereby reducing the number of wait handles that can be monitored.
Using the Code
Timer support is added through two additional methods -- AddTimer()
and
RemoveTimer()
.
AddTimer()
is a function template and has a signature quite similar to
AddWaitHandle()
. Besides the function object that is the timer handler which will be
called when the timer expires, it takes two additional parameters; the timer interval to be
specified in milliseconds and a boolean that specifies whether this is a one-off timer or a repeat
timer. As the name implies, one-off timers are just that -- timer function object is invoked once
after the preset interval elapses and the timer is no longer usable and is disposed.
Repeat timers, on the other hand, work like the good old GUI windows timer, routinely invoking
the timer function object until it is explicitly stopped. The function returns an unsigned
,
which is the unique id of the timer just created. This id can be later used to stop the timer by
supplying it to the RemoveTimer()
method.
To use, declare your class deriving it from WFMOFHandler
and register the timers
that you need by supplying the timer handler function or method as a function object. As with wait
handles, this argument can be an explicitly constructed function object or one that is composed on the
fly using the STL std::bind()
facility. Both these two usages are shown below in the
MyDaemon
constructor.
#include "wfmohandler.h"
class AsyncSocket {
USHORT m_port;
WSAEVENT m_event;
SOCKET m_socket;
AsyncSocket();
AsyncSocket(const AsyncSocket&);
public:
AsyncSocket(USHORT port)
: m_port(port)
, m_event(::WSACreateEvent())
, m_socket(::WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, 0))
{
struct sockaddr_in sin = {0};
sin.sin_family = AF_INET;
sin.sin_port = ::htons(port);
sin.sin_addr.s_addr = ::inet_addr("127.0.0.1");
if (m_event != NULL && m_socket != INVALID_SOCKET
&& ::bind(m_socket, reinterpret_cast<const sockaddr*>(&sin), sizeof(sin)) == 0) {
if (::WSAEventSelect(m_socket, m_event, FD_READ) == 0)
return;
}
std::cerr << "Error initializing AsyncSocket, error code: " << WSAGetLastError() << std::endl;
if (m_event != NULL) ::WSACloseEvent(m_event);
if (m_socket != INVALID_SOCKET) ::closesocket(m_socket);
throw std::exception("socket creation error");
}
~AsyncSocket()
{
::closesocket(m_socket);
::WSACloseEvent(m_event);
}
operator HANDLE() { return m_event; }
void ReadIncomingPacket()
{
std::vector<char> buf(64*1024);
struct sockaddr_in from = {0};
int fromlen = sizeof(from);
int cbRecd = ::recvfrom(m_socket, &buf[0], buf.size(),
0, reinterpret_cast<sockaddr*>(&from), &fromlen);
if (cbRecd > 0) {
std::cerr << cbRecd << " bytes received on port " << m_port << std::endl;
} else {
int rc = ::WSAGetLastError();
if (rc == WSAEWOULDBLOCK) {
::WSAResetEvent(m_event);
} else {
std::cerr << "Error receiving data from port " << m_port
<< ", error code: " << ::WSAGetLastError() << std::endl;
}
}
}
};
class MyDaemon : public WFMOHandler {
AsyncSocket m_socket1;
AsyncSocket m_socket2;
AsyncSocket m_socket3;
class AnotherEventHandler { AsyncSocket& m_socket;
public:
AnotherEventHandler(AsyncSocket& sock) : m_socket(sock) {}
void operator()() {
m_socket.ReadIncomingPacket(); }
};
AnotherEventHandler m_aeh;
public:
MyDaemon()
: WFMOHandler()
, m_socket1(5000)
, m_socket2(6000)
, m_socket3(7000)
, m_aeh(m_socket3)
{
WFMOHandler::AddWaitHandle(m_socket1,
std::bind(&AsyncSocket::ReadIncomingPacket, &m_socket1));
WFMOHandler::AddWaitHandle(m_socket2,
std::bind(&AsyncSocket::ReadIncomingPacket, &m_socket2));
WFMOHandler::AddWaitHandle(m_socket3, m_aeh);
m_timerid = WFMOHandler::AddTimer(1000, true,
std::bind(&MyDaemon::RoutineTimer, this, &m_socket1));
m_oneofftimerid = WFMOHandler::AddTimer(3000, false,
std::bind(&MyDaemon::OneOffTimer, this));
}
virtual ~MyDaemon()
{
Stop();
WFMOHandler::RemoveWaitHandle(m_socket2);
WFMOHandler::RemoveWaitHandle(m_socket1);
}
void RoutineTimer(AsyncSocket* pSock)
{
pSock;
std::cout << "Routine timer has expired!" << std::endl;
}
void OneOffTimer()
{
std::cout << "One off tmer has expired!" << std::endl;
m_oneofftimerid = 0;
}
};
The code above is the same as what was used for the
previous article save for the addition of two methods and hooking them up with the new timer API.
Both use cases of the timer API -- one off timer and repeat timer -- are exhibited
in the sample.
Points of Interest
Unlike the wait handles, timer handles are managed internally by WFMOHandler. When a timer is
added, a new Win32 timer object is created and added to the wait handle array. This handle is not
visible to the class client and is released when the timer is removed by a corresponding call to the
RemoveTimer()
method. However, for one-off timers, the timer handle will automatically
be released after the timer function returns irrespective of whether RemoveTimer()
is called or not.
Under the hood, the timer interface is implemented using the same technique as what is employed
for wait handles. However, the inner class template, TimerHandler<>
, used for generating
timer specific class instances, adds an extra class, TimerIntermediate
, to the
derivation chain. On the surface, this class would appear to have been created to hold the unique
id of the timer which is used to track the removal request from client classes and might even seem
redundant as the timer id can be stored in the template class TimerHandler<>
. However,
there is another reason why this additional class is there in the derivation chain of
TimerHandler<>
.
Having TimerIntermediate
in the derivation chain helps us distinguish these
objects from WaitHandler<>
class instance objects from the common
m_waithandlers
collection. This is necessary as in RemoveTimer()
we need to locate the TimerHandler<>
class instance object for the timer id that
is being requested to be removed. However, since the objects corresponding to all wait handles
that we track are stored uniformly, as a pointer to the base class WaitHandlerBase
,
we need a mechanism to differentiate the two children. This is where having
TimerIntermediate
in the derivation chain comes handy. Since all the
TimerHandler<>
instances also have a TimerIntermediate
in the derivation
chain, we can distinguish the two by using dynamic_cast<TimerIntermediate*>
to cast
the WaitHandlerBase
pointer upwards to TimerIntermediate
.
Note that we could have achieved the objective of distinguishing the two children of
WaitHandlerBase
by adding an extra method to the class that returns a boolean by
default. TimerHandler<>
can then override this method and return a different
value to identify itself. Sample implementation is shown below.
struct WaitHandlerBase {
HANDLE m_h;
bool m_markfordeletion;
WaitHandlerBase(HANDLE h) : m_h(h), m_markfordeletion(false)
{}
virtual ~WaitHandlerBase()
{}
virtual bool IsTimer() { return false; }
virtual void invoke(WFMOHandler*) = 0;
};
template<typename Handler>
struct TimerHandler : public WaitHandlerBase, public TimerIntermediate {
...
virtual bool IsTimer() { return true; }
...
}
However, this entails changing the base class design to accommodate a derived class'
requirement. Something that is to be ideally avoided if possible. Of course
this would be the more efficient approach of the two, but since removing of timers is typically
not an oft repeated task in a program, I felt such a design (or compromise of pure design, rather)
was not warranted.
Caveats
One caveat of the implementation is that once a timer is created, its duration cannot be changed.
If the timer interval is to be changed, the only solution is to remove the existing timer and then
add a new timer with the new interval. Of course this behavior can be altered though this was enough
for my needs as I developed this class. Implementing this would however require the
TimerIntermediate
class to be extended so that it maintains the timer interval as
well as the repeat flag so that these properties can be updated from the relevant method.
History
07 Jan 2014 - Intial release