event.h
Created with C++BuilderX
/**
 *  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 Handling Subsystem */

#ifndef __EVENT_H__
#define __EVENT_H__

#include <wx/wx.h>
#include <list>
#include <map>

/**
 * \page ehs Event Handling Subsystem Overview
 *
 * EHS provides functionality to perform Event-driven programming. EventTable's
 * are used to post/store/handle events. EventTable is derived from
 * EventTableBase, which interfaces all EventTable objects with main loop, 
 * latter of which is located in EventMain class. All EventTable objects
 * are registred with EventMain on construction and de-registred on
 * destruction. Whenever there are events, EventTableBase::HandleEvents()
 * function is called on each and every registred EventTableBase objects.
 *
 * Since one of the most logical way to use this functionality is in
 * multi-threaded environment, where threads submit events and main thread
 * handles them, everything in here must be 100% thread-safe - thus the
 * large amount of mutexes.
 *
 * Customizability:
 *   New classes can be derived from EventTableBase (or even from EventTable)
 *   to further customize this functionality.
 *   EventTable is a template class with three (four in the future) arguments,
 *   which define the event object, event type and event handlers, allowing
 *   usage of it within any user-defined class. The afore-mentioned fourth
 *   argument (in TODO list) is event handler calling Policy, which would
 *   default to currently hardcoded void function(EventSource*, EventType).
 *
 * \todo EventTable Handler Calling Policies.
 * \todo Duplicate EventTable::m_pending list to avoid long mutex wait times.
 * \todo Create wrapper class EventSink from which it would be possible to
 *       uniquely derive user classes to give them event handling 
 *       functionality. The class could be templatized (all EventTable template
 *       parameters) plus some random argument in order to create a unique 
 *       class signature for each user class. See EventTable class 
 *       documentation for further information.
 */


/**
 * Abstract base class for event engine. Provides two common features for all
 * event tables:
 *
 * Notify() function, which need to be used whenever there is new event posted
 * to event queue. This is needed by main event loop to "wake up" and call
 * handlers.
 *
 * Pure virtual function prototype HandleEvents(), which needs to be overridden
 * by derived classes. This function will be called once per each main event
 * loop and must handle all events in the queue.
 *
 * EventTableBase class registers itself in central list (main event loop).
 */
class EventTableBase {
public:
	//! Constructor - Add to central event table list
	EventTableBase() {
		EventMain::GetInstance().AddEventTable(this);
	}
	//! Destructor - Remove from central event table list
	virtual ~EventTableBase() {
		EventMain::GetInstance().RemoveEventTable(this);
	}
	//! Notify central event system to wake up
	static inline void Notify() {
		wxMutexLocker lock(s_notifyLock);
		s_eventNotify.Broadcast();
	}

	//! Must be overridden by derived classes. This is called from central
	//! event loop for each of the registred objects. Should handle all
	//! pending events (if existing).
	virtual void HandleEvents() = 0;

	//! For implementation use only - wait until there is a pending event 
	//! somewhere.
	inline static wxCondError WaitForEvent() {
		return s_eventNotify.Wait();
	}
	//! For implementation use only - wait until there is a pending event
	//! somewhere, but only wait a specified amount of time.
	inline static wxCondError WaitForEvent(unsigned long milliseconds) {
		return s_eventNotify.WaitTimeout(milliseconds);
	}
private:
	static wxCondition s_eventNotify;  //!< Used to broadcast new events
	static wxMutex s_notifyLock;       //!< Protects the above wxCondition
};


/**
 * Event posting, storing and handling mechanism
 *
 * This template class encapsulates events posting, storing, queueing and
 * handling mechanism in a typesafe generic manner. The following template
 * arguments must be passed when creating an object of this class:
 *
 * @param EventObject     This specifies the source of the events. Generally,
 *                        this is the class for which the event table stands
 *                        for. Pointer to the original event source is also
 *                        passed to EventHandler.
 *
 * @param EventHandler    This is a callback function object which is to be
 *                        called when a specified event happens. The handlers
 *                        are registred through AddHandler() member function,
 *                        for a specified event source object. The prototype
 *                        of the EventHandler is required to be as follows:
 *                        void function(EventObject *source, EventType evt);
 *
 * @param EventType       This specifies what kind of events are being stored
 *                        within this object. The EventType is also passed to
 *                        event handler. This can be used to specify different
 *                        kinds of events, for example EVT_WORKCOMPLETE,
 *                        EVT_USRABORT, EVT_INTERNALERROR and so on.
 *
 * Usage:
 *
 * Option 1: Derive your event source class from this class, and then use
 *           the static member functions to post events, as well as to register
 *           handlers for events.
 * Option 2: Create a static member object of this class in your even source
 *           class and provide static accessors (or have the object public) to
 *           access it.
 *
 * Implementation Details:
 *
 * First and foremost, everything in here must be thread-safe, since we are
 * most likely being used from multi-threaded environment. All accesses to
 * all lists must be protected through mutexes. If we are getting too long
 * wait times for mutexes, we can consider breaking up the m_pending events
 * list into two lists: m_newPending and m_curPending. New events would then
 * be submitted to m_newPending while m_curPending can still be processed
 * at same time. Only time when we'd need to lock both lists would be after
 * handling all events in m_curPending list, at which point we could simply
 * "swap" the lists. However, this is future idea.
 *
 * As for now, we have single pending events list which is populated with events
 * through PostEvent() member function. When HandleEvents() function is called
 * (usually from application main loop), we loop through our pending events
 * list and locate handlers for the event types/objects from m_handlers list.
 * When handler is found, the function is called, otherwise the event is
 * silently ignored.
 *
 * \todo If we make all objects/functions in this class static, it means
 *         client code can't have two different event tables with exact same
 *         set of template arguments. However, if we don't make everything
 *         static, derived classes create an instance of this object per each
 *         of their own copy, thus again not working. One solution would be
 *         to create a simple wrapper class, named EventSink, which provides
 *         static member object of this class and static forwarding functions.
 *         Users would then derive from EventSink class instead of this one.
 * \todo Event handler function calling is currently hardcoded and function
 *         prototype pre-defined. However, we could use template Policies for
 *         that for maximum customizability. The policy would define event
 *         handler calling rules, thus specifying the event handler prototype.
 *         Default policy would be what is hardcoded right now:
 *         void function(EventSource *source, EventType event);
 */
template<class EventObject, class EventHandler, class EventType>
class EventTable : public EventTableBase {
public:
	//! Constructor - register with central event table list
	EventTable() {
	}
	//! Destructor - de-register from central event table list
	virtual ~EventTable() {
		EventMain::GetInstance().RemoveEventTable(this);
	}

	//! Post new event and notify central event table.
	inline void PostEvent(EventObject *source, EventType evt) {
		wxMutexLocker lock1(m_pendingLock);
		m_pending.push_back(InternalEvent(source, evt);
		EventTableBase::Notify();
	}
	//! Add new event handler
	inline void AddHandler(EventObject *source, EventHandler handler) {
		wxMutexLocker lock(m_handlersLock);
		m_handlers.insert(std::make_pair(source, handler));
	}
	//! Remove event handler
	inline void RemoveHandler(EventObject *source, EventHandler handler) {
		//! TODO! EventTable::RemoveHandler()
	}

	//! Handle all pending events
	virtual void HandleEvents() {
		wxMutexLocker lock1(m_handlersLock);
		wxMutexLocker lock2(m_pendingLock);
		for (Piter i = m_pending.begin(); i != m_pending.end(); i++) {
			HIter elem = s_handlers.find((*i).m_source);
			if (elem == s_handlers.end()) {
				continue; // No handler found
			}
			(*elem).second((*i).m_source, (*i).m_evt);
			s_pending.pop_front();
		}
	}
private:
	/**
	 * Internal structure used to encapsulate the stored data.
	 */
	struct InternalEvent {
		InternalEvent(EventObject *source, EventType evt)
			: m_source(source), m_evt(evt) {
		}
		EventObject *m_source;
		EventType m_evt;
	};

	//! Pending events
	std::list<InternalEvent> m_pending;
	//! Event handlers
	std::multi_map<EventObject*, EventHandler> m_handlers;

	wxMutex m_pendingLock;   //!< Protects pending events list
	wxMutex m_handlersLock;  //!< Protects event handlers list

	//! Lazyness strikes.
	typedef std::list<InternalEvent>::iterator PIter;
	typedef std::multimap<EventObject*, EventHandler>::iterator MIter;
};

/**
 * This Singleton class is the application main event system. Users should never
 * need to access this class directly - it is accessed from EventTableBase 
 * constructor/destructors.
 *
 * The purpose of this class is to provide a central list of event tables,
 * and wait for events. When event accours (notified through EventTableBase::
 * s_eventNotify wxCondition::Broadcast()), MainLoop() makes a virtual function
 * call HandleEvents() on each registred event table, effectivly handling
 * all pending events.
 *
 * Note: All here must be thread-safe and protected through mutexes.
 */
class EventMain {
public:
	//! Can be used to retrieve the only instance of this class
	static inline EventMain& const GetInstance() {
		static wxMutex s_eMainLock;
		static EventMain s_eMain;
		wxMutexLocker lock(s_eMainLock);
		return s_eMain;
	}
	//! Initialie this object
	static inline void Initialize() {
		GetInstance();
	}
	//! Add event table
	inline void AddEventTable(EventTableBase *et) {
		wxMutexLocker lock(m_listLock);
		m_list.push_back(et);
	}
	//! Remove event table
	inline void RemoveEventTable(EventTableBase *et) {
		wxMutexLocker lock(m_listLock);
		m_list.erase(et);
	}
	//! Enter main event loop. This runs until someone calls ExitMainLoop()
	void MainLoop() {
		while (true) {
			m_runningLock.Lock();
			if (!m_running) {
				m_runningLock.Unlock();
				return;
			}
			m_runningLock.Unlock();
			EventTableBase::WaitForEvent();
			for (LIter i = m_list.begin(); i != m_list.end(); i++) {
				(*i).HandleEvents();
			}
		}
	}
	//! Exit main loop. Notice that this function returns immediately, but
	//! application exit will be delayed until next event loop.
	void ExitMainLoop() {
		wxMutexLocker lock(m_runningLock);
		m_running = false;
		EventTableBase::Notify();
	}
private:
	std::list<EventTableBase*> m_list;    //!< List of event tables
	wxMutex m_listLock;                   //!< Protects the list
	typedef std::list<EventTableBase*>::iterator LIter;
	bool m_running;                       //!< Runs until this is true
	wxMutex m_runningLock;                //!< Protects the above bool

	EventMain() : m_running(true) { }     //!< Constructor
	EventMain(const EventMain&);          //!< Copying forbidden
	EventMain& operator=(EventMain&);     //!< Copying forbidden
	~EventMain() {}                       //!< Destructor
};
#endif


event.h
Created with C++BuilderX