| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| // Date: Sun Nov 7 21:43:34 CST 2010 |
| |
| #ifndef BUTIL_SYNCHRONOUS_EVENT_H |
| #define BUTIL_SYNCHRONOUS_EVENT_H |
| |
| #include <vector> // std::vector |
| #include <algorithm> // std::find |
| #include <errno.h> // errno |
| #include "butil/logging.h" |
| |
| // Synchronous event notification. |
| // Observers to an event will be called immediately in the same context where |
| // the event is notified. This utility uses a vector to store all observers |
| // thus is only suitable for a relatively small amount of observers. |
| // |
| // Example: |
| // // Declare event type |
| // typedef SynchronousEvent<int, const Foo*> FooEvent; |
| // |
| // // Implement observer type |
| // class FooObserver : public FooEvent::Observer { |
| // void on_event(int, const Foo*) { |
| // ... handle the event ... |
| // } |
| // }; |
| // |
| // FooEvent foo_event; // An instance of the event |
| // FooObserver foo_observer; // An instance of the observer |
| // foo_event.subscribe(&foo_observer); // register the observer to the event |
| // foo_event.notify(1, NULL); // foo_observer.on_event(1, NULL) is |
| // // called *immediately* |
| |
| namespace butil { |
| |
| namespace detail { |
| // NOTE: This is internal class. Inherit SynchronousEvent<..>::Observer instead. |
| template <typename _A1, typename _A2, typename _A3> class EventObserver; |
| } |
| |
| // All methods are *NOT* thread-safe. |
| // You can copy a SynchronousEvent. |
| template <typename _A1 = void, typename _A2 = void, typename _A3 = void> |
| class SynchronousEvent { |
| public: |
| typedef detail::EventObserver<_A1, _A2, _A3> Observer; |
| typedef std::vector<Observer*> ObserverSet; |
| |
| SynchronousEvent() : _n(0) {} |
| |
| // Add an observer, callable inside on_event() and added observers |
| // will be called with the same event in the same run. |
| // Returns 0 when successful, -1 when the obsever is NULL or already added. |
| int subscribe(Observer* ob) { |
| if (NULL == ob) { |
| LOG(ERROR) << "Observer is NULL"; |
| return -1; |
| } |
| if (std::find(_obs.begin(), _obs.end(), ob) != _obs.end()) { |
| return -1; |
| } |
| _obs.push_back(ob); |
| ++_n; |
| return 0; |
| } |
| |
| // Remove an observer, callable inside on_event(). |
| // Users are responsible for removing observers before destroying them. |
| // Returns 0 when successful, -1 when the observer is NULL or already removed. |
| int unsubscribe(Observer* ob) { |
| if (NULL == ob) { |
| LOG(ERROR) << "Observer is NULL"; |
| return -1; |
| } |
| typename ObserverSet::iterator |
| it = std::find(_obs.begin(), _obs.end(), ob); |
| if (it == _obs.end()) { |
| return -1; |
| } |
| *it = NULL; |
| --_n; |
| return 0; |
| } |
| |
| // Remove all observers, callable inside on_event() |
| void clear() { |
| for (typename ObserverSet::iterator |
| it = _obs.begin(); it != _obs.end(); ++it) { |
| *it = NULL; |
| } |
| _n = 0; |
| } |
| |
| // Get number of observers |
| size_t size() const { return _n; } |
| |
| // No observers or not |
| bool empty() const { return size() == 0UL; } |
| |
| // Notify observers without parameter, errno will not be changed |
| void notify() { |
| const int saved_errno = errno; |
| for (size_t i = 0; i < _obs.size(); ++i) { |
| if (_obs[i]) { |
| _obs[i]->on_event(); |
| } |
| } |
| _shrink(); |
| errno = saved_errno; |
| } |
| |
| // Notify observers with 1 parameter, errno will not be changed |
| template <typename _B1> void notify(const _B1& b1) { |
| const int saved_errno = errno; |
| for (size_t i = 0; i < _obs.size(); ++i) { |
| if (_obs[i]) { |
| _obs[i]->on_event(b1); |
| } |
| } |
| _shrink(); |
| errno = saved_errno; |
| } |
| |
| // Notify observers with 2 parameters, errno will not be changed |
| template <typename _B1, typename _B2> |
| void notify(const _B1& b1, const _B2& b2) { |
| const int saved_errno = errno; |
| for (size_t i = 0; i < _obs.size(); ++i) { |
| if (_obs[i]) { |
| _obs[i]->on_event(b1, b2); |
| } |
| } |
| _shrink(); |
| errno = saved_errno; |
| } |
| |
| // Notify observers with 3 parameters, errno will not be changed |
| template <typename _B1, typename _B2, typename _B3> |
| void notify(const _B1& b1, const _B2& b2, const _B3& b3) { |
| const int saved_errno = errno; |
| for (size_t i = 0; i < _obs.size(); ++i) { |
| if (_obs[i]) { |
| _obs[i]->on_event(b1, b2, b3); |
| } |
| } |
| _shrink(); |
| errno = saved_errno; |
| } |
| |
| private: |
| void _shrink() { |
| if (_n == _obs.size()) { |
| return; |
| } |
| for (typename ObserverSet::iterator |
| it1 = _obs.begin(), |
| it2 = _obs.begin(); it2 != _obs.end(); ++it2) { |
| if (*it2) { |
| *it1++ = *it2; |
| } |
| } |
| _obs.resize(_n); |
| } |
| |
| size_t _n; |
| ObserverSet _obs; |
| }; |
| |
| namespace detail { |
| |
| // Add const reference for types which is larger than sizeof(void*). This |
| // is reasonable in most cases and making signature of SynchronousEvent<...> |
| // cleaner. |
| template <typename T> struct _AddConstRef { typedef const T& type; }; |
| template <typename T> struct _AddConstRef<T&> { typedef T& type; }; |
| |
| // We have to re-invent some wheels to avoid dependence on <boost/mpl/if.hpp> |
| template <bool cond, typename T1, typename T2> |
| struct if_c { typedef T1 type; }; |
| |
| template <typename T1, typename T2> |
| struct if_c<false, T1, T2> { typedef T2 type; }; |
| |
| template <typename T> |
| struct AddConstRef : public if_c<(sizeof(T)<=sizeof(void*)), |
| T, typename _AddConstRef<T>::type> {}; |
| |
| template <> class EventObserver<void, void, void> { |
| public: |
| virtual ~EventObserver() {} |
| virtual void on_event() = 0; |
| }; |
| |
| template <typename _A1> class EventObserver<_A1, void, void> { |
| public: |
| virtual ~EventObserver() {} |
| virtual void on_event(typename AddConstRef<_A1>::type) = 0; |
| }; |
| |
| template <typename _A1, typename _A2> class EventObserver<_A1, _A2, void> { |
| public: |
| virtual ~EventObserver() {} |
| virtual void on_event(typename AddConstRef<_A1>::type, |
| typename AddConstRef<_A2>::type) = 0; |
| }; |
| |
| template <typename _A1, typename _A2, typename _A3> class EventObserver { |
| public: |
| virtual ~EventObserver() {} |
| virtual void on_event(typename AddConstRef<_A1>::type, |
| typename AddConstRef<_A2>::type, |
| typename AddConstRef<_A3>::type) = 0; |
| }; |
| } // end namespace detail |
| } // end namespace butil |
| |
| #endif // BUTIL_SYNCHRONOUS_EVENT_H |