/* 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 "rpl/lifetime.h" #include namespace rpl { struct no_value { }; struct no_error { no_error() = delete; }; 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 putNext(Value value) const; void putError(Error error) const; void putDone() const; void setLifetime(lifetime &&lifetime) 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 putNext(Value value) = 0; virtual void putError(Error error) = 0; virtual void putDone() = 0; void setLifetime(lifetime &&lifetime); 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 putNext(Value value) override; void putError(Error error) override; void putDone() 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::putNext(Value value) const { if (_instance) { if (_instance->putNext(std::move(value))) { return true; } _instance = nullptr; } return false; } template void consumer::putError(Error error) const { if (_instance) { std::exchange(_instance, nullptr)->putError(std::move(error)); } } template void consumer::putDone() const { if (_instance) { std::exchange(_instance, nullptr)->putDone(); } } template void consumer::setLifetime(lifetime &&lifetime) const { if (_instance) { _instance->setLifetime(std::move(lifetime)); } else { lifetime.destroy(); } } template void consumer::terminate() const { if (_instance) { std::exchange(_instance, nullptr)->terminate(); } } template void consumer::abstract_consumer_instance::setLifetime( lifetime &&lifetime) { std::unique_lock lock(_mutex); if (_terminated) { lock.unlock(); lifetime.destroy(); } else { _lifetime = std::move(lifetime); } } 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::putNext( 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 void consumer::consumer_instance::putError( 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::putDone() { std::unique_lock lock(this->_mutex); if (!this->_terminated) { auto handler = std::move(this->_done); lock.unlock(); handler(); this->terminate(); } } } // namespace rpl