Home | History | Annotate | Download | only in deprecated
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef CHROME_COMMON_DEPRECATED_EVENT_SYS_INL_H_
      6 #define CHROME_COMMON_DEPRECATED_EVENT_SYS_INL_H_
      7 #pragma once
      8 
      9 #include <map>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/logging.h"
     13 #include "base/message_loop.h"
     14 #include "base/port.h"
     15 #include "base/synchronization/condition_variable.h"
     16 #include "base/synchronization/lock.h"
     17 #include "chrome/common/deprecated/event_sys.h"
     18 
     19 // How to use Channels:
     20 
     21 // 0. Assume Bob is the name of the class from which you want to broadcast
     22 //    events.
     23 // 1. Choose an EventType. This could be an Enum or something more complicated.
     24 // 2. Create an EventTraits class for your EventType. It must have
     25 //    two members:
     26 //
     27 //      typedef x EventType;
     28 //      static bool IsChannelShutdownEvent(const EventType& event);
     29 //
     30 // 3. Add an EventChannel<MyEventTraits>* instance and event_channel() const;
     31 //    accessor to Bob.
     32 //    Delete the channel ordinarily in Bob's destructor, or whenever you want.
     33 // 4. To broadcast events, call bob->event_channel()->NotifyListeners(event).
     34 // 5. Only call NotifyListeners from a single thread at a time.
     35 
     36 // How to use Listeners/Hookups:
     37 
     38 // 0. Assume you want a class called Lisa to listen to events from Bob.
     39 // 1. Create an event handler method in Lisa. Its single argument should be of
     40 //    your event type.
     41 // 2. Add a EventListenerHookup* hookup_ member to Lisa.
     42 // 3. Initialize the hookup by calling:
     43 //
     44 //      hookup_ = NewEventListenerHookup(bob->event_channel(),
     45 //                                       this,
     46 //                                       &Lisa::HandleEvent);
     47 //
     48 // 4. Delete hookup_ in Lisa's destructor, or anytime sooner to stop receiving
     49 //    events.
     50 
     51 // An Event Channel is a source, or broadcaster of events. Listeners subscribe
     52 // by calling the AddListener() method. The owner of the channel calls the
     53 // NotifyListeners() method.
     54 //
     55 // Don't inherit from this class. Just make an event_channel member and an
     56 // event_channel() accessor.
     57 
     58 // No reason why CallbackWaiters has to be templatized.
     59 class CallbackWaiters {
     60  public:
     61   CallbackWaiters() : waiter_count_(0),
     62                       callback_done_(false),
     63                       condvar_(&mutex_) {
     64   }
     65   ~CallbackWaiters() {
     66     DCHECK_EQ(0, waiter_count_);
     67   }
     68   void WaitForCallbackToComplete(base::Lock* listeners_mutex) {
     69     {
     70       base::AutoLock lock(mutex_);
     71       waiter_count_ += 1;
     72       listeners_mutex->Release();
     73       while (!callback_done_)
     74         condvar_.Wait();
     75       waiter_count_ -= 1;
     76       if (0 != waiter_count_)
     77         return;
     78     }
     79     delete this;
     80   }
     81 
     82   void Signal() {
     83     base::AutoLock lock(mutex_);
     84     callback_done_ = true;
     85     condvar_.Broadcast();
     86   }
     87 
     88  protected:
     89   int waiter_count_;
     90   bool callback_done_;
     91   base::Lock mutex_;
     92   base::ConditionVariable condvar_;
     93 };
     94 
     95 template <typename EventTraitsType, typename NotifyLock,
     96           typename ScopedNotifyLocker>
     97 class EventChannel {
     98  public:
     99   typedef EventTraitsType EventTraits;
    100   typedef typename EventTraits::EventType EventType;
    101   typedef EventListener<EventType> Listener;
    102 
    103  protected:
    104   typedef std::map<Listener*, bool> Listeners;
    105 
    106  public:
    107   // The shutdown event gets send in the EventChannel's destructor.
    108   explicit EventChannel(const EventType& shutdown_event)
    109     : current_listener_callback_(NULL),
    110       current_listener_callback_message_loop_(NULL),
    111       callback_waiters_(NULL),
    112       shutdown_event_(shutdown_event) {
    113   }
    114 
    115   ~EventChannel() {
    116     // Tell all the listeners that the channel is being deleted.
    117     NotifyListeners(shutdown_event_);
    118 
    119     // Make sure all the listeners have been disconnected. Otherwise, they
    120     // will try to call RemoveListener() at a later date.
    121 #if defined(DEBUG)
    122     base::AutoLock lock(listeners_mutex_);
    123     for (typename Listeners::iterator i = listeners_.begin();
    124          i != listeners_.end(); ++i) {
    125       DCHECK(i->second) << "Listener not disconnected";
    126     }
    127 #endif
    128   }
    129 
    130   // Never call this twice for the same listener.
    131   //
    132   // Thread safe.
    133   void AddListener(Listener* listener) {
    134     base::AutoLock lock(listeners_mutex_);
    135     typename Listeners::iterator found = listeners_.find(listener);
    136     if (found == listeners_.end()) {
    137       listeners_.insert(std::make_pair(listener,
    138                                        false));  // Not dead yet.
    139     } else {
    140       DCHECK(found->second) << "Attempted to add the same listener twice.";
    141       found->second = false;  // Not dead yet.
    142     }
    143   }
    144 
    145   // If listener's callback is currently executing, this method waits until the
    146   // callback completes before returning.
    147   //
    148   // Thread safe.
    149   void RemoveListener(Listener* listener) {
    150     bool wait = false;
    151     listeners_mutex_.Acquire();
    152     typename Listeners::iterator found = listeners_.find(listener);
    153     if (found != listeners_.end()) {
    154       found->second = true;  // Mark as dead.
    155       wait = (found->first == current_listener_callback_ &&
    156           (MessageLoop::current() != current_listener_callback_message_loop_));
    157     }
    158     if (!wait) {
    159       listeners_mutex_.Release();
    160       return;
    161     }
    162     if (NULL == callback_waiters_)
    163       callback_waiters_ = new CallbackWaiters;
    164     callback_waiters_->WaitForCallbackToComplete(&listeners_mutex_);
    165   }
    166 
    167   // Blocks until all listeners have been notified.
    168   //
    169   // NOT thread safe.  Must only be called by one thread at a time.
    170   void NotifyListeners(const EventType& event) {
    171     ScopedNotifyLocker lock_notify(notify_lock_);
    172     listeners_mutex_.Acquire();
    173     DCHECK(NULL == current_listener_callback_);
    174     current_listener_callback_message_loop_ = MessageLoop::current();
    175     typename Listeners::iterator i = listeners_.begin();
    176     while (i != listeners_.end()) {
    177       if (i->second) {  // Clean out dead listeners
    178         listeners_.erase(i++);
    179         continue;
    180       }
    181       current_listener_callback_ = i->first;
    182       listeners_mutex_.Release();
    183 
    184       i->first->HandleEvent(event);
    185 
    186       listeners_mutex_.Acquire();
    187       current_listener_callback_ = NULL;
    188       if (NULL != callback_waiters_) {
    189         callback_waiters_->Signal();
    190         callback_waiters_ = NULL;
    191       }
    192 
    193       ++i;
    194     }
    195     listeners_mutex_.Release();
    196   }
    197 
    198   // A map iterator remains valid until the element it points to gets removed
    199   // from the map, so a map is perfect for our needs.
    200   //
    201   // Map value is a bool, true means the Listener is dead.
    202   Listeners listeners_;
    203   // NULL means no callback is currently being called.
    204   Listener* current_listener_callback_;
    205   // Only valid when current_listener is not NULL.
    206   // The thread on which the callback is executing.
    207   MessageLoop* current_listener_callback_message_loop_;
    208   // Win32 Event that is usually NULL. Only created when another thread calls
    209   // Remove while in callback. Owned and closed by the thread calling Remove().
    210   CallbackWaiters* callback_waiters_;
    211 
    212   base::Lock listeners_mutex_;  // Protects all members above.
    213   const EventType shutdown_event_;
    214   NotifyLock notify_lock_;
    215 
    216   DISALLOW_COPY_AND_ASSIGN(EventChannel);
    217 };
    218 
    219 // An EventListenerHookup hooks up a method in your class to an EventChannel.
    220 // Deleting the hookup disconnects from the EventChannel.
    221 //
    222 // Contains complexity of inheriting from Listener class and managing lifetimes.
    223 //
    224 // Create using NewEventListenerHookup() to avoid explicit template arguments.
    225 class EventListenerHookup {
    226  public:
    227   virtual ~EventListenerHookup() { }
    228 };
    229 
    230 template <typename EventChannel, typename EventTraits,
    231           class Derived>
    232 class EventListenerHookupImpl : public EventListenerHookup,
    233 public EventListener<typename EventTraits::EventType> {
    234  public:
    235   explicit EventListenerHookupImpl(EventChannel* channel)
    236     : channel_(channel), deleted_(NULL) {
    237     channel->AddListener(this);
    238     connected_ = true;
    239   }
    240 
    241   ~EventListenerHookupImpl() {
    242     if (NULL != deleted_)
    243       *deleted_ = true;
    244     if (connected_)
    245       channel_->RemoveListener(this);
    246   }
    247 
    248   typedef typename EventTraits::EventType EventType;
    249   virtual void HandleEvent(const EventType& event) {
    250     DCHECK(connected_);
    251     bool deleted = false;
    252     deleted_ = &deleted;
    253     static_cast<Derived*>(this)->Callback(event);
    254     if (deleted)  // The callback (legally) deleted this.
    255       return;  // The only safe thing to do.
    256     deleted_ = NULL;
    257     if (EventTraits::IsChannelShutdownEvent(event)) {
    258       channel_->RemoveListener(this);
    259       connected_ = false;
    260     }
    261   }
    262 
    263  protected:
    264   EventChannel* const channel_;
    265   bool connected_;
    266   bool* deleted_;  // Allows the handler to delete the hookup.
    267 };
    268 
    269 // SimpleHookup just passes the event to the callback message.
    270 template <typename EventChannel, typename EventTraits,
    271           typename CallbackObject, typename CallbackMethod>
    272 class SimpleHookup
    273     : public EventListenerHookupImpl<EventChannel, EventTraits,
    274                                      SimpleHookup<EventChannel,
    275                                                   EventTraits,
    276                                                   CallbackObject,
    277                                                   CallbackMethod> > {
    278  public:
    279   SimpleHookup(EventChannel* channel, CallbackObject* cbobject,
    280                CallbackMethod cbmethod)
    281     : EventListenerHookupImpl<EventChannel, EventTraits,
    282                               SimpleHookup>(channel), cbobject_(cbobject),
    283     cbmethod_(cbmethod) { }
    284 
    285   typedef typename EventTraits::EventType EventType;
    286   void Callback(const EventType& event) {
    287     (cbobject_->*cbmethod_)(event);
    288   }
    289   CallbackObject* const cbobject_;
    290   CallbackMethod const cbmethod_;
    291 };
    292 
    293 // ArgHookup also passes an additional arg to the callback method.
    294 template <typename EventChannel, typename EventTraits,
    295           typename CallbackObject, typename CallbackMethod,
    296           typename CallbackArg0>
    297 class ArgHookup
    298     : public EventListenerHookupImpl<EventChannel, EventTraits,
    299                                      ArgHookup<EventChannel, EventTraits,
    300                                                CallbackObject,
    301                                                CallbackMethod,
    302                                                CallbackArg0> > {
    303  public:
    304   ArgHookup(EventChannel* channel, CallbackObject* cbobject,
    305             CallbackMethod cbmethod, CallbackArg0 arg0)
    306     : EventListenerHookupImpl<EventChannel, EventTraits,
    307                               ArgHookup>(channel), cbobject_(cbobject),
    308       cbmethod_(cbmethod), arg0_(arg0) { }
    309 
    310   typedef typename EventTraits::EventType EventType;
    311   void Callback(const EventType& event) {
    312     (cbobject_->*cbmethod_)(arg0_, event);
    313   }
    314   CallbackObject* const cbobject_;
    315   CallbackMethod const cbmethod_;
    316   CallbackArg0 const arg0_;
    317 };
    318 
    319 
    320 template <typename EventChannel, typename CallbackObject,
    321           typename CallbackMethod>
    322 EventListenerHookup* NewEventListenerHookup(EventChannel* channel,
    323                                             CallbackObject* cbobject,
    324                                             CallbackMethod cbmethod) {
    325   return new SimpleHookup<EventChannel,
    326     typename EventChannel::EventTraits,
    327     CallbackObject, CallbackMethod>(channel, cbobject, cbmethod);
    328 }
    329 
    330 template <typename EventChannel, typename CallbackObject,
    331           typename CallbackMethod, typename CallbackArg0>
    332 EventListenerHookup* NewEventListenerHookup(EventChannel* channel,
    333                                             CallbackObject* cbobject,
    334                                             CallbackMethod cbmethod,
    335                                             CallbackArg0 arg0) {
    336   return new ArgHookup<EventChannel,
    337     typename EventChannel::EventTraits,
    338     CallbackObject, CallbackMethod, CallbackArg0>(channel, cbobject,
    339                                                   cbmethod, arg0);
    340 }
    341 
    342 #endif  // CHROME_COMMON_DEPRECATED_EVENT_SYS_INL_H_
    343