Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: chrome/browser/sync/util/event_sys-inl.h

Issue 194065: Initial commit of sync engine code to browser/sync.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Fixes to gtest include path, reverted syncapi. Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #ifndef CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_
6 #define CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_
7
8 #include <map>
9
10 #include "base/atomicops.h"
11 #include "base/basictypes.h"
12 #include "base/logging.h"
13 #include "base/port.h"
14 #include "chrome/browser/sync/util/compat-pthread.h"
15 #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
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), callback_done_(false) {
62 }
63 ~CallbackWaiters() {
64 DCHECK_EQ(0, waiter_count_);
65 }
66 void WaitForCallbackToComplete(PThreadMutex* listeners_mutex) {
67 {
68 PThreadScopedLock<PThreadMutex> lock(&mutex_);
69 waiter_count_ += 1;
70 listeners_mutex->Unlock();
71 while (!callback_done_)
72 pthread_cond_wait(&condvar_.condvar_, &mutex_.mutex_);
73 waiter_count_ -= 1;
74 if (0 != waiter_count_)
75 return;
76 }
77 delete this;
78 }
79
80 void Signal() {
81 PThreadScopedLock<PThreadMutex> lock(&mutex_);
82 callback_done_ = true;
83 pthread_cond_broadcast(&condvar_.condvar_);
84 }
85
86 protected:
87 int waiter_count_;
88 bool callback_done_;
89 PThreadMutex mutex_;
90 PThreadCondVar condvar_;
91 };
92
93 template <typename EventTraitsType, typename NotifyLock,
94 typename ScopedNotifyLocker>
95 class EventChannel {
96 public:
97 typedef EventTraitsType EventTraits;
98 typedef typename EventTraits::EventType EventType;
99 typedef EventListener<EventType> Listener;
100
101 protected:
102 typedef std::map<Listener*, bool> Listeners;
103 typedef PThreadScopedLock<PThreadMutex> ScopedListenersLock;
104
105 public:
106 // The shutdown event gets send in the EventChannel's destructor.
107 explicit EventChannel(const EventType& shutdown_event)
108 : callback_waiters_(NULL), shutdown_event_(shutdown_event),
109 current_listener_callback_(NULL) {
110 }
111
112 ~EventChannel() {
113 // Tell all the listeners that the channel is being deleted.
114 NotifyListeners(shutdown_event_);
115
116 // Make sure all the listeners have been disconnected. Otherwise, they
117 // will try to call RemoveListener() at a later date.
118 #ifdef DEBUG
119 ScopedListenersLock lock(&listeners_mutex_);
120 for (typename Listeners::iterator i = listeners_.begin();
121 i != listeners_.end(); ++i) {
122 DCHECK(i->second) << "Listener not disconnected";
123 }
124 #endif
125 }
126
127 // Never call this twice for the same listener.
128 //
129 // Thread safe.
130 void AddListener(Listener* listener) {
131 ScopedListenersLock lock(&listeners_mutex_);
132 typename Listeners::iterator found = listeners_.find(listener);
133 if (found == listeners_.end()) {
134 listeners_.insert(std::make_pair(listener,
135 false)); // Not dead yet.
136 } else {
137 DCHECK(found->second) << "Attempted to add the same listener twice.";
138 found->second = false; // Not dead yet.
139 }
140 }
141
142 // If listener's callback is currently executing, this method waits until the
143 // callback completes before returning.
144 //
145 // Thread safe.
146 void RemoveListener(Listener* listener) {
147 bool wait = false;
148 listeners_mutex_.Lock();
149 typename Listeners::iterator found = listeners_.find(listener);
150 if (found != listeners_.end()) {
151 found->second = true; // Mark as dead.
152 wait = (found->first == current_listener_callback_ &&
153 (!pthread_equal(current_listener_callback_thread_id_,
154 pthread_self())));
155 }
156 if (!wait) {
157 listeners_mutex_.Unlock();
158 return;
159 }
160 if (NULL == callback_waiters_)
161 callback_waiters_ = new CallbackWaiters;
162 callback_waiters_->WaitForCallbackToComplete(&listeners_mutex_);
163 }
164
165 // Blocks until all listeners have been notified.
166 //
167 // NOT thread safe. Must only be called by one thread at a time.
168 void NotifyListeners(const EventType& event) {
169 ScopedNotifyLocker lock_notify(&notify_lock_);
170 listeners_mutex_.Lock();
171 DCHECK(NULL == current_listener_callback_);
172 current_listener_callback_thread_id_ = pthread_self();
173 typename Listeners::iterator i = listeners_.begin();
174 while (i != listeners_.end()) {
175 if (i->second) { // Clean out dead listeners
176 listeners_.erase(i++);
177 continue;
178 }
179 current_listener_callback_ = i->first;
180 listeners_mutex_.Unlock();
181
182 i->first->HandleEvent(event);
183
184 listeners_mutex_.Lock();
185 current_listener_callback_ = NULL;
186 if (NULL != callback_waiters_) {
187 callback_waiters_->Signal();
188 callback_waiters_ = NULL;
189 }
190
191 ++i;
192 }
193 listeners_mutex_.Unlock();
194 }
195
196 // A map iterator remains valid until the element it points to gets removed
197 // from the map, so a map is perfect for our needs.
198 //
199 // Map value is a bool, true means the Listener is dead.
200 Listeners listeners_;
201 // NULL means no callback is currently being called.
202 Listener* current_listener_callback_;
203 // Only valid when current_listener is not NULL.
204 // The thread on which the callback is executing.
205 pthread_t current_listener_callback_thread_id_;
206 // Win32 Event that is usually NULL. Only created when another thread calls
207 // Remove while in callback. Owned and closed by the thread calling Remove().
208 CallbackWaiters* callback_waiters_;
209
210 PThreadMutex listeners_mutex_; // Protects all members above.
211 const EventType shutdown_event_;
212 NotifyLock notify_lock_;
213
214 DISALLOW_COPY_AND_ASSIGN(EventChannel);
215 };
216
217 // An EventListenerHookup hooks up a method in your class to an EventChannel.
218 // Deleting the hookup disconnects from the EventChannel.
219 //
220 // Contains complexity of inheriting from Listener class and managing lifetimes.
221 //
222 // Create using NewEventListenerHookup() to avoid explicit template arguments.
223 class EventListenerHookup {
224 public:
225 virtual ~EventListenerHookup() { }
226 };
227
228 template <typename EventChannel, typename EventTraits,
229 class Derived>
230 class EventListenerHookupImpl : public EventListenerHookup,
231 public EventListener<typename EventTraits::EventType> {
232 public:
233 explicit EventListenerHookupImpl(EventChannel* channel)
234 : channel_(channel), deleted_(NULL) {
235 channel->AddListener(this);
236 connected_ = true;
237 }
238
239 ~EventListenerHookupImpl() {
240 if (NULL != deleted_)
241 *deleted_ = true;
242 if (connected_)
243 channel_->RemoveListener(this);
244 }
245
246 typedef typename EventTraits::EventType EventType;
247 virtual void HandleEvent(const EventType& event) {
248 DCHECK(connected_);
249 bool deleted = false;
250 deleted_ = &deleted;
251 static_cast<Derived*>(this)->Callback(event);
252 if (deleted) // The callback (legally) deleted this.
253 return; // The only safe thing to do.
254 deleted_ = NULL;
255 if (EventTraits::IsChannelShutdownEvent(event)) {
256 channel_->RemoveListener(this);
257 connected_ = false;
258 }
259 }
260
261 protected:
262 EventChannel* const channel_;
263 bool connected_;
264 bool* deleted_; // Allows the handler to delete the hookup.
265 };
266
267 // SimpleHookup just passes the event to the callback message.
268 template <typename EventChannel, typename EventTraits,
269 typename CallbackObject, typename CallbackMethod>
270 class SimpleHookup
271 : public EventListenerHookupImpl<EventChannel, EventTraits,
272 SimpleHookup<EventChannel,
273 EventTraits,
274 CallbackObject,
275 CallbackMethod> > {
276 public:
277 SimpleHookup(EventChannel* channel, CallbackObject* cbobject,
278 CallbackMethod cbmethod)
279 : EventListenerHookupImpl<EventChannel, EventTraits,
280 SimpleHookup>(channel), cbobject_(cbobject),
281 cbmethod_(cbmethod) { }
282
283 typedef typename EventTraits::EventType EventType;
284 void Callback(const EventType& event) {
285 (cbobject_->*cbmethod_)(event);
286 }
287 CallbackObject* const cbobject_;
288 CallbackMethod const cbmethod_;
289 };
290
291 // ArgHookup also passes an additional arg to the callback method.
292 template <typename EventChannel, typename EventTraits,
293 typename CallbackObject, typename CallbackMethod,
294 typename CallbackArg0>
295 class ArgHookup :
296 public EventListenerHookupImpl<EventChannel, EventTraits,
297 ArgHookup<EventChannel, EventTraits,
298 CallbackObject,
299 CallbackMethod,
300 CallbackArg0> > {
301 public:
302 ArgHookup(EventChannel* channel, CallbackObject* cbobject,
303 CallbackMethod cbmethod, CallbackArg0 arg0)
304 : EventListenerHookupImpl<EventChannel, EventTraits,
305 ArgHookup>(channel), cbobject_(cbobject),
306 cbmethod_(cbmethod), arg0_(arg0) { }
307
308 typedef typename EventTraits::EventType EventType;
309 void Callback(const EventType& event) {
310 (cbobject_->*cbmethod_)(arg0_, event);
311 }
312 CallbackObject* const cbobject_;
313 CallbackMethod const cbmethod_;
314 CallbackArg0 const arg0_;
315 };
316
317
318 template <typename EventChannel, typename CallbackObject,
319 typename CallbackMethod>
320 EventListenerHookup* NewEventListenerHookup(EventChannel* channel,
321 CallbackObject* cbobject,
322 CallbackMethod cbmethod) {
323 return new SimpleHookup<EventChannel,
324 typename EventChannel::EventTraits,
325 CallbackObject, CallbackMethod>(channel, cbobject, cbmethod);
326 }
327
328 template <typename EventChannel, typename CallbackObject,
329 typename CallbackMethod, typename CallbackArg0>
330 EventListenerHookup* NewEventListenerHookup(EventChannel* channel,
331 CallbackObject* cbobject,
332 CallbackMethod cbmethod,
333 CallbackArg0 arg0) {
334 return new ArgHookup<EventChannel,
335 typename EventChannel::EventTraits,
336 CallbackObject, CallbackMethod, CallbackArg0>(channel, cbobject,
337 cbmethod, arg0);
338 }
339
340 #endif // CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698