/*
* Copyright (C) 2004 Alo Sarv <madcat_@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/** @file event.h Interface for Event subsystem */
#ifndef __EVENT_H__
#define __EVENT_H__
/**
* @page ehsv2 Event Handling Subsystem, version 2
*
* \section intro Introduction
* Event Handling Subsystem version 2 (EHS) provides a generic API for passing
* arbitary objects from a <i>source</i> to one or more <i>handlers</i> without
* coupling neither the source and handlers, nor coupling either of those with
* the EHS.
*
* \section ratio Rationale
* The original Event Handling Subsystem (version 1) had a number of issues that
* made the subsystem inconvenient to use, errorpone and hard to debug. Namely,
* the syntax for postingevents and/or setting handlers was so inconvenient that
* we had to resort to preprocessor macros to compensate for that. In addition,
* the original engine was based on CBFunctor library, which dates back to 1994,
* and thus breaks on some modern compilers. Thus, the system was rewritten.
*
* \section req Requirements
* The requirements for EHSv2 are:
* \list
* - Decouple the event source from event tables. This is required to allow
* posting events from sources that were originally not designed for events,
* because they come from, for example, third-party libraries.
* - Easy-to-learn and easy-to-use usage syntax.
* - Base on a modern function objects library that is portable.
*
* \section usage Usage
* There are two common operations needed to be done when it comes to event
* tables usage - setting up event handlers, and posting events. In order to
* provide maximum simplicity for those operations, there are a set of utility
* methods (defined in <b>EHS</b> namespace) which simplify the syntax. The
* basic usage of EHS goes as follows:
*
* \code
* // Initialize objects. Note: the first event (posted in MySource
* // constructor) is not handled.
* MyHandler *h = new MyHandler;
* MySource *s = new MySource;
* // Set up handler, and post an event
* EHS::addHandler(s, h, &MyHandler::handlerfunc);
* EHS::postEvent(s, MyEvent("Hello world!"));
* \endcode
*
* The utility methods are defined in <b>EHS</b> namespace. If your compiler
* does not support argument-dependant lookup, the calls to those methods must
* be qualified with explicit namespace name, e.g.
*
* \code
* EHS::addHandler(s, h, &MyHandler::handlerfunc);
* EHS::postEvent(s, MyEvent("Hello world!"));
* \endcode
*/
// hydranode includes
#include <hn/eventbase.h>
#include <hn/gettickcount.h>
// event tables backend - Boost.Signal, Boost.Function and Boost.Bind libraries
#include <boost/signal.hpp>
#include <boost/signals/trackable.hpp>
#include <boost/signals/connection.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
// multi-thread safety - Boost.Thread (mutexes)
#include <boost/thread.hpp>
// std includes
#include <map>
/**
* EventTable template class encapsulates pending events storage, event handlers
* storage and event handlers calling when instructed to do so from main event
* loop. For each event source and event type, there is a specific event table.
* All event tables are Singletons.
*/
template<
typename Source,
typename Event,
typename Handler = boost::function<void (Source, Event)>
>
class EventTable : public EventTableBase {
public:
/**
* Accessor for the single instance of this class. Note that the static
* object is also protected via mutexes to be completely safe in MT
* environment.
*/
static EventTable& instance() {
static EventTable evtT;
static boost::recursive_mutex evtTMutex;;
boost::recursive_mutex::scoped_lock l(evtTMutex);
return evtT;
}
/**
* Connect an event handler to a source. The handler shall be called
* whenever the source object emits events.
*
* \note It is recommended to use classes derived from boost::signals::
* trackable for event handlers, and connect them via the
* overloaded addHandler method, since that way automatic
* disconnection is guaranteed when either the signal source or
* the signal handler objects get destroyed.
*
* @param src Source the handler is interested in
* @param ha Event handler function object
* @return The established connection between the event source
* and event handler. This can later be used to disconnect
* the handler from the source.
*/
boost::signals::connection addHandler(Source src, Handler ha) {
boost::recursive_mutex::scoped_lock l(m_sigMapMutex);
Iter i = m_sigMap.find(src);
if (i == m_sigMap.end()) {
i = m_sigMap.insert(
m_sigMap.end(),
std::make_pair(src, new SigType())
);
}
return (*i).second->connect(ha);
}
/**
* Overloaded version of the above method, performs function object
* binding internally. This is the recommended version of this function,
* since while being also simpler to use, it also guarantees function
* disconnection in case the object is derived from boost::signals::
* trackable.
*
* \note This limitation is inherent from Boost.Signals library -
* namely, trackable objects only work when bound with
* boost::bind, raw Boost.Function or Boost.Lambda functors do not
* implement the necesery interface for this to work.
*
* @param src Event source to be interested in
* @param obj Event handler object
* @param func Event handler function
* @return The established connection between the event source and
* event handler, which can later be used to disconnect the
* handler from the source.
*/
template<typename T>
boost::signals::connection addHandler(
Source src, T *obj, void (T::*func)(Source, Event)
) {
return addHandler(src, boost::bind(func, obj, _1, _2));
}
/**
* Disconnect an event handler which has previously been connected with
* addHandler() method.
*
* @param src Event source
* @param c Connection to be disconnected
*/
void delHandler(Source src, const boost::signals::connection &c) {
boost::recursive_mutex::scoped_lock l(m_sigMapMutex);
Iter i = m_sigMap.find(src);
if (i != m_sigMap.end()) {
(*i).second->disconnect(c);
}
if ((*i).second->empty()) {
delete (*i).second;
m_sigMap.erase(i);
}
}
/**
* Erase all handlers refering to a specific source. This is useful to
* be called from either source object's destructor, or by whoever is
* destroying the source, to clean up the event table things.
*
* @param src Source object
*/
void delHandler(Source src) {
boost::recursive_mutex::scoped_lock l(m_sigMapMutex);
Iter i = m_sigMap.find(src);
if (i != m_sigMap.end()) {
delete (*i).second;
m_sigMap.erase(i);
}
}
/**
* Post an event to the event queue, which shall be passed to all
* handlers during next event loop.
*
* @param src Event source
* @param evt The event itself
*/
void postEvent(Source src, Event evt) {
boost::recursive_mutex::scoped_lock l(m_pendingMutex);
m_pending.push_back(InternalEvent(src, evt));
}
/**
* Post a delayed event, which shall be emitted when the delay is over.
* Note that the actual delay until to the event emitting may vary,
* depending on application load, up to 100ms.
*
* @param src Event source
* @param evt The event itself
* @param delay Delay, in milliseconds, until event will be passed to
* handlers
*/
void postDelayedEvent(Source src, Event evt, uint32_t delay) {
boost::recursive_mutex::scoped_lock l(m_delayedMutex);
m_delayed.insert(
std::make_pair(
Utils::getTick() + delay,
InternalEvent(src, evt)
)
);
}
/**
* Call all handlers for all pending events. This method is called from
* main event loop.
*/
virtual void handleEvents() {
checkForDelayed();
boost::recursive_mutex::scoped_lock l1(m_pendingMutex);
boost::recursive_mutex::scoped_lock l2(m_sigMapMutex);
for (PIter i = m_pending.begin(); i != m_pending.end(); ++i) {
Iter j = m_sigMap.find((*i).getSource());
if (j != m_sigMap.end()) {
(*(*j).second)(
(*i).getSource(), ((*i).getEvent())
);
}
}
m_pending.clear();
}
private:
/**
* Singleton pattern requirements - private constructor/destructor, and
* non-copyable requirement.
*/
//@{
EventTable() {}
EventTable(const EventTable&);
EventTable& operator=(const EventTable &);
~EventTable() {}
//@}
class InternalEvent;
//! Type of signal used in this event table
typedef typename boost::signal<void (Source, Event)> SigType;
//! Helper typedef, for iterating on sigMap
typedef typename std::map<Source, SigType*>::iterator Iter;
//! Helper typedef, for iterating on pending events vector
typedef typename std::vector<InternalEvent>::iterator PIter;
/**
* Stores all signal objects for all objects of type Source. This map
* is filled by addHandler() method, which adds the signal here if it's
* not here already. Also, addHandler() connects functions to the
* signals in this map. When a signal in this map no longer has any
* handlers, it should be removed (done in delHandler() method).
*
* \note We'r using heap-allocated signal objects here, since Signal
* is not copyable.
*/
std::map<Source, SigType*> m_sigMap;
/**
* Pending events queue, filled by postEvent() method and cleared in
* handleEvents().
*/
std::vector<InternalEvent> m_pending;
/**
* Delayed events, which will be posted when the timeout is over.
* The key to this map is the actual tick when the event should be
* emitted.
*/
std::multimap<uint32_t, InternalEvent> m_delayed;
/**
* Recursive mutexes which protect the above three containers in multi-
* threaded environment. They are recursive since we are most likely
* to be called from an event handler from same thread, in which case we
* want to be able to access the maps.
*/
//@{
boost::recursive_mutex m_sigMapMutex;
boost::recursive_mutex m_pendingMutex;
boost::recursive_mutex m_delayedMutex;
//@}
/**
* Wrapper class for temporary storing pending events and similar.
*/
class InternalEvent {
public:
//! Constructor
InternalEvent(Source src, Event evt)
: m_src(src), m_evt(evt){}
//! @name Accessors
//@{
Source getSource() const { return m_src; }
Event getEvent() const { return m_evt; }
//@}
private:
Source m_src; //!< Event source object
Event m_evt; //!< The event itself
};
/**
* Transfer all delayed events for which the delay is over to pending
* list.
*/
void checkForDelayed() {
boost::recursive_mutex::scoped_lock l1(m_delayedMutex);
boost::recursive_mutex::scoped_lock l2(m_pendingMutex);
uint64_t tick = Utils::getTick();
while (m_delayed.size() && (*m_delayed.begin()).first < tick) {
m_pending.push_back((*m_delayed.begin()).second);
m_delayed.erase(m_delayed.begin());
}
}
};
//! Event Handler Subsystem utility functions
namespace EHS {
/**
* @name Event Handling Subsystem Utility Functions
* Globally defined methods for easier access for automatic template
* parameter deduction. Thus, the following two source lines are equivalent:
*
* \code
* EventTable<MyClass, MyEvent>::instance().postEvent(this, evt);
* postEvent(this, evt);
* \endcode
*/
//@{
template<typename Source, typename Event>
void postEvent(Source src, Event evt) {
EventTable<Source, Event>::instance().postEvent(src, evt);
}
template<typename Source, typename Event>
boost::signals::connection addHandler(
Source src, boost::function<void (Source, Event)> ha
) {
return EventTable<Source, Event>::instance().addHandler(src, ha);
}
template<typename Source, typename Event, typename T>
boost::signals::connection addHandler(
Source src, T *obj, void (T::*ha)(Source, Event)
) {
return EventTable<Source, Event>::instance().addHandler(src, obj, ha);
}
template<typename Source, typename Event>
void delHandler(Source src, boost::signals::connection conn) {
EventTable<Source, Event>::instance().delHandler(src, conn);
}
template<typename Source, typename Event>
void delHandler(Source src) {
EventTable<Source, Event>::instance().delHandler(src);
}
//@}
}
#endif