// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once #include "td/utils/CancellationToken.h" #include "td/utils/common.h" #include "td/utils/invoke.h" #include "td/utils/MovableValue.h" #include "td/utils/Status.h" #include #include #include namespace td { template class PromiseInterface { public: PromiseInterface() = default; PromiseInterface(const PromiseInterface &) = delete; PromiseInterface &operator=(const PromiseInterface &) = delete; PromiseInterface(PromiseInterface &&) = default; PromiseInterface &operator=(PromiseInterface &&) = default; virtual ~PromiseInterface() = default; virtual void set_value(T &&value) { set_result(std::move(value)); } virtual void set_error(Status &&error) { set_result(std::move(error)); } virtual void set_result(Result &&result) { if (result.is_ok()) { set_value(result.move_as_ok()); } else { set_error(result.move_as_error()); } } virtual bool is_cancellable() const { return false; } virtual bool is_canceled() const { return false; } }; template class SafePromise; template class Promise; namespace detail { template struct GetArg final : public GetArg {}; template class GetArg { public: using type = Arg; }; template class GetArg { public: using type = Arg; }; template using get_arg_t = std::decay_t::type>; template struct DropResult { using type = T; }; template struct DropResult> { using type = T; }; template using drop_result_t = typename DropResult::type; template class LambdaPromise : public PromiseInterface { enum class State : int32 { Empty, Ready, Complete }; public: void set_value(ValueT &&value) override { CHECK_IMPL(state_.get() == State::Ready, file_, line_); do_ok(std::move(value)); state_ = State::Complete; } void set_error(Status &&error) override { if (state_.get() == State::Ready) { do_error(std::move(error)); state_ = State::Complete; } } LambdaPromise(const LambdaPromise &) = delete; LambdaPromise &operator=(const LambdaPromise &) = delete; LambdaPromise(LambdaPromise &&) = default; LambdaPromise &operator=(LambdaPromise &&) = default; ~LambdaPromise() override { if (state_.get() == State::Ready) { do_error(Status::Error("Lost promise")); } } template explicit LambdaPromise(const char *file, int line, FromT &&func) : func_(std::forward(func)), state_(State::Ready), file_(file), line_(line) { } private: FunctionT func_; MovableValue state_{State::Empty}; const char *file_ = ""; int line_ = 0; template std::enable_if_t>::value, void> do_error(Status &&status) { func_(Result(std::move(status))); } template std::enable_if_t>::value, void> do_error(Y &&status) { func_(Auto()); } template std::enable_if_t>::value, void> do_ok(ValueT &&value) { func_(Result(std::move(value))); } template std::enable_if_t>::value, void> do_ok(ValueT &&value) { func_(std::move(value)); } }; template struct is_promise_interface : std::false_type {}; template struct is_promise_interface> : std::true_type {}; template struct is_promise_interface> : std::true_type {}; template struct is_promise_interface_ptr : std::false_type {}; template struct is_promise_interface_ptr> : std::true_type {}; template ::value, bool> has_t = false> auto lambda_promise(F &&f) { return LambdaPromise>>, std::decay_t>(__FILE__, __LINE__, std::forward(f)); } template ::value, bool> has_t = true> auto lambda_promise(F &&f) { return LambdaPromise>(__FILE__, __LINE__, std::forward(f)); } template >::value, bool> from_promise_interface = true> auto &&promise_interface(F &&f) { return std::forward(f); } template >::value, bool> from_promise_interface = false> auto promise_interface(F &&f) { return lambda_promise(std::forward(f)); } template >::value, bool> from_promise_interface = true> auto promise_interface_ptr(F &&f) { return std::forward(f); } template >::value, bool> from_promise_interface = false> auto promise_interface_ptr(F &&f) { return td::make_unique(std::forward(f)))>>( promise_interface(std::forward(f))); } } // namespace detail template class Promise { public: void set_value(T &&value) { if (!promise_) { return; } promise_->set_value(std::move(value)); promise_.reset(); } void set_error(Status &&error) { if (!promise_) { return; } promise_->set_error(std::move(error)); promise_.reset(); } void set_result(Result &&result) { if (!promise_) { return; } promise_->set_result(std::move(result)); promise_.reset(); } void reset() { promise_.reset(); } bool is_cancellable() const { if (!promise_) { return false; } return promise_->is_cancellable(); } bool is_canceled() const { if (!promise_) { return false; } return promise_->is_canceled(); } unique_ptr> release() { return std::move(promise_); } Promise() = default; explicit Promise(unique_ptr> promise) : promise_(std::move(promise)) { } Promise(Auto) { } Promise(SafePromise &&other); Promise &operator=(SafePromise &&other); template , Promise>::value, int> = 0> Promise(F &&f) : promise_(detail::promise_interface_ptr(std::forward(f))) { } explicit operator bool() const noexcept { return static_cast(promise_); } private: unique_ptr> promise_; }; template class SafePromise { public: SafePromise(Promise promise, Result result) : promise_(std::move(promise)), result_(std::move(result)) { } SafePromise(const SafePromise &) = delete; SafePromise &operator=(const SafePromise &) = delete; SafePromise(SafePromise &&) = default; SafePromise &operator=(SafePromise &&) = default; ~SafePromise() { if (promise_) { promise_.set_result(std::move(result_)); } } Promise release() { return std::move(promise_); } private: Promise promise_; Result result_; }; template Promise::Promise(SafePromise &&other) : Promise(other.release()) { } template Promise &Promise::operator=(SafePromise &&other) { *this = other.release(); return *this; } namespace detail { template class CancellablePromise final : public PromiseT { public: template CancellablePromise(CancellationToken cancellation_token, ArgsT &&...args) : PromiseT(std::forward(args)...), cancellation_token_(std::move(cancellation_token)) { } bool is_cancellable() const final { return true; } bool is_canceled() const final { return static_cast(cancellation_token_); } private: CancellationToken cancellation_token_; }; template class JoinPromise final : public PromiseInterface { public: explicit JoinPromise(ArgsT &&...arg) : promises_(std::forward(arg)...) { } void set_value(Unit &&) final { tuple_for_each(promises_, [](auto &promise) { promise.set_value(Unit()); }); } void set_error(Status &&error) final { tuple_for_each(promises_, [&error](auto &promise) { promise.set_error(error.clone()); }); } private: std::tuple...> promises_; }; } // namespace detail class PromiseCreator { public: template >> static Promise lambda_impl(const char *file, int line, OkT &&ok) { return Promise( td::make_unique>>(file, line, std::forward(ok))); } #define lambda(...) lambda_impl(__FILE__, __LINE__, __VA_ARGS__) template >> static auto cancellable_lambda_impl(const char *file, int line, CancellationToken cancellation_token, OkT &&ok) { return Promise(td::make_unique>>>( std::move(cancellation_token), file, line, std::forward(ok))); } #define cancellable_lambda(...) cancellable_lambda_impl(__FILE__, __LINE__, __VA_ARGS__) template static Promise<> join(ArgsT &&...args) { return Promise<>(td::make_unique>(std::forward(args)...)); } }; inline void set_promises(vector> &promises) { auto moved_promises = std::move(promises); promises.clear(); for (auto &promise : moved_promises) { promise.set_value(Unit()); } } template void fail_promises(vector> &promises, Status &&error) { CHECK(error.is_error()); auto moved_promises = std::move(promises); promises.clear(); auto size = moved_promises.size(); if (size == 0) { return; } size--; for (size_t i = 0; i < size; i++) { auto &promise = moved_promises[i]; if (promise) { promise.set_error(error.clone()); } } moved_promises[size].set_error(std::move(error)); } } // namespace td