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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/sync/util/event_sys-inl.h
===================================================================
--- chrome/browser/sync/util/event_sys-inl.h (revision 0)
+++ chrome/browser/sync/util/event_sys-inl.h (revision 0)
@@ -0,0 +1,340 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_
+#define CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_
+
+#include <map>
+
+#include "base/atomicops.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/port.h"
+#include "chrome/browser/sync/util/compat-pthread.h"
+#include "chrome/browser/sync/util/event_sys.h"
+#include "chrome/browser/sync/util/pthread_helpers.h"
+#include "chrome/browser/sync/util/sync_types.h"
+
+// How to use Channels:
+
+// 0. Assume Bob is the name of the class from which you want to broadcast
+// events.
+// 1. Choose an EventType. This could be an Enum or something more complicated.
+// 2. Create an EventTraits class for your EventType. It must have
+// two members:
+//
+// typedef x EventType;
+// static bool IsChannelShutdownEvent(const EventType& event);
+//
+// 3. Add an EventChannel<MyEventTraits>* instance and event_channel() const;
+// accessor to Bob.
+// Delete the channel ordinarily in Bob's destructor, or whenever you want.
+// 4. To broadcast events, call bob->event_channel()->NotifyListeners(event).
+// 5. Only call NotifyListeners from a single thread at a time.
+
+// How to use Listeners/Hookups:
+
+// 0. Assume you want a class called Lisa to listen to events from Bob.
+// 1. Create an event handler method in Lisa. Its single argument should be of
+// your event type.
+// 2. Add a EventListenerHookup* hookup_ member to Lisa.
+// 3. Initialize the hookup by calling:
+//
+// hookup_ = NewEventListenerHookup(bob->event_channel(),
+// this,
+// &Lisa::HandleEvent);
+//
+// 4. Delete hookup_ in Lisa's destructor, or anytime sooner to stop receiving
+// events.
+
+// An Event Channel is a source, or broadcaster of events. Listeners subscribe
+// by calling the AddListener() method. The owner of the channel calls the
+// NotifyListeners() method.
+//
+// Don't inherit from this class. Just make an event_channel member and an
+// event_channel() accessor.
+
+// No reason why CallbackWaiters has to be templatized.
+class CallbackWaiters {
+ public:
+ CallbackWaiters() : waiter_count_(0), callback_done_(false) {
+ }
+ ~CallbackWaiters() {
+ DCHECK_EQ(0, waiter_count_);
+ }
+ void WaitForCallbackToComplete(PThreadMutex* listeners_mutex) {
+ {
+ PThreadScopedLock<PThreadMutex> lock(&mutex_);
+ waiter_count_ += 1;
+ listeners_mutex->Unlock();
+ while (!callback_done_)
+ pthread_cond_wait(&condvar_.condvar_, &mutex_.mutex_);
+ waiter_count_ -= 1;
+ if (0 != waiter_count_)
+ return;
+ }
+ delete this;
+ }
+
+ void Signal() {
+ PThreadScopedLock<PThreadMutex> lock(&mutex_);
+ callback_done_ = true;
+ pthread_cond_broadcast(&condvar_.condvar_);
+ }
+
+ protected:
+ int waiter_count_;
+ bool callback_done_;
+ PThreadMutex mutex_;
+ PThreadCondVar condvar_;
+};
+
+template <typename EventTraitsType, typename NotifyLock,
+ typename ScopedNotifyLocker>
+class EventChannel {
+ public:
+ typedef EventTraitsType EventTraits;
+ typedef typename EventTraits::EventType EventType;
+ typedef EventListener<EventType> Listener;
+
+ protected:
+ typedef std::map<Listener*, bool> Listeners;
+ typedef PThreadScopedLock<PThreadMutex> ScopedListenersLock;
+
+ public:
+ // The shutdown event gets send in the EventChannel's destructor.
+ explicit EventChannel(const EventType& shutdown_event)
+ : callback_waiters_(NULL), shutdown_event_(shutdown_event),
+ current_listener_callback_(NULL) {
+ }
+
+ ~EventChannel() {
+ // Tell all the listeners that the channel is being deleted.
+ NotifyListeners(shutdown_event_);
+
+ // Make sure all the listeners have been disconnected. Otherwise, they
+ // will try to call RemoveListener() at a later date.
+#ifdef DEBUG
+ ScopedListenersLock lock(&listeners_mutex_);
+ for (typename Listeners::iterator i = listeners_.begin();
+ i != listeners_.end(); ++i) {
+ DCHECK(i->second) << "Listener not disconnected";
+ }
+#endif
+ }
+
+ // Never call this twice for the same listener.
+ //
+ // Thread safe.
+ void AddListener(Listener* listener) {
+ ScopedListenersLock lock(&listeners_mutex_);
+ typename Listeners::iterator found = listeners_.find(listener);
+ if (found == listeners_.end()) {
+ listeners_.insert(std::make_pair(listener,
+ false)); // Not dead yet.
+ } else {
+ DCHECK(found->second) << "Attempted to add the same listener twice.";
+ found->second = false; // Not dead yet.
+ }
+ }
+
+ // If listener's callback is currently executing, this method waits until the
+ // callback completes before returning.
+ //
+ // Thread safe.
+ void RemoveListener(Listener* listener) {
+ bool wait = false;
+ listeners_mutex_.Lock();
+ typename Listeners::iterator found = listeners_.find(listener);
+ if (found != listeners_.end()) {
+ found->second = true; // Mark as dead.
+ wait = (found->first == current_listener_callback_ &&
+ (!pthread_equal(current_listener_callback_thread_id_,
+ pthread_self())));
+ }
+ if (!wait) {
+ listeners_mutex_.Unlock();
+ return;
+ }
+ if (NULL == callback_waiters_)
+ callback_waiters_ = new CallbackWaiters;
+ callback_waiters_->WaitForCallbackToComplete(&listeners_mutex_);
+ }
+
+ // Blocks until all listeners have been notified.
+ //
+ // NOT thread safe. Must only be called by one thread at a time.
+ void NotifyListeners(const EventType& event) {
+ ScopedNotifyLocker lock_notify(&notify_lock_);
+ listeners_mutex_.Lock();
+ DCHECK(NULL == current_listener_callback_);
+ current_listener_callback_thread_id_ = pthread_self();
+ typename Listeners::iterator i = listeners_.begin();
+ while (i != listeners_.end()) {
+ if (i->second) { // Clean out dead listeners
+ listeners_.erase(i++);
+ continue;
+ }
+ current_listener_callback_ = i->first;
+ listeners_mutex_.Unlock();
+
+ i->first->HandleEvent(event);
+
+ listeners_mutex_.Lock();
+ current_listener_callback_ = NULL;
+ if (NULL != callback_waiters_) {
+ callback_waiters_->Signal();
+ callback_waiters_ = NULL;
+ }
+
+ ++i;
+ }
+ listeners_mutex_.Unlock();
+ }
+
+ // A map iterator remains valid until the element it points to gets removed
+ // from the map, so a map is perfect for our needs.
+ //
+ // Map value is a bool, true means the Listener is dead.
+ Listeners listeners_;
+ // NULL means no callback is currently being called.
+ Listener* current_listener_callback_;
+ // Only valid when current_listener is not NULL.
+ // The thread on which the callback is executing.
+ pthread_t current_listener_callback_thread_id_;
+ // Win32 Event that is usually NULL. Only created when another thread calls
+ // Remove while in callback. Owned and closed by the thread calling Remove().
+ CallbackWaiters* callback_waiters_;
+
+ PThreadMutex listeners_mutex_; // Protects all members above.
+ const EventType shutdown_event_;
+ NotifyLock notify_lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventChannel);
+};
+
+// An EventListenerHookup hooks up a method in your class to an EventChannel.
+// Deleting the hookup disconnects from the EventChannel.
+//
+// Contains complexity of inheriting from Listener class and managing lifetimes.
+//
+// Create using NewEventListenerHookup() to avoid explicit template arguments.
+class EventListenerHookup {
+ public:
+ virtual ~EventListenerHookup() { }
+};
+
+template <typename EventChannel, typename EventTraits,
+ class Derived>
+class EventListenerHookupImpl : public EventListenerHookup,
+public EventListener<typename EventTraits::EventType> {
+ public:
+ explicit EventListenerHookupImpl(EventChannel* channel)
+ : channel_(channel), deleted_(NULL) {
+ channel->AddListener(this);
+ connected_ = true;
+ }
+
+ ~EventListenerHookupImpl() {
+ if (NULL != deleted_)
+ *deleted_ = true;
+ if (connected_)
+ channel_->RemoveListener(this);
+ }
+
+ typedef typename EventTraits::EventType EventType;
+ virtual void HandleEvent(const EventType& event) {
+ DCHECK(connected_);
+ bool deleted = false;
+ deleted_ = &deleted;
+ static_cast<Derived*>(this)->Callback(event);
+ if (deleted) // The callback (legally) deleted this.
+ return; // The only safe thing to do.
+ deleted_ = NULL;
+ if (EventTraits::IsChannelShutdownEvent(event)) {
+ channel_->RemoveListener(this);
+ connected_ = false;
+ }
+ }
+
+ protected:
+ EventChannel* const channel_;
+ bool connected_;
+ bool* deleted_; // Allows the handler to delete the hookup.
+};
+
+// SimpleHookup just passes the event to the callback message.
+template <typename EventChannel, typename EventTraits,
+ typename CallbackObject, typename CallbackMethod>
+class SimpleHookup
+ : public EventListenerHookupImpl<EventChannel, EventTraits,
+ SimpleHookup<EventChannel,
+ EventTraits,
+ CallbackObject,
+ CallbackMethod> > {
+ public:
+ SimpleHookup(EventChannel* channel, CallbackObject* cbobject,
+ CallbackMethod cbmethod)
+ : EventListenerHookupImpl<EventChannel, EventTraits,
+ SimpleHookup>(channel), cbobject_(cbobject),
+ cbmethod_(cbmethod) { }
+
+ typedef typename EventTraits::EventType EventType;
+ void Callback(const EventType& event) {
+ (cbobject_->*cbmethod_)(event);
+ }
+ CallbackObject* const cbobject_;
+ CallbackMethod const cbmethod_;
+};
+
+// ArgHookup also passes an additional arg to the callback method.
+template <typename EventChannel, typename EventTraits,
+ typename CallbackObject, typename CallbackMethod,
+ typename CallbackArg0>
+class ArgHookup :
+ public EventListenerHookupImpl<EventChannel, EventTraits,
+ ArgHookup<EventChannel, EventTraits,
+ CallbackObject,
+ CallbackMethod,
+ CallbackArg0> > {
+ public:
+ ArgHookup(EventChannel* channel, CallbackObject* cbobject,
+ CallbackMethod cbmethod, CallbackArg0 arg0)
+ : EventListenerHookupImpl<EventChannel, EventTraits,
+ ArgHookup>(channel), cbobject_(cbobject),
+ cbmethod_(cbmethod), arg0_(arg0) { }
+
+ typedef typename EventTraits::EventType EventType;
+ void Callback(const EventType& event) {
+ (cbobject_->*cbmethod_)(arg0_, event);
+ }
+ CallbackObject* const cbobject_;
+ CallbackMethod const cbmethod_;
+ CallbackArg0 const arg0_;
+};
+
+
+template <typename EventChannel, typename CallbackObject,
+ typename CallbackMethod>
+EventListenerHookup* NewEventListenerHookup(EventChannel* channel,
+ CallbackObject* cbobject,
+ CallbackMethod cbmethod) {
+ return new SimpleHookup<EventChannel,
+ typename EventChannel::EventTraits,
+ CallbackObject, CallbackMethod>(channel, cbobject, cbmethod);
+}
+
+template <typename EventChannel, typename CallbackObject,
+ typename CallbackMethod, typename CallbackArg0>
+EventListenerHookup* NewEventListenerHookup(EventChannel* channel,
+ CallbackObject* cbobject,
+ CallbackMethod cbmethod,
+ CallbackArg0 arg0) {
+ return new ArgHookup<EventChannel,
+ typename EventChannel::EventTraits,
+ CallbackObject, CallbackMethod, CallbackArg0>(channel, cbobject,
+ cbmethod, arg0);
+}
+
+#endif // CHROME_BROWSER_SYNC_UTIL_EVENT_SYS_INL_H_
Property changes on: chrome\browser\sync\util\event_sys-inl.h
___________________________________________________________________
Added: svn:eol-style
+ LF

Powered by Google App Engine
This is Rietveld 408576698