// No copyright #pragma once #include #include #include #include #include #include #include #include "iface/common/exception.hh" namespace nf7 { template class Future final { public: // !! Listener MUST NOT throw any exceptions !! class Internal; using Listener = std::function; class Completer; class Internal final { public: Internal() = default; Internal(T&& v) noexcept : result_(std::move(v)) { } explicit Internal(std::exception_ptr e) noexcept : result_(e) { } Internal(const Internal& src) = default; Internal(Internal&& src) = default; Internal& operator=(const Internal& src) = default; Internal& operator=(Internal&& src) = default; void Complete(T&& v) noexcept { assert(yet()); result_ = std::move(v); Finalize(); } void Throw(std::exception_ptr e = std::current_exception()) noexcept { assert(yet()); assert(nullptr != e); result_ = e; Finalize(); } void Listen(Listener&& listener) { assert(!calling_listener_ && "do not add listener while listener callback"); if (yet()) { try { listeners_.push_back(std::move(listener)); } catch (const std::exception&) { throw Exception("memory shortage"); } } else { calling_listener_ = true; listener(*this); calling_listener_ = false; } } void Ref() noexcept { ++refcnt_; } void Unref() noexcept { if (--refcnt_ == 0 && yet()) { Throw(std::make_exception_ptr(Exception {"forgotten"})); } } bool yet() const noexcept { return std::holds_alternative(result_); } bool done() const noexcept { return std::holds_alternative(result_); } std::exception_ptr error() const noexcept { return std::holds_alternative(result_) ? std::get(result_) : std::exception_ptr {}; } const T& value() const { assert(!yet()); if (auto err = error()) { std::rethrow_exception(err); } assert(done()); return std::get(result_); } private: void Finalize() noexcept { calling_listener_ = true; for (auto& listener : listeners_) { listener(*this); } listeners_.clear(); calling_listener_ = false; } struct Yet {}; std::variant result_; std::vector listeners_; uint64_t refcnt_ = 0; bool calling_listener_ = false; }; Future() = delete; explicit Future(T&& v) : internal_(Internal(std::move(v))) { } explicit Future(std::exception_ptr e) : internal_(Internal(e)) { } Future(const Future&) = default; Future(Future&&) = default; Future& operator=(const Future&) = default; Future& operator=(Future&&) = default; Future& Listen(Listener&& listener) { internal().Listen(std::move(listener)); return *this; } Future& Then(std::function&& f) { Listen([f = std::move(f)](auto& fu) noexcept { if (fu.done()) { f(fu.value()); } }); return *this; } Future& Catch(std::function&& f) { Listen([f = std::move(f)](auto& fu) noexcept { try { fu.value(); } catch (const Exception& e) { f(e); } }); return *this; } template Future ThenAnd(std::function&& f) { typename Future::Completer comp; auto fu = comp.future(); Listen([f = std::move(f), comp = std::move(comp)] (auto& fu) mutable noexcept { try { comp.Complete(f(fu.value())); } catch (...) { comp.Throw(); } }); return fu; } bool yet() const noexcept { return internal().yet(); } bool done() const noexcept { return internal().done(); } std::exception_ptr error() const noexcept { return internal().error(); } const T& value() const { return internal().value(); } private: Future(std::shared_ptr&& in) noexcept : internal_(std::move(in)) { } Future(const std::shared_ptr& in) noexcept : internal_(std::move(in)) { } Internal& internal() noexcept { return std::holds_alternative(internal_) ? std::get(internal_) : *std::get>(internal_); } const Internal& internal() const noexcept { return std::holds_alternative(internal_) ? std::get(internal_) : *std::get>(internal_); } std::variant> internal_; }; template class Future::Completer final { public: Completer() try : internal_(std::make_shared()) { internal_->Ref(); } catch (const std::exception&) { throw Exception("memory shortage"); } ~Completer() noexcept { if (nullptr != internal_) { internal_->Unref(); } } Completer(const Completer& src) noexcept : internal_(src.internal_) { if (nullptr != internal_) { internal_->Ref(); } } Completer(Completer&&) = default; Completer& operator=(const Completer& src) noexcept { if (this != &src) { if (nullptr != internal_) { internal_->Unref(); } internal_ = src.internal_; if (nullptr != internal_) { internal_->Ref(); } } return *this; } Completer& operator=(Completer&& src) noexcept { if (this != &src) { if (nullptr != internal_) { internal_->Unref(); } internal_ = std::move(src.internal_); } return *this; } void Complete(T&& v) noexcept { assert(nullptr != internal_); internal_->Complete(std::move(v)); } void Throw(std::exception_ptr e = std::current_exception()) noexcept { assert(nullptr != internal_); internal_->Throw(e); } void Run(std::function& f) noexcept { assert(nullptr != internal_); try { Complete(f()); } catch (...) { Throw(); } } Future future() const noexcept { assert(nullptr != internal_); return {internal_}; } private: std::shared_ptr internal_; }; } // namespace nf7