OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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_CHANNEL_H_ |
| 6 #define CHROME_BROWSER_SYNC_UTIL_CHANNEL_H_ |
| 7 |
| 8 /////////////////////////////////////////////////////////////////////////////// |
| 9 // |
| 10 // OVERVIEW: |
| 11 // |
| 12 // A threadsafe container for a list of observers. Observers are able to |
| 13 // remove themselves during iteration, and can be added on any thread. This |
| 14 // allows observers to safely remove themselves during notifications. It |
| 15 // also provides a handler when an observer is added that will remove the |
| 16 // observer on destruction. |
| 17 // |
| 18 // It is expected that all observers are removed before destruction. |
| 19 // The channel owner should notify all observers to disconnect on shutdown if |
| 20 // needed to ensure this. |
| 21 // |
| 22 // TYPICAL USAGE: |
| 23 // |
| 24 // class MyWidget { |
| 25 // public: |
| 26 // ... |
| 27 // |
| 28 // class Observer : public ChannelEventHandler<FooEvent> { |
| 29 // public: |
| 30 // virtual void HandleChannelEvent(const FooEvent& w) = 0; |
| 31 // }; |
| 32 // |
| 33 // ChannelHookup<MyEvent>* AddObserver(Observer* obs) { |
| 34 // return channel_.AddObserver(obs); |
| 35 // } |
| 36 // |
| 37 // void RemoveObserver(Observer* obs) { |
| 38 // channel_.RemoveObserver(obs); |
| 39 // } |
| 40 // |
| 41 // void NotifyFoo(FooEvent& event) { |
| 42 // channel_.Notify(event); |
| 43 // } |
| 44 // |
| 45 // private: |
| 46 // Channel<FooEvent> channel_; |
| 47 // }; |
| 48 // |
| 49 // |
| 50 /////////////////////////////////////////////////////////////////////////////// |
| 51 |
| 52 #include "base/lock.h" |
| 53 #include "base/observer_list.h" |
| 54 |
| 55 namespace browser_sync { |
| 56 |
| 57 template <typename EventType> |
| 58 class Channel; |
| 59 |
| 60 class EventHandler { |
| 61 }; |
| 62 |
| 63 template <typename EventType> |
| 64 class ChannelEventHandler : public EventHandler { |
| 65 public: |
| 66 virtual void HandleChannelEvent(const EventType& event) = 0; |
| 67 }; |
| 68 |
| 69 // This class manages a connection to a channel. When it is destroyed, it |
| 70 // will remove the listener from the channel observer list. |
| 71 template <typename EventType> |
| 72 class ChannelHookup { |
| 73 public: |
| 74 ChannelHookup(Channel<EventType>* channel, |
| 75 browser_sync::ChannelEventHandler<EventType>* handler) |
| 76 : channel_(channel), |
| 77 handler_(handler) {} |
| 78 ~ChannelHookup() { |
| 79 channel_->RemoveObserver(handler_); |
| 80 } |
| 81 |
| 82 private: |
| 83 Channel<EventType>* channel_; |
| 84 browser_sync::ChannelEventHandler<EventType>* handler_; |
| 85 }; |
| 86 |
| 87 template <typename EventType> |
| 88 class Channel { |
| 89 public: |
| 90 typedef ObserverListBase<EventHandler> ChannelObserverList; |
| 91 |
| 92 Channel() : locking_thread_(0) {} |
| 93 |
| 94 ChannelHookup<EventType>* AddObserver( |
| 95 ChannelEventHandler<EventType>* observer) { |
| 96 AutoLock scoped_lock(event_handlers_mutex_); |
| 97 event_handlers_.AddObserver(observer); |
| 98 return new ChannelHookup<EventType>(this, observer); |
| 99 } |
| 100 |
| 101 void RemoveObserver(ChannelEventHandler<EventType>* observer) { |
| 102 // This can be called in response to a notification, so we may already have |
| 103 // locked this channel on this thread. |
| 104 bool need_lock = (locking_thread_ != PlatformThread::CurrentId()); |
| 105 if (need_lock) |
| 106 event_handlers_mutex_.Acquire(); |
| 107 |
| 108 event_handlers_mutex_.AssertAcquired(); |
| 109 event_handlers_.RemoveObserver(observer); |
| 110 if (need_lock) |
| 111 event_handlers_mutex_.Release(); |
| 112 } |
| 113 |
| 114 void Notify(const EventType& event) { |
| 115 AutoLock scoped_lock(event_handlers_mutex_); |
| 116 |
| 117 // This may result in an observer trying to remove itself, so keep track |
| 118 // of the thread we're locked on. |
| 119 locking_thread_ = PlatformThread::CurrentId(); |
| 120 |
| 121 ChannelObserverList::Iterator it(event_handlers_); |
| 122 EventHandler* obs; |
| 123 while ((obs = it.GetNext()) != NULL) { |
| 124 static_cast<ChannelEventHandler<EventType>* >(obs)-> |
| 125 HandleChannelEvent(event); |
| 126 } |
| 127 |
| 128 // Set back to an invalid thread id. |
| 129 locking_thread_ = 0; |
| 130 } |
| 131 |
| 132 private: |
| 133 Lock event_handlers_mutex_; |
| 134 PlatformThreadId locking_thread_; |
| 135 ObserverList<EventHandler> event_handlers_; |
| 136 }; |
| 137 |
| 138 } // namespace browser_sync |
| 139 |
| 140 #endif // CHROME_BROWSER_SYNC_UTIL_CHANNEL_H_ |
OLD | NEW |