From ed1a995bffb9dff8d6487c5fedc8e939f8648fd6 Mon Sep 17 00:00:00 2001 From: Shen-Ta Hsieh Date: Sat, 27 Feb 2021 18:35:56 +0800 Subject: [PATCH] thread: Add `Future` implement for mingw32 without pthread --- src/thread/Future.hxx | 43 ++++ src/thread/WindowsFuture.hxx | 394 +++++++++++++++++++++++++++++++++++ 2 files changed, 437 insertions(+) create mode 100644 src/thread/Future.hxx create mode 100644 src/thread/WindowsFuture.hxx diff --git a/src/thread/Future.hxx b/src/thread/Future.hxx new file mode 100644 index 000000000..a277066f9 --- /dev/null +++ b/src/thread/Future.hxx @@ -0,0 +1,43 @@ +/* + * Copyright 2020-2021 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef THREAD_FUTURE_HXX +#define THREAD_FUTURE_HXX + +#ifdef _WIN32 + +#include "WindowsFuture.hxx" + +template +using Future = WinFuture; +template +using Promise = WinPromise; + +#else + +#include + +template +using Future = std::future; +template +using Promise = std::promise; + +#endif + +#endif diff --git a/src/thread/WindowsFuture.hxx b/src/thread/WindowsFuture.hxx new file mode 100644 index 000000000..f5d304a1e --- /dev/null +++ b/src/thread/WindowsFuture.hxx @@ -0,0 +1,394 @@ +/* + * Copyright 2020-2021 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef THREAD_WINDOWS_FUTURE_HXX +#define THREAD_WINDOWS_FUTURE_HXX + +#include "CriticalSection.hxx" +#include "WindowsCond.hxx" +#include +#include +#include + +enum class WinFutureErrc : int { + future_already_retrieved = 1, + promise_already_satisfied, + no_state, + broken_promise, +}; + +enum class WinFutureStatus { ready, timeout, deferred }; + +static inline const std::error_category &win_future_category() noexcept; +class WinFutureCategory : public std::error_category { +public: + const char *name() const noexcept override { return "win_future"; } + std::string message(int Errcode) const override { + using namespace std::literals; + switch (static_cast(Errcode)) { + case WinFutureErrc::broken_promise: + return "Broken promise"s; + case WinFutureErrc::future_already_retrieved: + return "Future already retrieved"s; + case WinFutureErrc::promise_already_satisfied: + return "Promise already satisfied"s; + case WinFutureErrc::no_state: + return "No associated state"s; + default: + return "Unknown error"s; + } + } + std::error_condition default_error_condition(int code) const noexcept override { + return std::error_condition(code, win_future_category()); + } +}; +static inline const std::error_category &win_future_category() noexcept { + static const WinFutureCategory win_future_category_instance{}; + return win_future_category_instance; +} + +class WinFutureError : public std::logic_error { +public: + WinFutureError(WinFutureErrc errcode) + : WinFutureError( + std::error_code(static_cast(errcode), win_future_category())) {} + +private: + explicit WinFutureError(std::error_code errcode) + : std::logic_error("WinFutureError: " + errcode.message()), code(errcode) {} + std::error_code code; +}; + +template +class WinFutureState { +private: + mutable CriticalSection mutex; + WindowsCond condition; + std::variant result; + bool retrieved = false; + bool ready = false; + +public: + bool is_ready() const noexcept { + std::unique_lock lock(mutex); + return ready; + } + + bool already_retrieved() const noexcept { + std::unique_lock lock(mutex); + return retrieved; + } + + void wait() { + std::unique_lock lock(mutex); + condition.wait(lock, [this]() { return ready; }); + } + + template + WinFutureStatus + wait_for(const std::chrono::duration &timeout_duration) const { + std::unique_lock lock(mutex); + // deferred function not support yet + if (condition.wait_for(lock, timeout_duration, + [this]() { return ready; })) { + return WinFutureStatus::ready; + } + return WinFutureStatus::timeout; + } + + virtual T &get_value() { + std::unique_lock lock(mutex); + if (retrieved) { + throw WinFutureError(WinFutureErrc::future_already_retrieved); + } + if (auto eptr = std::get_if(&result)) { + std::rethrow_exception(*eptr); + } + retrieved = true; + condition.wait(lock, [this]() { return ready; }); + if (auto eptr = std::get_if(&result)) { + std::rethrow_exception(*eptr); + } + return *std::get_if(&result); + } + + void set_value(const T &value) { + std::unique_lock lock(mutex); + if (!std::holds_alternative(&result)) { + throw WinFutureError(WinFutureErrc::promise_already_satisfied); + } + result.template emplace(value); + ready = true; + condition.notify_all(); + } + + void set_value(T &&value) { + std::unique_lock lock(mutex); + if (!std::holds_alternative(result)) { + throw WinFutureError(WinFutureErrc::promise_already_satisfied); + } + result.template emplace(std::move(value)); + ready = true; + condition.notify_all(); + } + + void set_exception(std::exception_ptr eptr) { + std::unique_lock lock(mutex); + if (!std::holds_alternative(result)) { + throw WinFutureError(WinFutureErrc::promise_already_satisfied); + } + result.template emplace(eptr); + ready = true; + condition.notify_all(); + } +}; + +template +class WinFutureStateManager { +public: + WinFutureStateManager() = default; + WinFutureStateManager(std::shared_ptr> new_state) + : state(std::move(new_state)) {} + WinFutureStateManager(const WinFutureStateManager &) = default; + WinFutureStateManager &operator=(const WinFutureStateManager &) = default; + WinFutureStateManager(WinFutureStateManager &&) = default; + WinFutureStateManager &operator=(WinFutureStateManager &&) = default; + + [[nodiscard]] bool valid() const noexcept { return static_cast(state); } + + void wait() const { + if (!valid()) { + throw WinFutureError(WinFutureErrc::no_state); + } + state->wait(); + } + + template + WinFutureStatus + wait_for(const std::chrono::duration &timeout_duration) const { + if (!valid()) { + throw WinFutureError(WinFutureErrc::no_state); + } + return state->wait_for(timeout_duration); + } + + T &get_value() const { + if (!valid()) { + throw WinFutureError(WinFutureErrc::no_state); + } + return state->get_value(); + } + + void set_value(const T &value) { + if (!valid()) { + throw WinFutureError(WinFutureErrc::no_state); + } + state->set_value(value); + } + + void set_value(T &&value) { + if (!valid()) { + throw WinFutureError(WinFutureErrc::no_state); + } + state->set_value(std::move(value)); + } + + void set_exception(std::exception_ptr eptr) { + if (!valid()) { + throw WinFutureError(WinFutureErrc::no_state); + } + state->set_exception(eptr); + } + +private: + std::shared_ptr> state; +}; + +template +class WinFuture : public WinFutureStateManager { + using Base = WinFutureStateManager; + static_assert(!std::is_array_v && std::is_object_v && + std::is_destructible_v, + "T in future must meet the Cpp17Destructible requirements " + "(N4878 [futures.unique.future]/4)."); + +public: + WinFuture() noexcept = default; + WinFuture(WinFuture &&) noexcept = default; + WinFuture &operator=(WinFuture &&) noexcept = default; + WinFuture(const WinFuture &) noexcept = delete; + WinFuture &operator=(const WinFuture &) noexcept = delete; + + WinFuture(const Base &base, std::monostate) : Base(base) {} + ~WinFuture() noexcept = default; + T get() { + WinFuture local(std::move(*this)); + return std::move(local.get_value()); + } + +private: + using Base::get_value; + using Base::set_exception; + using Base::set_value; +}; + +template +class WinFuture : public WinFutureStateManager { + using Base = WinFutureStateManager; + +public: + WinFuture() noexcept = default; + WinFuture(WinFuture &&) noexcept = default; + WinFuture &operator=(WinFuture &&) noexcept = default; + WinFuture(const WinFuture &) noexcept = delete; + WinFuture &operator=(const WinFuture &) noexcept = delete; + + WinFuture(const Base &base, std::monostate) : Base(base) {} + ~WinFuture() noexcept = default; + T &get() { + WinFuture local(std::move(*this)); + return *local.get_value(); + } + +private: + using Base::get_value; + using Base::set_exception; + using Base::set_value; +}; + +template <> +class WinFuture : public WinFutureStateManager { + using Base = WinFutureStateManager; + +public: + WinFuture() noexcept = default; + WinFuture(WinFuture &&) noexcept = default; + WinFuture &operator=(WinFuture &&) noexcept = default; + WinFuture(const WinFuture &) noexcept = delete; + WinFuture &operator=(const WinFuture &) noexcept = delete; + + WinFuture(const Base &base, std::monostate) : Base(base) {} + ~WinFuture() noexcept = default; + void get() { + WinFuture local(std::move(*this)); + local.get_value(); + } + +private: + using Base::get_value; + using Base::set_exception; + using Base::set_value; +}; + +template +class WinPromiseBase { +public: + WinPromiseBase(std::shared_ptr> new_state) + : state(std::move(new_state)) {} + WinPromiseBase(WinPromiseBase &&) = default; + WinPromiseBase &operator=(WinPromiseBase &&) = default; + WinPromiseBase(const WinPromiseBase &) = delete; + WinPromiseBase &operator=(const WinPromiseBase &) = delete; + + WinFutureStateManager &get_state_for_set() { + if (!state.valid()) { + throw WinFutureError(WinFutureErrc::no_state); + } + return state; + } + + WinFutureStateManager &get_state_for_future() { + if (!state.valid()) { + throw WinFutureError(WinFutureErrc::no_state); + } + if (future_retrieved) { + throw WinFutureError(WinFutureErrc::future_already_retrieved); + } + future_retrieved = true; + return state; + } + +private: + WinFutureStateManager state; + bool future_retrieved = false; +}; + +template +class WinPromise { +public: + WinPromise() : base(std::make_shared>()) {} + WinPromise(WinPromise &&) = default; + WinPromise(const WinPromise &) = delete; + ~WinPromise() noexcept {} + [[nodiscard]] WinFuture get_future() { + return WinFuture(base.get_state_for_future(), std::monostate()); + } + void set_value(const T &value) { base.get_state_for_set().set_value(value); } + void set_value(T &&value) { + base.get_state_for_set().set_value(std::forward(value)); + } + void set_exception(std::exception_ptr eptr) { + base.get_state_for_set().set_exception(eptr); + } + +private: + WinPromiseBase base; +}; + +template +class WinPromise { +public: + WinPromise() : base(std::make_shared>()) {} + WinPromise(WinPromise &&) = default; + WinPromise(const WinPromise &) = delete; + ~WinPromise() noexcept {} + [[nodiscard]] WinFuture get_future() { + return WinFuture(base.get_state_for_future(), std::monostate()); + } + void set_value(T &value) { + base.get_state_for_set().set_value(std::addressof(value)); + } + void set_exception(std::exception_ptr eptr) { + base.get_state_for_set().set_exception(eptr); + } + +private: + WinPromiseBase base; +}; + +template <> +class WinPromise { +public: + WinPromise() : base(std::make_shared>()) {} + WinPromise(WinPromise &&) = default; + WinPromise(const WinPromise &) = delete; + ~WinPromise() noexcept {} + [[nodiscard]] WinFuture get_future() { + return WinFuture(base.get_state_for_future(), std::monostate()); + } + void set_value() { base.get_state_for_set().set_value(0); } + void set_exception(std::exception_ptr eptr) { + base.get_state_for_set().set_exception(eptr); + } + +private: + WinPromiseBase base; +}; + +#endif