/* 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-2017 John Preston, https://desktop.telegram.org */ #pragma once #include #include #include namespace rpl { namespace details { template const Arg &const_ref_val(); template < typename Func, typename Arg, typename = decltype(std::declval()(const_ref_val()))> void const_ref_call_helper(Func &handler, const Arg &value, int) { handler(value); } template < typename Func, typename Arg> void const_ref_call_helper(Func &handler, const Arg &value, double) { auto copy = value; handler(std::move(copy)); } } // namespace details struct no_value { no_value() = delete; }; struct no_error { no_error() = delete; }; struct empty_value { }; struct empty_error { }; template class consumer { public: template < typename OnNext, typename OnError, typename OnDone, typename = decltype(std::declval()(std::declval())), typename = decltype(std::declval()(std::declval())), typename = decltype(std::declval()())> consumer( OnNext &&next, OnError &&error, OnDone &&done); bool put_next(Value &&value) const; bool put_next_copy(const Value &value) const; bool put_next_forward(Value &&value) const { return put_next(std::move(value)); } bool put_next_forward(const Value &value) const { return put_next_copy(value); } void put_error(Error &&error) const; void put_error_copy(const Error &error) const; void put_error_forward(Error &&error) const { return put_error(std::move(error)); } void put_error_forward(const Error &error) const { return put_error_copy(error); } void put_done() const; void add_lifetime(lifetime &&lifetime) const; template Type *make_state(Args&& ...args) const; void terminate() const; bool operator==(const consumer &other) const { return _instance == other._instance; } bool operator!=(const consumer &other) const { return !(*this == other); } bool operator<(const consumer &other) const { return _instance < other._instance; } bool operator>(const consumer &other) const { return other < *this; } bool operator<=(const consumer &other) const { return !(other < *this); } bool operator>=(const consumer &other) const { return !(*this < other); } private: class abstract_consumer_instance; template class consumer_instance; template std::shared_ptr ConstructInstance( OnNext &&next, OnError &&error, OnDone &&done); mutable std::shared_ptr _instance; }; template class consumer::abstract_consumer_instance { public: virtual bool put_next(Value &&value) = 0; virtual bool put_next_copy(const Value &value) = 0; virtual void put_error(Error &&error) = 0; virtual void put_error_copy(const Error &error) = 0; virtual void put_done() = 0; void add_lifetime(lifetime &&lifetime); template Type *make_state(Args&& ...args); void terminate(); protected: lifetime _lifetime; bool _terminated = false; std::mutex _mutex; }; template template class consumer::consumer_instance : public consumer::abstract_consumer_instance { public: template consumer_instance( OnNextImpl &&next, OnErrorImpl &&error, OnDoneImpl &&done) : _next(std::forward(next)) , _error(std::forward(error)) , _done(std::forward(done)) { } bool put_next(Value &&value) override; bool put_next_copy(const Value &value) override; void put_error(Error &&error) override; void put_error_copy(const Error &error) override; void put_done() override; private: OnNext _next; OnError _error; OnDone _done; }; template template std::shared_ptr::abstract_consumer_instance> consumer::ConstructInstance( OnNext &&next, OnError &&error, OnDone &&done) { return std::make_shared, std::decay_t, std::decay_t>>( std::forward(next), std::forward(error), std::forward(done)); } template template < typename OnNext, typename OnError, typename OnDone, typename, typename, typename> consumer::consumer( OnNext &&next, OnError &&error, OnDone &&done) : _instance(ConstructInstance( std::forward(next), std::forward(error), std::forward(done))) { } template bool consumer::put_next(Value &&value) const { if (_instance) { if (_instance->put_next(std::move(value))) { return true; } _instance = nullptr; } return false; } template bool consumer::put_next_copy(const Value &value) const { if (_instance) { if (_instance->put_next_copy(value)) { return true; } _instance = nullptr; } return false; } template void consumer::put_error(Error &&error) const { if (_instance) { std::exchange(_instance, nullptr)->put_error(std::move(error)); } } template void consumer::put_error_copy(const Error &error) const { if (_instance) { std::exchange(_instance, nullptr)->put_error_copy(error); } } template void consumer::put_done() const { if (_instance) { std::exchange(_instance, nullptr)->put_done(); } } template void consumer::add_lifetime(lifetime &&lifetime) const { if (_instance) { _instance->add_lifetime(std::move(lifetime)); } else { lifetime.destroy(); } } template template Type *consumer::make_state(Args&& ...args) const { Expects(_instance != nullptr); return _instance->template make_state(std::forward(args)...); } template void consumer::terminate() const { if (_instance) { std::exchange(_instance, nullptr)->terminate(); } } template void consumer::abstract_consumer_instance::add_lifetime( lifetime &&lifetime) { std::unique_lock lock(_mutex); if (_terminated) { lock.unlock(); lifetime.destroy(); } else { _lifetime.add(std::move(lifetime)); } } template template Type *consumer::abstract_consumer_instance::make_state( Args&& ...args) { std::unique_lock lock(_mutex); Expects(!_terminated); return _lifetime.template make_state(std::forward(args)...); } template void consumer::abstract_consumer_instance::terminate() { std::unique_lock lock(_mutex); if (!_terminated) { _terminated = true; auto handler = std::exchange(_lifetime, lifetime()); lock.unlock(); handler.destroy(); } } template template bool consumer::consumer_instance::put_next( Value &&value) { std::unique_lock lock(this->_mutex); if (this->_terminated) { return false; } auto handler = this->_next; lock.unlock(); handler(std::move(value)); return true; } template template bool consumer::consumer_instance::put_next_copy( const Value &value) { std::unique_lock lock(this->_mutex); if (this->_terminated) { return false; } auto handler = this->_next; lock.unlock(); details::const_ref_call_helper(handler, value, 0); return true; } template template void consumer::consumer_instance::put_error( Error &&error) { std::unique_lock lock(this->_mutex); if (!this->_terminated) { auto handler = std::move(this->_error); lock.unlock(); handler(std::move(error)); this->terminate(); } } template template void consumer::consumer_instance::put_error_copy( const Error &error) { std::unique_lock lock(this->_mutex); if (!this->_terminated) { auto handler = std::move(this->_error); lock.unlock(); details::const_ref_call_helper(handler, error, 0); this->terminate(); } } template template void consumer::consumer_instance::put_done() { std::unique_lock lock(this->_mutex); if (!this->_terminated) { auto handler = std::move(this->_done); lock.unlock(); handler(); this->terminate(); } } } // namespace rpl