| /** |
| *Licensed to the Apache Software Foundation (ASF) under one |
| *or more contributor license agreements. See the NOTICE file |
| *distributed with this work for additional information |
| *regarding copyright ownership. The ASF licenses this file |
| *to you under the Apache License, Version 2.0 (the |
| *"License"); you may not use this file except in compliance |
| *with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| *Unless required by applicable law or agreed to in writing, |
| *software distributed under the License is distributed on an |
| *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| *specific language governing permissions and limitations |
| *under the License. |
| */ |
| |
| #pragma once |
| |
| #include <exception> |
| #include <utility> |
| |
| #include "celix/impl/SharedPromiseState.h" |
| #include "celix/Promise.h" |
| #include "celix/PromiseIllegalStateException.h" |
| |
| namespace celix { |
| |
| /** |
| * @brief A Deferred Promise resolution. |
| * |
| * <p> |
| * Instances of this class can be used to create a {@link celix::Promise} that can be |
| * resolved in the future. The {@link #getPromise() associated} Promise can be |
| * successfully resolved with {@link #resolve(T)} or resolved with a |
| * failure with {@link #fail(const std::exception&)}. It can also be resolved with the |
| * resolution of another promise using {@link #resolveWith(Promise)}. |
| * |
| * <p> |
| * The associated Promise can be provided to any one, but the Deferred object |
| * should be made available only to the party that will responsible for |
| * resolving the Promise. |
| * |
| * @tparam <T> The value type associated with the created Promise. |
| */ |
| template<typename T> |
| class Deferred { |
| public: |
| using type = T; |
| |
| explicit Deferred(std::shared_ptr<celix::impl::SharedPromiseState<T>> state); |
| |
| /** |
| * @brief Fail the Promise associated with this Deferred. |
| * |
| * <p/> |
| * After the associated Promise is resolved with the specified failure, all registered callbacks are called and any |
| * chained Promises are resolved. |
| * <p/> |
| * Resolving the associated Promise happens-before any registered callback is called. That is, in a registered |
| * callback, Promise.isDone() must return true and Promise.getValue() and Promise.getFailure() must not block. |
| * |
| * If the associated promise is already resolved, the call will be ignored. |
| * |
| * @param failure The failure in the form of an exception pointer. |
| */ |
| void fail(const std::exception_ptr& failure); |
| |
| /** |
| * @brief Try to fail the Promise associated with this Deferred. |
| * |
| * Same as `fail`, but will return `true` if the associated promise was successfully failed and `false` if |
| * the associated promise was already resolved. |
| */ |
| bool tryFail(const std::exception_ptr& failure); |
| |
| /** |
| * @brief Fail the Promise associated with this Deferred. |
| * |
| * <p/> |
| * After the associated Promise is resolved with the specified failure, all registered callbacks are called and any |
| * chained Promises are resolved. |
| * <p/> |
| * Resolving the associated Promise happens-before any registered callback is called. That is, in a registered |
| * callback, Promise.isDone() must return true and Promise.getValue() and Promise.getFailure() must not block. |
| * |
| * If the associated promise is already resolved, the call will be ignored. |
| * |
| * @param failure The failure in the form of an const std::exception reference. |
| */ |
| template<typename E, typename std::enable_if_t< std::is_base_of_v<std::exception, E>, bool> = true > |
| void fail(const E& failure); |
| |
| |
| /** |
| * @brief Try to fail the Promise associated with this Deferred. |
| * |
| * Same as `fail`, but will return `true` if the associated promise was successfully failed and `false` if |
| * the associated promise was already resolved. |
| */ |
| template<typename E, typename std::enable_if_t< std::is_base_of_v<std::exception, E>, bool> = true > |
| bool tryFail(const E& failure); |
| |
| /** |
| * @brief Returns the Promise associated with this Deferred. |
| * |
| * <p> |
| * All Promise objects created by the associated Promise will use the |
| * executors of the associated Promise. |
| * </p> |
| * |
| * @return The Promise associated with this Deferred. |
| */ |
| [[nodiscard]] Promise<T> getPromise(); |
| |
| /** |
| * @brief Resolve the Promise associated with this Deferred. |
| * |
| * <p/> |
| * After the associated Promise is resolved with the specified value, all registered callbacks are called and any |
| * chained Promises are resolved. |
| * <p/> |
| * Resolving the associated Promise happens-before any registered callback is called. That is, in a registered |
| * callback, Promise.isDone() must return true and Promise.getValue() and Promise.getFailure() must not block. |
| * |
| * If the associated promise is already resolved, the call will be ignored. |
| * |
| * @param value The value of the resolved Promise. |
| */ |
| void resolve(T&& value); |
| |
| /** |
| * @brief Resolve the Promise associated with this Deferred. |
| * |
| * <p/> |
| * After the associated Promise is resolved with the specified value, all registered callbacks are called and any |
| * chained Promises are resolved. |
| * <p/> |
| * Resolving the associated Promise happens-before any registered callback is called. That is, in a registered |
| * callback, Promise.isDone() must return true and Promise.getValue() and Promise.getFailure() must not block. |
| * |
| * If the associated promise is already resolved, the call will be ignored. |
| * |
| * @param value The value of the resolved Promise. |
| */ |
| void resolve(const T& value); |
| |
| /** |
| * @brief Try to resolve the Promise associated with this Deferred. |
| * |
| * Same as `resolve`, but will return `true` if the associated promise was successfully resolved and `false` if |
| * the associated promise was already resolved. |
| */ |
| bool tryResolve(T&& value); |
| |
| /** |
| * @brief Try to resolve the Promise associated with this Deferred. |
| * |
| * Same as `resolve`, but will return `true` if the associated promise was successfully resolved and `false` if |
| * the associated promise was already resolved. |
| */ |
| bool tryResolve(const T& value); |
| |
| /** |
| * @brief Resolve the Promise associated with this Deferred with the specified Promise. |
| * |
| * <p/> |
| * If the specified Promise is successfully resolved, the associated Promise is resolved with the value of the |
| * specified Promise. If the specified Promise is resolved with a failure, the associated Promise is resolved with |
| * the failure of the specified Promise. |
| * <p/> |
| * After the associated Promise is resolved with the specified Promise, all registered callbacks are called and any |
| * chained Promises are resolved. |
| * <p/> |
| * Resolving the associated Promise happens-before any registered callback is called. That is, in a registered |
| * callback, Promise.isDone() must return true and Promise.getValue() and Promise.getFailure() must not block |
| * |
| * @tparam U The promise type must be assignable from T. |
| * @param with A Promise whose value or failure will be used to resolve the associated Promise. Must not be null. |
| * @return A Promise that is resolved only when the associated Promise is resolved by the specified Promise. The |
| * returned Promise will be successfully resolved, with the value null, if the associated Promise was resolved by |
| * the specified Promise. The returned Promise will be resolved with a failure of IllegalStateException if the |
| * associated Promise was already resolved when the specified Promise was resolved. |
| */ |
| template<typename U> |
| celix::Promise<void> resolveWith(celix::Promise<U> with); |
| |
| private: |
| std::shared_ptr<celix::impl::SharedPromiseState<T>> state; |
| }; |
| |
| template<> |
| class Deferred<void> { |
| public: |
| using type = void; |
| |
| explicit Deferred(std::shared_ptr<celix::impl::SharedPromiseState<void>> state); |
| |
| /** |
| * @brief Fail the Promise associated with this Deferred. |
| * |
| * <p/> |
| * After the associated Promise is resolved with the specified failure, all registered callbacks are called and any |
| * chained Promises are resolved. |
| * <p/> |
| * Resolving the associated Promise happens-before any registered callback is called. That is, in a registered |
| * callback, Promise.isDone() must return true and Promise.getValue() and Promise.getFailure() must not block. |
| * |
| * If the associated promise is already resolved, the call will be ignored. |
| * |
| * @param failure The failure in the form of an exception pointer. |
| */ |
| void fail(const std::exception_ptr& failure); |
| |
| /** |
| * @brief Try to fail the Promise associated with this Deferred. |
| * |
| * Same as `fail`, but will return `true` if the associated promise was successfully failed and `false` if |
| * the associated promise was already resolved. |
| */ |
| bool tryFail(const std::exception_ptr& failure); |
| |
| /** |
| * @brief Fail the Promise associated with this Deferred. |
| * |
| * <p/> |
| * After the associated Promise is resolved with the specified failure, all registered callbacks are called and any |
| * chained Promises are resolved. |
| * <p/> |
| * Resolving the associated Promise happens-before any registered callback is called. That is, in a registered |
| * callback, Promise.isDone() must return true and Promise.getValue() and Promise.getFailure() must not block. |
| * |
| * If the associated promise is already resolved, the call will be ignored. |
| * |
| * @param failure The failure in the form of an const std::exception reference. |
| */ |
| template<typename E, typename std::enable_if_t< std::is_base_of_v<std::exception, E>, bool> = true > |
| void fail(const E& failure); |
| |
| /** |
| * @brief Try to fail the Promise associated with this Deferred. |
| * |
| * Same as `fail`, but will return `true` if the associated promise was successfully failed and `false` if |
| * the associated promise was already resolved. |
| */ |
| template<typename E, typename std::enable_if_t< std::is_base_of_v<std::exception, E>, bool> = true > |
| bool tryFail(const E& failure); |
| |
| /** |
| * @brief Returns the Promise associated with this Deferred. |
| * |
| * <p> |
| * All Promise objects created by the associated Promise will use the |
| * executors of the associated Promise. |
| * </p> |
| * |
| * @return The Promise associated with this Deferred. |
| */ |
| [[nodiscard]] Promise<void> getPromise(); |
| |
| /** |
| * @brief Resolve the Promise associated with this Deferred. |
| * |
| * <p/> |
| * After the associated Promise is resolved with the specified value, all registered callbacks are called and any |
| * chained Promises are resolved. |
| * <p/> |
| * Resolving the associated Promise happens-before any registered callback is called. That is, in a registered |
| * callback, Promise.isDone() must return true and Promise.getValue() and Promise.getFailure() must not block. |
| * |
| * If the associated promise is already resolved, the call will be ignored. |
| * |
| * @param value The value of the resolved Promise. |
| */ |
| void resolve(); |
| |
| /** |
| * @brief Try to resolve the Promise associated with this Deferred. |
| * |
| * Same as `resolve`, but will return `true` if the associated promise was successfully resolved and `false` if |
| * the associated promise was already resolved. |
| */ |
| bool tryResolve(); |
| |
| template<typename U> |
| celix::Promise<void> resolveWith(celix::Promise<U> with); |
| private: |
| std::shared_ptr<celix::impl::SharedPromiseState<void>> state; |
| }; |
| } |
| |
| |
| |
| /********************************************************************************* |
| Implementation |
| *********************************************************************************/ |
| |
| template<typename T> |
| celix::Deferred<T>::Deferred(std::shared_ptr<celix::impl::SharedPromiseState<T>> _state) : state{std::move(_state)} {} |
| |
| inline celix::Deferred<void>::Deferred(std::shared_ptr<celix::impl::SharedPromiseState<void>> _state) : state{std::move(_state)} {} |
| |
| template<typename T> |
| void celix::Deferred<T>::fail(const std::exception_ptr& failure) { |
| state->tryFail(failure); |
| } |
| |
| template<typename T> |
| bool celix::Deferred<T>::tryFail(const std::exception_ptr& failure) { |
| return state->tryFail(failure); |
| } |
| |
| inline void celix::Deferred<void>::fail(const std::exception_ptr& failure) { |
| state->tryFail(failure); |
| } |
| |
| inline bool celix::Deferred<void>::tryFail(const std::exception_ptr& failure) { |
| return state->tryFail(failure); |
| } |
| |
| template<typename T> |
| template<typename E, typename std::enable_if_t< std::is_base_of_v<std::exception, E>, bool>> |
| void celix::Deferred<T>::fail(const E& failure) { |
| state->template tryFail<E>(failure); |
| } |
| |
| template<typename T> |
| template<typename E, typename std::enable_if_t< std::is_base_of_v<std::exception, E>, bool>> |
| bool celix::Deferred<T>::tryFail(const E& failure) { |
| return state->template tryFail<E>(failure); |
| } |
| |
| template<typename E, typename std::enable_if_t< std::is_base_of_v<std::exception, E>, bool>> |
| void celix::Deferred<void>::fail(const E& failure) { |
| state->tryFail<E>(failure); |
| } |
| |
| template<typename E, typename std::enable_if_t< std::is_base_of_v<std::exception, E>, bool>> |
| bool celix::Deferred<void>::tryFail(const E& failure) { |
| return state->tryFail<E>(failure); |
| } |
| |
| template<typename T> |
| celix::Promise<T> celix::Deferred<T>::getPromise() { |
| return celix::Promise<T>{state}; |
| } |
| |
| inline celix::Promise<void> celix::Deferred<void>::getPromise() { |
| return celix::Promise<void>{state}; |
| } |
| |
| template<typename T> |
| template<typename U> |
| celix::Promise<void> celix::Deferred<T>::resolveWith(celix::Promise<U> with) { |
| auto p = celix::impl::SharedPromiseState<void>::create(state->getExecutor(), state->getScheduledExecutor(), state->getPriority()); |
| with.onResolve([s = state, with, p] () mutable { |
| bool resolved; |
| if (with.isSuccessfullyResolved()) { |
| resolved = s->tryResolve(with.moveOrGetValue()); |
| } else { |
| resolved = s->tryFail(with.getFailure()); |
| } |
| if (resolved) { |
| p->tryResolve(); |
| } else { |
| //s was already resolved |
| p->tryFail(std::make_exception_ptr(celix::PromiseIllegalStateException{})); |
| } |
| }); |
| return celix::Promise<void>{p}; |
| } |
| |
| template<typename U> |
| inline celix::Promise<void> celix::Deferred<void>::resolveWith(celix::Promise<U> with) { |
| auto p = celix::impl::SharedPromiseState<void>::create(state->getExecutor(), state->getScheduledExecutor(), state->getPriority()); |
| with.onResolve([s = state, with, p] { |
| bool resolved; |
| if (with.isSuccessfullyResolved()) { |
| with.getValue(); |
| resolved = s->tryResolve(); |
| } else { |
| resolved = s->tryFail(with.getFailure()); |
| } |
| if (resolved) { |
| p->tryResolve(); |
| } else { |
| //s was already resolved |
| p->tryFail(std::make_exception_ptr(celix::PromiseIllegalStateException{})); |
| } |
| }); |
| return celix::Promise<void>{p}; |
| } |
| |
| template<typename T> |
| void celix::Deferred<T>::resolve(T&& value) { |
| state->tryResolve(std::forward<T>(value)); |
| } |
| |
| template<typename T> |
| void celix::Deferred<T>::resolve(const T& value) { |
| state->tryResolve(value); |
| } |
| |
| inline void celix::Deferred<void>::resolve() { |
| state->tryResolve(); |
| } |
| |
| template<typename T> |
| bool celix::Deferred<T>::tryResolve(T&& value) { |
| return state->tryResolve(std::forward<T>(value)); |
| } |
| |
| template<typename T> |
| bool celix::Deferred<T>::tryResolve(const T& value) { |
| return state->tryResolve(value); |
| } |
| |
| inline bool celix::Deferred<void>::tryResolve() { |
| return state->tryResolve(); |
| } |