| 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(¬ify_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
|
|
|
|
|