| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_ | 5 #ifndef CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_ |
| 6 #define CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_ | 6 #define CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_ |
| 7 | 7 |
| 8 #include <map> | 8 #include <map> |
| 9 | 9 |
| 10 #include "base/atomicops.h" | 10 #include "base/atomicops.h" |
| 11 #include "base/basictypes.h" | 11 #include "base/basictypes.h" |
| 12 #include "base/condition_variable.h" |
| 13 #include "base/lock.h" |
| 12 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/message_loop.h" |
| 13 #include "base/port.h" | 16 #include "base/port.h" |
| 14 #include "chrome/browser/sync/util/compat_pthread.h" | |
| 15 #include "chrome/browser/sync/util/event_sys.h" | 17 #include "chrome/browser/sync/util/event_sys.h" |
| 16 #include "chrome/browser/sync/util/pthread_helpers.h" | |
| 17 #include "chrome/browser/sync/util/sync_types.h" | 18 #include "chrome/browser/sync/util/sync_types.h" |
| 18 | 19 |
| 19 // How to use Channels: | 20 // How to use Channels: |
| 20 | 21 |
| 21 // 0. Assume Bob is the name of the class from which you want to broadcast | 22 // 0. Assume Bob is the name of the class from which you want to broadcast |
| 22 // events. | 23 // events. |
| 23 // 1. Choose an EventType. This could be an Enum or something more complicated. | 24 // 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 // 2. Create an EventTraits class for your EventType. It must have |
| 25 // two members: | 26 // two members: |
| 26 // | 27 // |
| (...skipping 24 matching lines...) Expand all Loading... |
| 51 // An Event Channel is a source, or broadcaster of events. Listeners subscribe | 52 // 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 // by calling the AddListener() method. The owner of the channel calls the |
| 53 // NotifyListeners() method. | 54 // NotifyListeners() method. |
| 54 // | 55 // |
| 55 // Don't inherit from this class. Just make an event_channel member and an | 56 // Don't inherit from this class. Just make an event_channel member and an |
| 56 // event_channel() accessor. | 57 // event_channel() accessor. |
| 57 | 58 |
| 58 // No reason why CallbackWaiters has to be templatized. | 59 // No reason why CallbackWaiters has to be templatized. |
| 59 class CallbackWaiters { | 60 class CallbackWaiters { |
| 60 public: | 61 public: |
| 61 CallbackWaiters() : waiter_count_(0), callback_done_(false) { | 62 CallbackWaiters() : waiter_count_(0), |
| 63 callback_done_(false), |
| 64 condvar_(&mutex_) { |
| 62 } | 65 } |
| 63 ~CallbackWaiters() { | 66 ~CallbackWaiters() { |
| 64 DCHECK_EQ(0, waiter_count_); | 67 DCHECK_EQ(0, waiter_count_); |
| 65 } | 68 } |
| 66 void WaitForCallbackToComplete(PThreadMutex* listeners_mutex) { | 69 void WaitForCallbackToComplete(Lock* listeners_mutex) { |
| 67 { | 70 { |
| 68 PThreadScopedLock<PThreadMutex> lock(&mutex_); | 71 AutoLock lock(mutex_); |
| 69 waiter_count_ += 1; | 72 waiter_count_ += 1; |
| 70 listeners_mutex->Unlock(); | 73 listeners_mutex->Release(); |
| 71 while (!callback_done_) | 74 while (!callback_done_) |
| 72 pthread_cond_wait(&condvar_.condvar_, &mutex_.mutex_); | 75 condvar_.Wait(); |
| 73 waiter_count_ -= 1; | 76 waiter_count_ -= 1; |
| 74 if (0 != waiter_count_) | 77 if (0 != waiter_count_) |
| 75 return; | 78 return; |
| 76 } | 79 } |
| 77 delete this; | 80 delete this; |
| 78 } | 81 } |
| 79 | 82 |
| 80 void Signal() { | 83 void Signal() { |
| 81 PThreadScopedLock<PThreadMutex> lock(&mutex_); | 84 AutoLock lock(mutex_); |
| 82 callback_done_ = true; | 85 callback_done_ = true; |
| 83 pthread_cond_broadcast(&condvar_.condvar_); | 86 condvar_.Broadcast(); |
| 84 } | 87 } |
| 85 | 88 |
| 86 protected: | 89 protected: |
| 87 int waiter_count_; | 90 int waiter_count_; |
| 88 bool callback_done_; | 91 bool callback_done_; |
| 89 PThreadMutex mutex_; | 92 Lock mutex_; |
| 90 PThreadCondVar condvar_; | 93 ConditionVariable condvar_; |
| 91 }; | 94 }; |
| 92 | 95 |
| 93 template <typename EventTraitsType, typename NotifyLock, | 96 template <typename EventTraitsType, typename NotifyLock, |
| 94 typename ScopedNotifyLocker> | 97 typename ScopedNotifyLocker> |
| 95 class EventChannel { | 98 class EventChannel { |
| 96 public: | 99 public: |
| 97 typedef EventTraitsType EventTraits; | 100 typedef EventTraitsType EventTraits; |
| 98 typedef typename EventTraits::EventType EventType; | 101 typedef typename EventTraits::EventType EventType; |
| 99 typedef EventListener<EventType> Listener; | 102 typedef EventListener<EventType> Listener; |
| 100 | 103 |
| 101 protected: | 104 protected: |
| 102 typedef std::map<Listener*, bool> Listeners; | 105 typedef std::map<Listener*, bool> Listeners; |
| 103 typedef PThreadScopedLock<PThreadMutex> ScopedListenersLock; | |
| 104 | 106 |
| 105 public: | 107 public: |
| 106 // The shutdown event gets send in the EventChannel's destructor. | 108 // The shutdown event gets send in the EventChannel's destructor. |
| 107 explicit EventChannel(const EventType& shutdown_event) | 109 explicit EventChannel(const EventType& shutdown_event) |
| 108 : current_listener_callback_(NULL), | 110 : current_listener_callback_(NULL), |
| 109 callback_waiters_(NULL), | 111 callback_waiters_(NULL), |
| 110 shutdown_event_(shutdown_event) { | 112 shutdown_event_(shutdown_event) { |
| 111 } | 113 } |
| 112 | 114 |
| 113 ~EventChannel() { | 115 ~EventChannel() { |
| 114 // Tell all the listeners that the channel is being deleted. | 116 // Tell all the listeners that the channel is being deleted. |
| 115 NotifyListeners(shutdown_event_); | 117 NotifyListeners(shutdown_event_); |
| 116 | 118 |
| 117 // Make sure all the listeners have been disconnected. Otherwise, they | 119 // Make sure all the listeners have been disconnected. Otherwise, they |
| 118 // will try to call RemoveListener() at a later date. | 120 // will try to call RemoveListener() at a later date. |
| 119 #ifdef DEBUG | 121 #ifdef DEBUG |
| 120 ScopedListenersLock lock(&listeners_mutex_); | 122 AutoLock lock(listeners_mutex_); |
| 121 for (typename Listeners::iterator i = listeners_.begin(); | 123 for (typename Listeners::iterator i = listeners_.begin(); |
| 122 i != listeners_.end(); ++i) { | 124 i != listeners_.end(); ++i) { |
| 123 DCHECK(i->second) << "Listener not disconnected"; | 125 DCHECK(i->second) << "Listener not disconnected"; |
| 124 } | 126 } |
| 125 #endif | 127 #endif |
| 126 } | 128 } |
| 127 | 129 |
| 128 // Never call this twice for the same listener. | 130 // Never call this twice for the same listener. |
| 129 // | 131 // |
| 130 // Thread safe. | 132 // Thread safe. |
| 131 void AddListener(Listener* listener) { | 133 void AddListener(Listener* listener) { |
| 132 ScopedListenersLock lock(&listeners_mutex_); | 134 AutoLock lock(listeners_mutex_); |
| 133 typename Listeners::iterator found = listeners_.find(listener); | 135 typename Listeners::iterator found = listeners_.find(listener); |
| 134 if (found == listeners_.end()) { | 136 if (found == listeners_.end()) { |
| 135 listeners_.insert(std::make_pair(listener, | 137 listeners_.insert(std::make_pair(listener, |
| 136 false)); // Not dead yet. | 138 false)); // Not dead yet. |
| 137 } else { | 139 } else { |
| 138 DCHECK(found->second) << "Attempted to add the same listener twice."; | 140 DCHECK(found->second) << "Attempted to add the same listener twice."; |
| 139 found->second = false; // Not dead yet. | 141 found->second = false; // Not dead yet. |
| 140 } | 142 } |
| 141 } | 143 } |
| 142 | 144 |
| 143 // If listener's callback is currently executing, this method waits until the | 145 // If listener's callback is currently executing, this method waits until the |
| 144 // callback completes before returning. | 146 // callback completes before returning. |
| 145 // | 147 // |
| 146 // Thread safe. | 148 // Thread safe. |
| 147 void RemoveListener(Listener* listener) { | 149 void RemoveListener(Listener* listener) { |
| 148 bool wait = false; | 150 bool wait = false; |
| 149 listeners_mutex_.Lock(); | 151 listeners_mutex_.Acquire(); |
| 150 typename Listeners::iterator found = listeners_.find(listener); | 152 typename Listeners::iterator found = listeners_.find(listener); |
| 151 if (found != listeners_.end()) { | 153 if (found != listeners_.end()) { |
| 152 found->second = true; // Mark as dead. | 154 found->second = true; // Mark as dead. |
| 153 wait = (found->first == current_listener_callback_ && | 155 wait = (found->first == current_listener_callback_ && |
| 154 (!pthread_equal(current_listener_callback_thread_id_, | 156 (MessageLoop::current() != current_listener_callback_message_loop_)); |
| 155 pthread_self()))); | |
| 156 } | 157 } |
| 157 if (!wait) { | 158 if (!wait) { |
| 158 listeners_mutex_.Unlock(); | 159 listeners_mutex_.Release(); |
| 159 return; | 160 return; |
| 160 } | 161 } |
| 161 if (NULL == callback_waiters_) | 162 if (NULL == callback_waiters_) |
| 162 callback_waiters_ = new CallbackWaiters; | 163 callback_waiters_ = new CallbackWaiters; |
| 163 callback_waiters_->WaitForCallbackToComplete(&listeners_mutex_); | 164 callback_waiters_->WaitForCallbackToComplete(&listeners_mutex_); |
| 164 } | 165 } |
| 165 | 166 |
| 166 // Blocks until all listeners have been notified. | 167 // Blocks until all listeners have been notified. |
| 167 // | 168 // |
| 168 // NOT thread safe. Must only be called by one thread at a time. | 169 // NOT thread safe. Must only be called by one thread at a time. |
| 169 void NotifyListeners(const EventType& event) { | 170 void NotifyListeners(const EventType& event) { |
| 170 ScopedNotifyLocker lock_notify(¬ify_lock_); | 171 ScopedNotifyLocker lock_notify(notify_lock_); |
| 171 listeners_mutex_.Lock(); | 172 listeners_mutex_.Acquire(); |
| 172 DCHECK(NULL == current_listener_callback_); | 173 DCHECK(NULL == current_listener_callback_); |
| 173 current_listener_callback_thread_id_ = pthread_self(); | 174 current_listener_callback_message_loop_ = MessageLoop::current(); |
| 174 typename Listeners::iterator i = listeners_.begin(); | 175 typename Listeners::iterator i = listeners_.begin(); |
| 175 while (i != listeners_.end()) { | 176 while (i != listeners_.end()) { |
| 176 if (i->second) { // Clean out dead listeners | 177 if (i->second) { // Clean out dead listeners |
| 177 listeners_.erase(i++); | 178 listeners_.erase(i++); |
| 178 continue; | 179 continue; |
| 179 } | 180 } |
| 180 current_listener_callback_ = i->first; | 181 current_listener_callback_ = i->first; |
| 181 listeners_mutex_.Unlock(); | 182 listeners_mutex_.Release(); |
| 182 | 183 |
| 183 i->first->HandleEvent(event); | 184 i->first->HandleEvent(event); |
| 184 | 185 |
| 185 listeners_mutex_.Lock(); | 186 listeners_mutex_.Acquire(); |
| 186 current_listener_callback_ = NULL; | 187 current_listener_callback_ = NULL; |
| 187 if (NULL != callback_waiters_) { | 188 if (NULL != callback_waiters_) { |
| 188 callback_waiters_->Signal(); | 189 callback_waiters_->Signal(); |
| 189 callback_waiters_ = NULL; | 190 callback_waiters_ = NULL; |
| 190 } | 191 } |
| 191 | 192 |
| 192 ++i; | 193 ++i; |
| 193 } | 194 } |
| 194 listeners_mutex_.Unlock(); | 195 listeners_mutex_.Release(); |
| 195 } | 196 } |
| 196 | 197 |
| 197 // A map iterator remains valid until the element it points to gets removed | 198 // A map iterator remains valid until the element it points to gets removed |
| 198 // from the map, so a map is perfect for our needs. | 199 // from the map, so a map is perfect for our needs. |
| 199 // | 200 // |
| 200 // Map value is a bool, true means the Listener is dead. | 201 // Map value is a bool, true means the Listener is dead. |
| 201 Listeners listeners_; | 202 Listeners listeners_; |
| 202 // NULL means no callback is currently being called. | 203 // NULL means no callback is currently being called. |
| 203 Listener* current_listener_callback_; | 204 Listener* current_listener_callback_; |
| 204 // Only valid when current_listener is not NULL. | 205 // Only valid when current_listener is not NULL. |
| 205 // The thread on which the callback is executing. | 206 // The thread on which the callback is executing. |
| 206 pthread_t current_listener_callback_thread_id_; | 207 MessageLoop* current_listener_callback_message_loop_; |
| 207 // Win32 Event that is usually NULL. Only created when another thread calls | 208 // Win32 Event that is usually NULL. Only created when another thread calls |
| 208 // Remove while in callback. Owned and closed by the thread calling Remove(). | 209 // Remove while in callback. Owned and closed by the thread calling Remove(). |
| 209 CallbackWaiters* callback_waiters_; | 210 CallbackWaiters* callback_waiters_; |
| 210 | 211 |
| 211 PThreadMutex listeners_mutex_; // Protects all members above. | 212 Lock listeners_mutex_; // Protects all members above. |
| 212 const EventType shutdown_event_; | 213 const EventType shutdown_event_; |
| 213 NotifyLock notify_lock_; | 214 NotifyLock notify_lock_; |
| 214 | 215 |
| 215 DISALLOW_COPY_AND_ASSIGN(EventChannel); | 216 DISALLOW_COPY_AND_ASSIGN(EventChannel); |
| 216 }; | 217 }; |
| 217 | 218 |
| 218 // An EventListenerHookup hooks up a method in your class to an EventChannel. | 219 // An EventListenerHookup hooks up a method in your class to an EventChannel. |
| 219 // Deleting the hookup disconnects from the EventChannel. | 220 // Deleting the hookup disconnects from the EventChannel. |
| 220 // | 221 // |
| 221 // Contains complexity of inheriting from Listener class and managing lifetimes. | 222 // Contains complexity of inheriting from Listener class and managing lifetimes. |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 332 CallbackObject* cbobject, | 333 CallbackObject* cbobject, |
| 333 CallbackMethod cbmethod, | 334 CallbackMethod cbmethod, |
| 334 CallbackArg0 arg0) { | 335 CallbackArg0 arg0) { |
| 335 return new ArgHookup<EventChannel, | 336 return new ArgHookup<EventChannel, |
| 336 typename EventChannel::EventTraits, | 337 typename EventChannel::EventTraits, |
| 337 CallbackObject, CallbackMethod, CallbackArg0>(channel, cbobject, | 338 CallbackObject, CallbackMethod, CallbackArg0>(channel, cbobject, |
| 338 cbmethod, arg0); | 339 cbmethod, arg0); |
| 339 } | 340 } |
| 340 | 341 |
| 341 #endif // CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_ | 342 #endif // CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_ |
| OLD | NEW |