• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
CallbackWaiters()61   CallbackWaiters() : waiter_count_(0),
62                       callback_done_(false),
63                       condvar_(&mutex_) {
64   }
~CallbackWaiters()65   ~CallbackWaiters() {
66     DCHECK_EQ(0, waiter_count_);
67   }
WaitForCallbackToComplete(base::Lock * listeners_mutex)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 
Signal()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.
EventChannel(const EventType & shutdown_event)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 
~EventChannel()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.
AddListener(Listener * listener)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.
RemoveListener(Listener * listener)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.
NotifyListeners(const EventType & event)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:
~EventListenerHookup()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:
EventListenerHookupImpl(EventChannel * channel)235   explicit EventListenerHookupImpl(EventChannel* channel)
236     : channel_(channel), deleted_(NULL) {
237     channel->AddListener(this);
238     connected_ = true;
239   }
240 
~EventListenerHookupImpl()241   ~EventListenerHookupImpl() {
242     if (NULL != deleted_)
243       *deleted_ = true;
244     if (connected_)
245       channel_->RemoveListener(this);
246   }
247 
248   typedef typename EventTraits::EventType EventType;
HandleEvent(const EventType & event)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:
SimpleHookup(EventChannel * channel,CallbackObject * cbobject,CallbackMethod cbmethod)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;
Callback(const EventType & event)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:
ArgHookup(EventChannel * channel,CallbackObject * cbobject,CallbackMethod cbmethod,CallbackArg0 arg0)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;
Callback(const EventType & event)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>
NewEventListenerHookup(EventChannel * channel,CallbackObject * cbobject,CallbackMethod cbmethod)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>
NewEventListenerHookup(EventChannel * channel,CallbackObject * cbobject,CallbackMethod cbmethod,CallbackArg0 arg0)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