|
/**
* \file notifier.h
* \author Plugware Solutions.com, Ltd.
* \date
* \brief [brief here]
*
* Copyright (c) 2003
* Plugware Solutions.com, Ltd.
*
* Permission to use, copy, modify, distribute and sell
* this software and its documentation for any purpose
* is hereby granted without fee, provided that the above
* copyright notice appear in all copies and the both that
* copyright notice and this permission notice appear
* in supporting documentation. Plugware Solutions.com, Ltd.
* makes no reresentations about the suitability of this
* software for any purpose. It is provided "as is"
* without express or implied warranty.
*/
#ifndef _plugware_notifier_h
#define _plugware_notifier_h
/*
** Includes
*/
#include <map>
#include "thread_pool.h"
/*
** Declarations
*/
namespace plugware
{
template <typename T_state> class notifier;
/**
* \class subscriber
* \author Plugware Solutions.com, Ltd.
* \date
* \brief [brief here]
*
* [details here]
*/
template <typename T_state>
struct subscriber
{
typedef T_state state;
typedef notifier<T_state> notifier;
void subscribe();
void unsubscribe();
virtual ~subscriber();
virtual void on_notify(const state&) = 0;
}; // struct subscriber
/**
* \class notifier
* \author Plugware Solutions.com, Ltd.
* \date
* \brief [brief here]
*
* [details here]
*/
template <typename T_state>
class notifier : public singleton<notifier>
{
protected: // implementation
friend struct singleton;
notifier();
~notifier();
public: // interface
typedef subscriber<T_state> subscriber;
typedef std::map<subscriber*, bool> subscriber_map;
void register_subscriber(subscriber* p_subscriber);
void unregister_subscriber(subscriber* p_subscriber);
// modifiers
void notify(const T_state& state);
private: // implementation
// modifiers
static void notify_subscriber(subscriber*p_subscriber, const T_state& state);
void cull_unregistered_subscribers();
// accessors
critical_section& get_lock() { return m_c_lock; }
subscriber_map& get_subscriber_map() { return m_c_subscribers; }
// const accessors
const & get_lock() const { return m_c_lock; }
const subscriber_map& get_subscriber_map() const { return m_c_subscribers; }
private: // data
critical_section m_c_lock;
subscriber_map m_c_subscribers;
}; // class notifier
template <typename T_state>
struct async_notifier : core::work_unit
{
async_notifier(const T_state&);
void process();
T_state m_state;
};
/*
** Implementation
*/
///////////////////////////////////////////////////////////////////////
// subscriber
///////////////////////////////////////////////////////////////////////
template <typename T_state>
subscriber<T_state>::~subscriber()
{
}
template <typename T_state>
void subscriber<T_state>::unsubscribe()
{
notifier::instance().unregister_subscriber(this);
}
template <typename T_state>
void subscriber<T_state>::subscribe()
{
notifier::instance().register_subscriber(this);
}
///////////////////////////////////////////////////////////////////////
// notifier
///////////////////////////////////////////////////////////////////////
template <typename T_state>
notifier<T_state>::notifier()
{
}
template <typename T_state>
notifier<T_state>::~notifier()
{
// whats the point of culling when our map is going to clear
// a LOT of timing issues can cause problems here ...
// diagnostics would be the only reason to turn this on;
// if that's the case, don't forget to sleep.
//::Sleep(1000);
//cull_unregistered_subscribers();
//_ASSERT(true == get_subscriber_map().empty()); // someone didn't unregister
}
template <typename T_state>
void notifier<T_state>::register_subscriber(subscriber* p_subscriber)
{
// validate, lock, register and unlock
if (0 == p_subscriber) throw std::invalid_argument("notifier<T_state>::register_subscriber: subscriber");
// WARNING: if you are being notified and you register
// subscribers on the same thread being notified, this will not lock:
// this may result in unintended consequences
auto_lock lock(get_lock());
std::pair<subscriber_map::iterator, bool> c_pair(
get_subscriber_map().insert(subscriber_map::value_type(p_subscriber, true)));
// if it is already in the map and it is ready to be unregistered, register it
if (false == c_pair.second && false == c_pair.first->second)
{
c_pair.first->second = true;
}
}
template <typename T_state>
void notifier<T_state>::unregister_subscriber(subscriber* p_subscriber)
{
// validate
if (0 == p_subscriber) throw std::invalid_argument("notifier<T_state>::unregister_subscriber: subscriber");
// find subscriber; not found return, found flag for removal
subscriber_map::iterator c_subscriber(get_subscriber_map().find(p_subscriber));
if (c_subscriber == get_subscriber_map().end()) return;
c_subscriber->second = false;
}
template <typename T_state>
void notifier<T_state>::notify(const T_state& state)
{
// for each *REGISTERED* subscriber
// (Reminder: subscribers are not registered automatically)
auto_lock lock(get_lock());
for (subscriber_map::iterator c_subscriber(get_subscriber_map().begin());
c_subscriber != get_subscriber_map().end();
++c_subscriber)
{
// if subscriber is registered notify
if (true == c_subscriber->second)
{
notify_subscriber(c_subscriber->first, state);
}
}
// remove all subscribers unregistered during notification
// note that this is not the most efficient way, as
// it forces us to walk the subscriber map twice per notification ...
cull_unregistered_subscribers();
}
template <typename T_state>
void notifier<T_state>::notify_subscriber(subscriber*p_subscriber, const T_state& state)
{
try
{
// validate and notify
if (0 == p_subscriber) throw std::invalid_argument("notifier<T_state>::notify_subscriber: subscriber");
p_subscriber->on_notify(state);
}
catch(...) {}
}
template <typename T_state>
void notifier<T_state>::cull_unregistered_subscribers()
{
// if not empty, lock and cull
if (true == get_subscriber_map().empty()) return;
auto_lock lock(get_lock());
for (subscriber_map::iterator c_subscriber(get_subscriber_map().begin());
c_subscriber != get_subscriber_map().end();)
{
// if subscriber has been unregistered
if (false == c_subscriber->second)
{
// remove it
c_subscriber = get_subscriber_map().erase(c_subscriber);
}
// otherwise we'll check the next
else ++c_subscriber;
}
}
///////////////////////////////////////////////////////////////////////
// async_notifier
///////////////////////////////////////////////////////////////////////
template <typename T_state>
async_notifier<T_state>::async_notifier(const T_state& state)
: m_state(state)
{
}
template <typename T_state>
void async_notifier<T_state>::process()
{
notifier<T_state>::instance().notify(m_state);
}
} // namespace plugware
#endif //_plugware_notifier_h
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
Joshua Emele lives in San Francisco. A member of
Plugware Solutions, Ltd. and specializes in network, database, and workflow applications in c++.
He is madly in love with life and his partner and enjoys teaching, playing classical guitar,
hiking, and digital electronics.
Plugware Solutions, Ltd. provides design, review, integration and implementation
consulting services and is the maker of the Plugware Web Services Platform.