OLD | NEW |
| (Empty) |
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_ | |
OLD | NEW |