/* * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed 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. */ #ifndef FFRT_API_CPP_FUTURE_H #define FFRT_API_CPP_FUTURE_H #include #include #include #include "cpp/condition_variable.h" #include "thread.h" namespace ffrt { struct non_copyable { protected: non_copyable() = default; ~non_copyable() = default; non_copyable(const non_copyable&) = delete; non_copyable& operator=(const non_copyable&) = delete; }; enum class future_status { ready, timeout, deferred }; namespace detail { template struct shared_state_base : private non_copyable { void wait() const noexcept { std::unique_lock lk(this->m_mtx); wait_(lk); } template future_status wait_for(const std::chrono::duration& waitTime) const noexcept { std::unique_lock lk(m_mtx); return m_cv.wait_for(lk, waitTime, [this] { return get_derived().has_value(); }) ? future_status::ready : future_status::timeout; } template future_status wait_until(const std::chrono::time_point& tp) const noexcept { std::unique_lock lk(m_mtx); return m_cv.wait_until(lk, tp, [this] { return get_derived().has_value(); }) ? future_status::ready : future_status::timeout; } protected: void wait_(std::unique_lock& lk) const noexcept { m_cv.wait(lk, [this] { return get_derived().has_value(); }); } mutable mutex m_mtx; mutable condition_variable m_cv; private: const Derived& get_derived() const { return *static_cast(this); } }; template struct shared_state : public shared_state_base> { void set_value(const R& value) noexcept { { std::unique_lock lk(this->m_mtx); m_res.emplace(value); } this->m_cv.notify_all(); } void set_value(R&& value) noexcept { { std::unique_lock lk(this->m_mtx); m_res.emplace(std::move(value)); } this->m_cv.notify_all(); } R& get() noexcept { std::unique_lock lk(this->m_mtx); this->wait_(lk); return m_res.value(); } bool has_value() const noexcept { return m_res.has_value(); } private: std::optional m_res; }; template <> struct shared_state : public shared_state_base> { void set_value() noexcept { { std::unique_lock lk(this->m_mtx); m_hasValue = true; } this->m_cv.notify_all(); } void get() noexcept { std::unique_lock lk(this->m_mtx); this->wait_(lk); } bool has_value() const noexcept { return m_hasValue; } private: bool m_hasValue {false}; }; }; // namespace detail template class future : private non_copyable { template friend struct promise; template friend struct packaged_task; public: explicit future(const std::shared_ptr>& state) noexcept : m_state(state) { } future() noexcept = default; future(future&& fut) noexcept { swap(fut); } future& operator=(future&& fut) noexcept { if (this != &fut) { future tmp(std::move(fut)); swap(tmp); } return *this; } bool valid() const noexcept { return m_state != nullptr; } R get() noexcept { auto tmp = std::move(m_state); if constexpr(!std::is_void_v) { return std::move(tmp->get()); } else { return tmp->get(); } } template future_status wait_for(const std::chrono::duration& waitTime) const noexcept { return m_state->wait_for(waitTime); } template future_status wait_until(const std::chrono::time_point& tp) const noexcept { return m_state->wait_until(tp); } void wait() const noexcept { m_state->wait(); } void swap(future& rhs) noexcept { std::swap(m_state, rhs.m_state); } private: std::shared_ptr> m_state; }; template struct promise : private non_copyable { promise() noexcept : m_state {std::make_shared>()} { } promise(promise&& p) noexcept { swap(p); } promise& operator=(promise&& p) noexcept { if (this != &p) { promise tmp(std::move(p)); swap(tmp); } return *this; } void set_value(const R& value) noexcept { m_state->set_value(value); } void set_value(R&& value) noexcept { m_state->set_value(std::move(value)); } future get_future() noexcept { return future {m_state}; } void swap(promise& rhs) noexcept { std::swap(m_state, rhs.m_state); } private: std::shared_ptr> m_state; }; template <> struct promise : private non_copyable { promise() noexcept : m_state {std::make_shared>()} { } promise(promise&& p) noexcept { swap(p); } promise& operator=(promise&& p) noexcept { if (this != &p) { promise tmp(std::move(p)); swap(tmp); } return *this; } void set_value() noexcept { m_state->set_value(); } future get_future() noexcept { return future {m_state}; } void swap(promise& rhs) noexcept { std::swap(m_state, rhs.m_state); } private: std::shared_ptr> m_state; }; template struct packaged_task; template struct packaged_task { packaged_task() noexcept = default; packaged_task(const packaged_task& pt) noexcept { m_fn = pt.m_fn; m_state = pt.m_state; } packaged_task(packaged_task&& pt) noexcept { swap(pt); } packaged_task& operator=(packaged_task&& pt) noexcept { if (this != &pt) { packaged_task tmp(std::move(pt)); swap(tmp); } return *this; } template explicit packaged_task(F&& f) noexcept : m_fn {std::forward(f)}, m_state {std::make_shared>()} { } bool valid() const noexcept { return bool(m_fn) && m_state != nullptr; } future get_future() noexcept { return future {m_state}; } void operator()(Args... args) { if constexpr(!std::is_void_v) { m_state->set_value(m_fn(std::forward(args)...)); } else { m_fn(std::forward(args)...); m_state->set_value(); } } void swap(packaged_task& pt) noexcept { std::swap(m_fn, pt.m_fn); std::swap(m_state, pt.m_state); } private: std::function m_fn; std::shared_ptr> m_state; }; template future, std::decay_t...>> async(F&& f, Args&& ... args) { using R = std::invoke_result_t, std::decay_t...>; packaged_task...)> pt {std::forward(f)}; auto fut {pt.get_future()}; auto th = ffrt::thread(std::move(pt), std::forward(args)...); th.detach(); return fut; } } // namespace ffrt #endif