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