interreality.org [VOS]
[Home] [About]
[Screenshots]
[Download]
[News]
[Community]
[Documentation] [Manual]
[Bugs & Requests] [Wiki]

/home/tetron/hack/vos/libs/vos/vutil/listener.hh

Go to the documentation of this file.
00001 /*
00002     This file is part of the Virtual Object System of
00003     the Interreality project (http://interreality.org).
00004 
00005     Copyright (C) 2001-2003 Peter Amstutz
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Lesser General Public
00009     License as published by the Free Software Foundation; either
00010     version 2 of the License, or (at your option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Lesser General Public License for more details.
00016 
00017     You should have received a copy of the GNU Lesser General Public
00018     License along with this library; if not, write to the Free Software
00019     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
00020 
00021     Peter Amstutz <http://www.interreality.org>
00022 */
00023 #ifndef _VUTIL_LISTENER_HH_
00024 #define _VUTIL_LISTENER_HH_
00025 
00026 /** @file
00027     Defines ChildChangeListener, ParentChangeListener, and TypeChangeListener.
00028 */
00029 
00030 #include <vos/vutil/vutildefs.hh>
00031 #include <vos/vutil/refcount.hh>
00032 #include <vos/vutil/taskqueue.hh>
00033 
00034 #include <string>
00035 
00036 namespace VUtil
00037 {
00038     /** @class ListenerTask listener.hh vos/vos/listener.hh
00039         @ingroup libvutil
00040 
00041         Template class used to define the task of delivering a
00042         particular event object to a particular listener object.  An
00043         Event class supplied to this template must implement the
00044         following method, which makes the event deliver itself to the
00045         supplied Listener:
00046 
00047         @code
00048         void Event::deliverTo(Listener*);
00049         @endcode
00050     */
00051     template<class Listener, class Event> class ListenerTask : public Task
00052     {
00053     private:
00054         Listener* l;
00055         vRef<Event> e;
00056 
00057     public:
00058         /** Constructor.
00059             @param li the listener object to deliver the event to
00060             @param ev the event to be delivered
00061         */
00062         ListenerTask(Listener* li, Event* ev) : l(li), e(ev, true) {
00063             if(RefCounted* rc = dynamic_cast<RefCounted*>(l)) {
00064                 rc->acquire();
00065             }
00066         }
00067 
00068         /** The actual task.  Because this is a generic template, we
00069             don't know ahead of time what method on the listener we
00070             actually want to call.  So we call the method deliverTo()
00071             on the event, and pass it the listener object; this has
00072             event deliver itself to the listener by calling the
00073             appropriate method on the listener.
00074          */
00075         virtual void doTask() {
00076             e->deliverTo(l);
00077         }
00078 
00079         /** Destructor */
00080         virtual ~ListenerTask() {
00081             if(RefCounted* rc = dynamic_cast<RefCounted*>(l)) {
00082                 rc->release();
00083             }
00084         }
00085     };
00086 
00087     /** @class ListenerBase listener.hh vos/vos/listener.hh
00088         @ingroup libvutil
00089 
00090         Template base class for the listener pattern.  This manages a
00091         list of pointers to listeners to be notified when
00092         distributeEvent() is called.  This class is threadsafe.
00093     */
00094     template<class Listener, class Event> class ListenerBase : public ObjectExciseListener
00095     {
00096     private:
00097         boost::mutex listeners_mutex;
00098         std::set<Listener*> listeners;
00099         bool inClear;
00100         bool async;
00101 
00102     public:
00103         /** Constructor */
00104         ListenerBase() : async(true) { }
00105 
00106         /** Destructor.  Cleans up the listener list. */
00107         virtual ~ListenerBase() {
00108             clearListeners();
00109         }
00110 
00111         /** Add a listener.  Knows about RefCounted objects (they will
00112             be acquired and an excise listener added).
00113 
00114             @param l the listener object to add.
00115         */
00116         void addListener(Listener* l) {
00117             boost::mutex::scoped_lock lk(listeners_mutex);
00118 
00119             if(listeners.count(l) == 0) {
00120                 if(RefCounted* rc = dynamic_cast<RefCounted*>(l)) {
00121                     rc->acquire();
00122                     rc->addExciseListener(this);
00123                 }
00124                 listeners.insert(l);
00125             }
00126         }
00127 
00128         /** Remove a listener.  Knows about RefCounted objects (they
00129             will be released and the excise listener removed).
00130             @param l the listener to remove.
00131          */
00132         void removeListener(Listener* l) {
00133             if(! inClear) {
00134                 boost::mutex::scoped_lock lk(listeners_mutex);
00135 
00136                 if(listeners.count(l) > 0) {
00137                     listeners.erase(l);
00138                     if(RefCounted* rc = dynamic_cast<RefCounted*>(l)) {
00139                         rc->removeExciseListener(this);
00140                         rc->release();
00141                     }
00142                 }
00143             }
00144         }
00145 
00146         /** Remove all listeners.  Knows about RefCounted objects
00147             (they will be released and the excise listener removed).
00148          */
00149         void clearListeners() {
00150             boost::mutex::scoped_lock lk(listeners_mutex);
00151             inClear = true;
00152 
00153             for(typename std::set<Listener*>::iterator i = listeners.begin();
00154                 i != listeners.end();
00155                 i++)
00156             {
00157                 if(RefCounted* rc = dynamic_cast<RefCounted*>(*i)) {
00158                     rc->removeExciseListener(this);
00159                     rc->release();
00160                 }
00161             }
00162             listeners.clear();
00163 
00164             inClear = false;
00165         }
00166 
00167         /** For each listener, queues up a task to deliver the
00168             supplied event.  Note that these tasks are added to the
00169             default TaskQueue and as such will be delivered in a
00170             separate thread.  This means there is no guarantee that
00171             then event delivery will have have completed (or even
00172             started) by the time this method returns!
00173 
00174             @param e the event to be distributed
00175          */
00176         void distributeEvent(Event* e) {
00177             boost::mutex::scoped_lock lk(listeners_mutex);
00178 
00179             if (async) {
00180                 for(typename std::set<Listener*>::iterator i = listeners.begin();
00181                     i != listeners.end();
00182                     i++)
00183                 {
00184                     TaskQueue::defaultTQ().addTask(new ListenerTask<Listener, Event>(*i, e));
00185                 }
00186             } else {
00187                 std::set<Listener*> templisteners = listeners;
00188 
00189                 lk.unlock();
00190 
00191                 for(typename std::set<Listener*>::iterator i = templisteners.begin();
00192                     i != templisteners.end();
00193                     i++)
00194                 {
00195                     e->deliverTo(*i);
00196                 }
00197             }
00198         }
00199 
00200         /** Callback called by RefCounted::excise() indicating the
00201             supplied object wants to go away.  This remove the object
00202             from the listener list.
00203 
00204             @param object
00205          */
00206         void notifyObjectExcise(RefCounted* object) {
00207             if(Listener* l = dynamic_cast<Listener*>(object)) {
00208                 removeListener(l);
00209             }
00210         }
00211 
00212         /** @return the number of listeners */
00213         unsigned int listenerCount() {
00214             boost::mutex::scoped_lock lk(listeners_mutex);
00215             return listeners.size();
00216         }
00217 
00218         /** @return true if the supplied object is present in the
00219             listener list
00220             @param l the listener in question
00221          */
00222         bool hasListener(Listener* l) {
00223             boost::mutex::scoped_lock lk(listeners_mutex);
00224             return (listeners.count(l) > 0);
00225         }
00226 
00227         /** @return true if event distribution is asynchronous.  If
00228             true event callbacks are pushed onto the task queue and
00229             distributeEvents() returns immediatly.  This should never
00230             block and is the default behavior.  If false, it will call
00231             event listeners synchronously in the thread of the caller
00232             (potentially blocking).
00233 
00234          */
00235         bool getAsynchronousEvents() {
00236             return async;
00237         }
00238 
00239         /** @param b True if event distribution is asynchronous.  If
00240             true event callbacks are pushed onto the task queue and
00241             distributeEvents() returns immediatly.  This should never
00242             block and is the default behavior.  If false, it will call
00243             event listeners synchronously in the thread of the caller
00244             (potentially blocking).
00245 
00246          */
00247         void setAsynchronousEvents(bool b) {
00248             async = b;
00249         }
00250     };
00251 }
00252 
00253 #endif