// Copyright (c) 2010 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_CHANNEL_H_ #define CHROME_BROWSER_SYNC_UTIL_CHANNEL_H_ #pragma once /////////////////////////////////////////////////////////////////////////////// // // OVERVIEW: // // A threadsafe container for a list of observers. Observers are able to // remove themselves during iteration, and can be added on any thread. This // allows observers to safely remove themselves during notifications. It // also provides a handler when an observer is added that will remove the // observer on destruction. // // It is expected that all observers are removed before destruction. // The channel owner should notify all observers to disconnect on shutdown if // needed to ensure this. // // TYPICAL USAGE: // // class MyWidget { // public: // ... // // class Observer : public ChannelEventHandler { // public: // virtual void HandleChannelEvent(const FooEvent& w) = 0; // }; // // ChannelHookup* AddObserver(Observer* obs) { // return channel_.AddObserver(obs); // } // // void RemoveObserver(Observer* obs) { // channel_.RemoveObserver(obs); // } // // void NotifyFoo(FooEvent& event) { // channel_.Notify(event); // } // // private: // Channel channel_; // }; // // /////////////////////////////////////////////////////////////////////////////// #include "base/lock.h" #include "base/observer_list.h" #include "base/threading/platform_thread.h" namespace browser_sync { template class Channel; class EventHandler { }; template class ChannelEventHandler : public EventHandler { public: virtual void HandleChannelEvent(const EventType& event) = 0; protected: virtual ~ChannelEventHandler() {} }; // This class manages a connection to a channel. When it is destroyed, it // will remove the listener from the channel observer list. template class ChannelHookup { public: ChannelHookup(Channel* channel, browser_sync::ChannelEventHandler* handler) : channel_(channel), handler_(handler) {} ~ChannelHookup() { channel_->RemoveObserver(handler_); } private: Channel* channel_; browser_sync::ChannelEventHandler* handler_; }; template class Channel { public: typedef ObserverListBase ChannelObserverList; Channel() : locking_thread_(0) {} ChannelHookup* AddObserver( ChannelEventHandler* observer) { AutoLock scoped_lock(event_handlers_mutex_); event_handlers_.AddObserver(observer); return new ChannelHookup(this, observer); } void RemoveObserver(ChannelEventHandler* observer) { // This can be called in response to a notification, so we may already have // locked this channel on this thread. bool need_lock = (locking_thread_ != base::PlatformThread::CurrentId()); if (need_lock) event_handlers_mutex_.Acquire(); event_handlers_mutex_.AssertAcquired(); event_handlers_.RemoveObserver(observer); if (need_lock) event_handlers_mutex_.Release(); } void Notify(const EventType& event) { AutoLock scoped_lock(event_handlers_mutex_); // This may result in an observer trying to remove itself, so keep track // of the thread we're locked on. locking_thread_ = base::PlatformThread::CurrentId(); ChannelObserverList::Iterator it(event_handlers_); EventHandler* obs; while ((obs = it.GetNext()) != NULL) { static_cast* >(obs)-> HandleChannelEvent(event); } // Set back to an invalid thread id. locking_thread_ = 0; } private: Lock event_handlers_mutex_; base::PlatformThreadId locking_thread_; ObserverList event_handlers_; }; } // namespace browser_sync #endif // CHROME_BROWSER_SYNC_UTIL_CHANNEL_H_