1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#ifndef CHROME_COMMON_DEPRECATED_EVENT_SYS_INL_H_
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#define CHROME_COMMON_DEPRECATED_EVENT_SYS_INL_H_
73345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#pragma once
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <map>
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/basictypes.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/message_loop.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/port.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/synchronization/condition_variable.h"
1621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "base/synchronization/lock.h"
1721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/common/deprecated/event_sys.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// How to use Channels:
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 0. Assume Bob is the name of the class from which you want to broadcast
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//    events.
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 1. Choose an EventType. This could be an Enum or something more complicated.
2421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// 2. Create an EventTraits class for your EventType. It must have
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//    two members:
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//      typedef x EventType;
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//      static bool IsChannelShutdownEvent(const EventType& event);
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 3. Add an EventChannel<MyEventTraits>* instance and event_channel() const;
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//    accessor to Bob.
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//    Delete the channel ordinarily in Bob's destructor, or whenever you want.
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 4. To broadcast events, call bob->event_channel()->NotifyListeners(event).
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 5. Only call NotifyListeners from a single thread at a time.
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// How to use Listeners/Hookups:
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 0. Assume you want a class called Lisa to listen to events from Bob.
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 1. Create an event handler method in Lisa. Its single argument should be of
4021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen//    your event type.
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 2. Add a EventListenerHookup* hookup_ member to Lisa.
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 3. Initialize the hookup by calling:
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//      hookup_ = NewEventListenerHookup(bob->event_channel(),
4521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen//                                       this,
4621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen//                                       &Lisa::HandleEvent);
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
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.
59class 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
95template <typename EventTraitsType, typename NotifyLock,
96          typename ScopedNotifyLocker>
97class 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.
225class EventListenerHookup {
226 public:
227  virtual ~EventListenerHookup() { }
228};
229
230template <typename EventChannel, typename EventTraits,
231          class Derived>
232class EventListenerHookupImpl : public EventListenerHookup,
233public 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.
270template <typename EventChannel, typename EventTraits,
271          typename CallbackObject, typename CallbackMethod>
272class 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.
294template <typename EventChannel, typename EventTraits,
295          typename CallbackObject, typename CallbackMethod,
296          typename CallbackArg0>
297class 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
320template <typename EventChannel, typename CallbackObject,
321          typename CallbackMethod>
322EventListenerHookup* 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
330template <typename EventChannel, typename CallbackObject,
331          typename CallbackMethod, typename CallbackArg0>
332EventListenerHookup* 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