/* This file is part of Telegram Desktop, the official desktop version of Telegram messaging app, see https://telegram.org Telegram Desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library. Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once #include "core/vector_of_moveable.h" namespace Notify { using ConnectionId = uint32; // startObservers() must be called after main() started (not in a global variable constructor). // finishObservers() must be called before main() finished (not in a global variable destructor). void startObservers(); void finishObservers(); using StartObservedEventCallback = void(*)(); using FinishObservedEventCallback = void(*)(); namespace internal { using ObservedEvent = uchar; using StartCallback = void(*)(void*); using FinishCallback = void(*)(void*); using UnregisterCallback = void(*)(void*,int connectionIndex); class BaseObservedEventRegistrator { public: BaseObservedEventRegistrator(void *that , StartCallback startCallback , FinishCallback finishCallback , UnregisterCallback unregisterCallback); protected: inline ObservedEvent event() const { return _event; } private: ObservedEvent _event; }; // Handler is one of Function<> instantiations. template struct ObserversList { struct Entry { Flags flags; Handler handler; }; std_::vector_of_moveable entries; QVector freeIndices; }; // If no filtering by flags is done, you can use Flags=int and this value. constexpr int UniversalFlag = 0x01; } // namespace internal // Objects of this class should be constructed in global scope. // startCallback will be called from Notify::startObservers(). // finishCallback will be called from Notify::finishObservers(). template class ObservedEventRegistrator : public internal::BaseObservedEventRegistrator { public: ObservedEventRegistrator(StartObservedEventCallback startCallback, FinishObservedEventCallback finishCallback) : internal::BaseObservedEventRegistrator(static_cast(this), ObservedEventRegistrator::start, ObservedEventRegistrator::finish, ObservedEventRegistrator::unregister) , _startCallback(startCallback), _finishCallback(finishCallback) { } bool started() const { return _list != nullptr; } ConnectionId registerObserver(Flags flags, Handler &&handler) { t_assert(started()); int connectionIndex = doRegisterObserver(flags, std_::forward(handler)); return (static_cast(event()) << 24) | static_cast(connectionIndex + 1); } template void notify(Flags flags, Args&&... args) { t_assert(started()); for (auto &entry : _list->entries) { if (!entry.handler.isNull() && (flags & entry.flags)) { entry.handler.call(std_::forward(args)...); } } } private: using Self = ObservedEventRegistrator; static void start(void *vthat) { Self *that = static_cast(vthat); t_assert(!that->started()); if (that->_startCallback) that->_startCallback(); that->_list = new internal::ObserversList(); } static void finish(void *vthat) { Self *that = static_cast(vthat); if (that->_finishCallback) that->_finishCallback(); delete that->_list; that->_list = nullptr; } static void unregister(void *vthat, int connectionIndex) { Self *that = static_cast(vthat); t_assert(that->started()); auto &entries(that->_list->entries); if (entries.size() <= connectionIndex) return; if (entries.size() == connectionIndex + 1) { for (entries.pop_back(); !entries.isEmpty() && entries.back().handler.isNull();) { entries.pop_back(); } } else { entries[connectionIndex].handler = Handler(); that->_list->freeIndices.push_back(connectionIndex); } } int doRegisterObserver(Flags flags, Handler &&handler) { while (!_list->freeIndices.isEmpty()) { auto freeIndex = _list->freeIndices.back(); _list->freeIndices.pop_back(); if (freeIndex < _list->entries.size()) { _list->entries[freeIndex] = { flags, std_::move(handler) }; return freeIndex; } } _list->entries.push_back({ flags, std_::move(handler) }); return _list->entries.size() - 1; } StartObservedEventCallback _startCallback; FinishObservedEventCallback _finishCallback; internal::ObserversList *_list = nullptr; }; // If no filtering of notifications by Flags is intended use this class. template class SimpleObservedEventRegistrator { public: SimpleObservedEventRegistrator(StartObservedEventCallback startCallback, FinishObservedEventCallback finishCallback) : _implementation(startCallback, finishCallback) { } bool started() const { return _implementation.started(); } ConnectionId registerObserver(Handler &&handler) { return _implementation.registerObserver(internal::UniversalFlag, std_::forward(handler)); } template void notify(Args&&... args) { return _implementation.notify(internal::UniversalFlag, std_::forward(args)...); } private: ObservedEventRegistrator _implementation; }; // Each observer type should have observerRegistered(Notify::ConnectionId connection) method. // Usually it is done by deriving the type from the Notify::Observer base class. // In destructor it should call Notify::unregisterObserver(connection) for all the connections. class Observer; namespace internal { void observerRegisteredDefault(Observer *observer, ConnectionId connection); } // namespace internal void unregisterObserver(ConnectionId connection); class Observer { public: virtual ~Observer() = 0; private: void observerRegistered(ConnectionId connection); friend void internal::observerRegisteredDefault(Observer *observer, ConnectionId connection); QVector _connections; }; namespace internal { template struct ObserverRegisteredGeneric { static inline void call(ObserverType *observer, ConnectionId connection) { observer->observerRegistered(connection); } }; template struct ObserverRegisteredGeneric { static inline void call(ObserverType *observer, ConnectionId connection) { observerRegisteredDefault(observer, connection); } }; } // namespace internal template inline void observerRegistered(ObserverType *observer, ConnectionId connection) { // For derivatives of the Observer class we call special friend function observerRegistered(). // For all other classes we call just a member function observerRegistered(). using ObserverRegistered = internal::ObserverRegisteredGeneric::value>; ObserverRegistered::call(observer, connection); } } // namespace Notify namespace base { namespace internal { using ObservableCallHandlers = base::lambda_unique; void RegisterPendingObservable(ObservableCallHandlers *handlers); void UnregisterActiveObservable(ObservableCallHandlers *handlers); void UnregisterObservable(ObservableCallHandlers *handlers); template struct SubscriptionHandlerHelper { using type = base::lambda_unique; }; template <> struct SubscriptionHandlerHelper { using type = base::lambda_unique; }; template using SubscriptionHandler = typename SubscriptionHandlerHelper::type; // Required because QShared/WeakPointer can't point to void. class BaseObservableData { }; template class CommonObservableData; template class ObservableData; } // namespace internal class Subscription { public: Subscription() = default; Subscription(const Subscription &) = delete; Subscription &operator=(const Subscription &) = delete; Subscription(Subscription &&other) : _node(createAndSwap(other._node)), _removeMethod(other._removeMethod) { } Subscription &operator=(Subscription &&other) { qSwap(_node, other._node); qSwap(_removeMethod, other._removeMethod); return *this; } void destroy() { if (_node) { (*_removeMethod)(_node); delete _node; _node = nullptr; } } ~Subscription() { destroy(); } private: struct Node { Node(const QSharedPointer &observable) : observable(observable) { } Node *next = nullptr; Node *prev = nullptr; QWeakPointer observable; }; using RemoveMethod = void(*)(Node*); Subscription(Node *node, RemoveMethod removeMethod) : _node(node), _removeMethod(removeMethod) { } Node *_node = nullptr; RemoveMethod _removeMethod; template friend class internal::CommonObservableData; template friend class internal::ObservableData; }; template class Observable; namespace internal { template class CommonObservable { public: using Handler = typename CommonObservableData::Handler; Subscription subscribe(Handler &&handler) { if (_data) { _data->append(std_::forward(handler)); } else { _data = MakeShared>(this, std_::forward(handler)); } return _data->last(); } private: QSharedPointer> _data; friend class CommonObservableData; friend class Observable; }; } // namespace internal template class Observable : public internal::CommonObservable { public: void notify(EventType &&event) { if (_data) { _data->notify(std_::move(event)); } } }; namespace internal { template class CommonObservableData : public BaseObservableData { public: using Handler = SubscriptionHandler; CommonObservableData(CommonObservable *observable, Handler &&handler) : _observable(observable) , _begin(new Node(observable->_data, std_::forward(handler))) , _end(_begin) { } void append(Handler &&handler) { auto node = new Node(_observable->_data, std_::forward(handler)); _end->next = node; node->prev = _end; _end = node; } Subscription last() { return { _end, &CommonObservableData::destroyNode }; } bool empty() const { return !_begin; } private: struct Node : public Subscription::Node { Node(const QSharedPointer &observer, Handler &&handler) : Subscription::Node(observer), handler(std_::move(handler)) { } Handler handler; }; void remove(Subscription::Node *node) { if (node->prev) { node->prev->next = node->next; } if (node->next) { node->next->prev = node->prev; } if (_begin == node) { _begin = static_cast(node->next); } if (_end == node) { _end = static_cast(node->prev); } if (_current == node) { _current = static_cast(node->prev); } else if (!_begin) { _observable->_data.reset(); } } static void destroyNode(Subscription::Node *node) { if (auto that = node->observable.lock()) { static_cast(that.data())->remove(node); } } template void notifyEnumerate(CallCurrent callCurrent) { _current = _begin; do { callCurrent(); if (_current) { _current = static_cast(_current->next); } else if (_begin) { _current = _begin; } else { break; } } while (_current); if (!_begin) { _observable->_data.reset(); } } CommonObservable *_observable = nullptr; Node *_begin; Node *_current = nullptr; Node *_end; ObservableCallHandlers _callHandlers; friend class ObservableData; }; template class ObservableData : public CommonObservableData { public: using CommonObservableData::CommonObservableData; void notify(EventType &&event) { if (!_callHandlers) { _callHandlers = [this]() { callHandlers(); }; } if (_events.empty()) { RegisterPendingObservable(&_callHandlers); } _events.push_back(std_::move(event)); } ~ObservableData() { UnregisterObservable(&_callHandlers); } private: void callHandlers() { auto events = createAndSwap(_events); for (auto &event : events) { notifyEnumerate([this, &event]() { _current->handler(event); }); } UnregisterActiveObservable(&_callHandlers); } std_::vector_of_moveable _events; }; template <> class ObservableData : public CommonObservableData { public: using CommonObservableData::CommonObservableData; void notify() { if (!_callHandlers) { _callHandlers = [this]() { callHandlers(); }; } if (!_eventsCount) { RegisterPendingObservable(&_callHandlers); } ++_eventsCount; } ~ObservableData() { UnregisterObservable(&_callHandlers); } private: void callHandlers() { auto eventsCount = createAndSwap(_eventsCount); for (int i = 0; i != eventsCount; ++i) { notifyEnumerate([this]() { _current->handler(); }); } UnregisterActiveObservable(&_callHandlers); } int _eventsCount = 0; }; } // namespace internal template <> class Observable : public internal::CommonObservable { public: void notify() { if (_data) { _data->notify(); } } }; class Subscriber { protected: template int subscribe(base::Observable &observable, Lambda &&handler) { _subscriptions.push_back(observable.subscribe(std_::forward(handler))); return _subscriptions.size() - 1; } template int subscribe(base::Observable *observable, Lambda &&handler) { return subscribe(*observable, std_::forward(handler)); } void unsubscribe(int index) { t_assert(index >= 0 && index < _subscriptions.size()); _subscriptions[index].destroy(); } private: std_::vector_of_moveable _subscriptions; }; void HandleObservables(); } // namespace base