| 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 |