Click here to Skip to main content
15,893,487 members
Articles / Programming Languages / Objective C

Generic Notifiers: Message Passing in C++

Rate me:
Please Sign up or sign in to vote.
4.28/5 (14 votes)
10 May 20045 min read 79.4K   1.8K   43  
An article that describes the design and implementation of synchronous/asynchronous communication between objects
/**
 * \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.


Written By
Web Developer
United States United States

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.


Comments and Discussions