diff --git a/Telegram/SourceFiles/core/observer.cpp b/Telegram/SourceFiles/core/observer.cpp index 65a9ffd7c5..6ec33a341a 100644 --- a/Telegram/SourceFiles/core/observer.cpp +++ b/Telegram/SourceFiles/core/observer.cpp @@ -22,50 +22,69 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "core/observer.h" namespace Notify { +namespace internal { namespace { -using StartCallbacksList = QVector; -using FinishCallbacksList = QVector; +struct StartCallbackData { + void *that; + StartCallback call; +}; +struct FinishCallbackData { + void *that; + FinishCallback call; +}; +struct UnregisterCallbackData { + void *that; + UnregisterCallback call; +}; +using StartCallbacksList = QVector; +using FinishCallbacksList = QVector; NeverFreedPointer StartCallbacks; NeverFreedPointer FinishCallbacks; -UnregisterObserverCallback UnregisterCallbacks[256]/* = { nullptr }*/; +UnregisterCallbackData UnregisterCallbacks[256]/* = { nullptr }*/; ObservedEvent LastRegisteredEvent/* = 0*/; } // namespace +} // namespace internal void startObservers() { - if (!StartCallbacks) return; + if (!internal::StartCallbacks) return; - for (auto &callback : *StartCallbacks) { - callback(); + for (auto &callback : *internal::StartCallbacks) { + callback.call(callback.that); } } void finishObservers() { - if (!FinishCallbacks) return; + if (!internal::FinishCallbacks) return; - for (auto &callback : *FinishCallbacks) { - callback(); + for (auto &callback : *internal::FinishCallbacks) { + callback.call(callback.that); } - StartCallbacks.clear(); - FinishCallbacks.clear(); + internal::StartCallbacks.clear(); + internal::FinishCallbacks.clear(); } -ObservedEventRegistrator::ObservedEventRegistrator(StartObservedEventCallback startCallback -, FinishObservedEventCallback finishCallback -, UnregisterObserverCallback unregisterCallback) { +namespace internal { + +BaseObservedEventRegistrator::BaseObservedEventRegistrator(void *that +, StartCallback startCallback +, FinishCallback finishCallback +, UnregisterCallback unregisterCallback) { _event = LastRegisteredEvent++; StartCallbacks.makeIfNull(); - StartCallbacks->push_back(startCallback); + StartCallbacks->push_back({ that, startCallback }); FinishCallbacks.makeIfNull(); - FinishCallbacks->push_back(finishCallback); + FinishCallbacks->push_back({ that, finishCallback }); - UnregisterCallbacks[_event] = unregisterCallback; + UnregisterCallbacks[_event] = { that, unregisterCallback }; } +} // namespace internal + // Observer base interface. Observer::~Observer() { for_const (auto connection, _connections) { @@ -78,10 +97,11 @@ void Observer::observerRegistered(ConnectionId connection) { } void unregisterObserver(ConnectionId connection) { - auto event = static_cast(connection >> 24); + auto event = static_cast(connection >> 24); auto connectionIndex = int(connection & 0x00FFFFFFU) - 1; - if (connectionIndex >= 0 && UnregisterCallbacks[event]) { - UnregisterCallbacks[event](connectionIndex); + auto &callback = internal::UnregisterCallbacks[event]; + if (connectionIndex >= 0 && callback.call && callback.that) { + callback.call(callback.that, connectionIndex); } } diff --git a/Telegram/SourceFiles/core/observer.h b/Telegram/SourceFiles/core/observer.h index 9ffa55f76d..1530328f85 100644 --- a/Telegram/SourceFiles/core/observer.h +++ b/Telegram/SourceFiles/core/observer.h @@ -24,7 +24,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Notify { -using ObservedEvent = uchar; using ConnectionId = uint32; // startObservers() must be called after main() started (not in a global variable constructor). @@ -33,19 +32,23 @@ void startObservers(); void finishObservers(); using StartObservedEventCallback = void(*)(); -using UnregisterObserverCallback = void(*)(int connectionIndex); using FinishObservedEventCallback = void(*)(); -// Objects of this class should be constructed in global scope. -// startCallback will be called from Notify::startObservers(). -// finishCallback will be called from Notify::finishObservers(). -// unregisterCallback will be used to destroy connections. -class ObservedEventRegistrator { -public: - ObservedEventRegistrator(StartObservedEventCallback startCallback, - FinishObservedEventCallback finishCallback, - UnregisterObserverCallback unregisterCallback); +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; } @@ -55,6 +58,138 @@ private: }; +// 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(reinterpret_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 = reinterpret_cast(vthat); + + t_assert(!that->started()); + if (that->_startCallback) that->_startCallback(); + that->_list = new internal::ObserversList(); + } + static void finish(void *vthat) { + Self *that = reinterpret_cast(vthat); + + if (that->_finishCallback) that->_finishCallback(); + delete that->_list; + that->_list = nullptr; + } + static void unregister(void *vthat, int connectionIndex) { + Self *that = reinterpret_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. @@ -78,66 +213,6 @@ private: }; -inline ConnectionId observerConnectionId(ObservedEvent event, int connectionIndex) { - t_assert(connectionIndex >= 0 && connectionIndex < 0x01000000); - return (static_cast(event) << 24) | (connectionIndex + 1); -} - -// 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 this value in both -// Notify::registerObserver() and Notify::notifyObservers() -constexpr int UniversalFlag = 0x01; - -template -ConnectionId registerObserver(ObservedEvent event, ObserversList &list, 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) }); - int connectionIndex = list.entries.size() - 1; - return (static_cast(event) << 24) | static_cast(connectionIndex + 1); -} - -template -void unregisterObserver(ObserversList &list, int connectionIndex) { - auto &entries(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(); - list.freeIndices.push_back(connectionIndex); - } -} - -template -void notifyObservers(ObserversList &list, Flags flags, Args&&... args) { - for (auto &entry : list.entries) { - if (!entry.handler.isNull() && (flags & entry.flags)) { - entry.handler.call(std_::forward(args)...); - } - } -} - namespace internal { template diff --git a/Telegram/SourceFiles/mtproto/file_download.cpp b/Telegram/SourceFiles/mtproto/file_download.cpp index 71353c24a2..ab8a25af52 100644 --- a/Telegram/SourceFiles/mtproto/file_download.cpp +++ b/Telegram/SourceFiles/mtproto/file_download.cpp @@ -1100,38 +1100,18 @@ namespace { using internal::ImageLoadedHandler; -using ImageLoadedObserversList = Notify::ObserversList; -NeverFreedPointer ImageLoadedObservers; - -void StartCallback() { - ImageLoadedObservers.makeIfNull(); -} -void FinishCallback() { - ImageLoadedObservers.clear(); -} -void UnregisterCallback(int connectionIndex) { - t_assert(!ImageLoadedObservers.isNull()); - Notify::unregisterObserver(*ImageLoadedObservers, connectionIndex); -} -Notify::ObservedEventRegistrator creator(StartCallback, FinishCallback, UnregisterCallback); - -bool Started() { - return !ImageLoadedObservers.isNull(); -} +Notify::SimpleObservedEventRegistrator creator(nullptr, nullptr); } // namespace namespace internal { Notify::ConnectionId plainRegisterImageLoadedObserver(ImageLoadedHandler &&handler) { - t_assert(Started()); - auto connectionId = Notify::registerObserver(creator.event(), *ImageLoadedObservers - , Notify::UniversalFlag, std_::forward(handler)); - return connectionId; + return creator.registerObserver(std_::forward(handler)); } void notifyImageLoaded() { - Notify::notifyObservers(*ImageLoadedObservers, Notify::UniversalFlag); + creator.notify(); } } // namespace internal diff --git a/Telegram/SourceFiles/observer_peer.cpp b/Telegram/SourceFiles/observer_peer.cpp index d1cbbde794..ca52c4a26b 100644 --- a/Telegram/SourceFiles/observer_peer.cpp +++ b/Telegram/SourceFiles/observer_peer.cpp @@ -29,11 +29,9 @@ void emitPeerUpdated(); } // namespace App namespace Notify { -namespace internal { namespace { -using PeerObserversList = ObserversList; -NeverFreedPointer PeerUpdateObservers; +using internal::PeerUpdateHandler; using SmallUpdatesList = QVector; NeverFreedPointer SmallUpdates; @@ -41,34 +39,25 @@ using AllUpdatesList = QMap; NeverFreedPointer AllUpdates; void StartCallback() { - PeerUpdateObservers.makeIfNull(); SmallUpdates.makeIfNull(); AllUpdates.makeIfNull(); } void FinishCallback() { - PeerUpdateObservers.clear(); SmallUpdates.clear(); AllUpdates.clear(); } -void UnregisterCallback(int connectionIndex) { - t_assert(!PeerUpdateObservers.isNull()); - unregisterObserver(*PeerUpdateObservers, connectionIndex); -} -ObservedEventRegistrator creator(StartCallback, FinishCallback, UnregisterCallback); - -bool Started() { - return !PeerUpdateObservers.isNull(); -} +ObservedEventRegistrator creator(StartCallback, FinishCallback); } // namespace +namespace internal { + ConnectionId plainRegisterPeerObserver(PeerUpdateFlags events, PeerUpdateHandler &&handler) { - t_assert(Started()); - auto connectionId = registerObserver(creator.event(), *PeerUpdateObservers - , events, std_::forward(handler)); - return connectionId; + return creator.registerObserver(events, std_::forward(handler)); } +} // namespace internal + void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) { if (!(mergeTo.flags & PeerUpdateFlag::NameChanged)) { if (mergeFrom.flags & PeerUpdateFlag::NameChanged) { @@ -79,53 +68,51 @@ void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) { mergeTo.flags |= mergeFrom.flags; } -} // namespace internal - void peerUpdatedDelayed(const PeerUpdate &update) { - t_assert(internal::Started()); + t_assert(creator.started()); - int existingUpdatesCount = internal::SmallUpdates->size(); + int existingUpdatesCount = SmallUpdates->size(); for (int i = 0; i < existingUpdatesCount; ++i) { - auto &existingUpdate = (*internal::SmallUpdates)[i]; + auto &existingUpdate = (*SmallUpdates)[i]; if (existingUpdate.peer == update.peer) { - internal::mergePeerUpdate(existingUpdate, update); + mergePeerUpdate(existingUpdate, update); return; } } - if (internal::AllUpdates->isEmpty()) { + if (AllUpdates->isEmpty()) { if (existingUpdatesCount < 5) { - internal::SmallUpdates->push_back(update); + SmallUpdates->push_back(update); } else { - internal::AllUpdates->insert(update.peer, update); + AllUpdates->insert(update.peer, update); } } else { - auto it = internal::AllUpdates->find(update.peer); - if (it != internal::AllUpdates->cend()) { - internal::mergePeerUpdate(it.value(), update); + auto it = AllUpdates->find(update.peer); + if (it != AllUpdates->cend()) { + mergePeerUpdate(it.value(), update); return; } - internal::AllUpdates->insert(update.peer, update); + AllUpdates->insert(update.peer, update); } } void peerUpdatedSendDelayed() { App::emitPeerUpdated(); - t_assert(internal::Started()); + t_assert(creator.started()); - if (internal::SmallUpdates->isEmpty()) return; + if (SmallUpdates->isEmpty()) return; - auto smallList = createAndSwap(*internal::SmallUpdates); - auto allList = createAndSwap(*internal::AllUpdates); + auto smallList = createAndSwap(*SmallUpdates); + auto allList = createAndSwap(*AllUpdates); for_const (auto &update, smallList) { - notifyObservers(*internal::PeerUpdateObservers, update.flags, update); + creator.notify(update.flags, update); } for_const (auto &update, allList) { - notifyObservers(*internal::PeerUpdateObservers, update.flags, update); + creator.notify(update.flags, update); } - if (internal::SmallUpdates->isEmpty()) { - std::swap(smallList, *internal::SmallUpdates); - internal::SmallUpdates->resize(0); + if (SmallUpdates->isEmpty()) { + std::swap(smallList, *SmallUpdates); + SmallUpdates->resize(0); } } diff --git a/Telegram/SourceFiles/ui/filedialog.cpp b/Telegram/SourceFiles/ui/filedialog.cpp index aaf02498c3..28e065adfa 100644 --- a/Telegram/SourceFiles/ui/filedialog.cpp +++ b/Telegram/SourceFiles/ui/filedialog.cpp @@ -240,9 +240,6 @@ namespace { using internal::QueryUpdateHandler; -using QueryObserversList = Notify::ObserversList; -NeverFreedPointer QueryUpdateObservers; - struct Query { enum class Type { ReadFile, @@ -268,55 +265,51 @@ using QueryList = QList; NeverFreedPointer Queries; void StartCallback() { - QueryUpdateObservers.makeIfNull(); Queries.makeIfNull(); } + void FinishCallback() { - QueryUpdateObservers.clear(); Queries.clear(); } -void UnregisterCallback(int connectionIndex) { - t_assert(!QueryUpdateObservers.isNull()); - Notify::unregisterObserver(*QueryUpdateObservers, connectionIndex); -} -Notify::ObservedEventRegistrator creator(StartCallback, FinishCallback, UnregisterCallback); -bool Started() { - return !QueryUpdateObservers.isNull(); -} +Notify::SimpleObservedEventRegistrator creator(StartCallback, FinishCallback); } // namespace QueryId queryReadFile(const QString &caption, const QString &filter) { - t_assert(Started()); + t_assert(creator.started()); + Queries->push_back(Query(Query::Type::ReadFile, caption, filter)); Global::RefHandleFileDialogQueue().call(); return Queries->back().id; } QueryId queryReadFiles(const QString &caption, const QString &filter) { - t_assert(Started()); + t_assert(creator.started()); + Queries->push_back(Query(Query::Type::ReadFiles, caption, filter)); Global::RefHandleFileDialogQueue().call(); return Queries->back().id; } QueryId queryWriteFile(const QString &caption, const QString &filter, const QString &filePath) { - t_assert(Started()); + t_assert(creator.started()); + Queries->push_back(Query(Query::Type::WriteFile, caption, filter, filePath)); Global::RefHandleFileDialogQueue().call(); return Queries->back().id; } QueryId queryReadFolder(const QString &caption) { - t_assert(Started()); + t_assert(creator.started()); + Queries->push_back(Query(Query::Type::ReadFolder, caption)); Global::RefHandleFileDialogQueue().call(); return Queries->back().id; } bool processQuery() { - if (!Started() || !Global::started() || Queries->isEmpty()) return false; + if (!creator.started() || !Global::started() || Queries->isEmpty()) return false; auto query = Queries->front(); Queries->pop_front(); @@ -364,19 +357,16 @@ bool processQuery() { } // No one know what happened during filedialogGet*() call in the event loop. - if (!Started() || !Global::started()) return false; + if (!creator.started() || !Global::started()) return false; - Notify::notifyObservers(*QueryUpdateObservers, Notify::UniversalFlag, update); + creator.notify(update); return true; } namespace internal { Notify::ConnectionId plainRegisterObserver(QueryUpdateHandler &&handler) { - t_assert(Started()); - auto connectionId = Notify::registerObserver(creator.event(), *QueryUpdateObservers - , Notify::UniversalFlag, std_::forward(handler)); - return connectionId; + return creator.registerObserver(std_::forward(handler)); } } // namespace internal