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