// 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 // std::vector #include // std::find #include // 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 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 class EventObserver; } // All methods are *NOT* thread-safe. // You can copy a SynchronousEvent. template class SynchronousEvent { public: typedef detail::EventObserver<_A1, _A2, _A3> Observer; typedef std::vector 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 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 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 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 struct _AddConstRef { typedef const T& type; }; template struct _AddConstRef { typedef T& type; }; // We have to re-invent some wheels to avoid dependence on template struct if_c { typedef T1 type; }; template struct if_c { typedef T2 type; }; template struct AddConstRef : public if_c<(sizeof(T)<=sizeof(void*)), T, typename _AddConstRef::type> {}; template <> class EventObserver { public: virtual ~EventObserver() {} virtual void on_event() = 0; }; template class EventObserver<_A1, void, void> { public: virtual ~EventObserver() {} virtual void on_event(typename AddConstRef<_A1>::type) = 0; }; template class EventObserver<_A1, _A2, void> { public: virtual ~EventObserver() {} virtual void on_event(typename AddConstRef<_A1>::type, typename AddConstRef<_A2>::type) = 0; }; template 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