From ed1a995bffb9dff8d6487c5fedc8e939f8648fd6 Mon Sep 17 00:00:00 2001 From: Shen-Ta Hsieh <ibmibmibm.tw@gmail.com> 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 <typename R> +using Future = WinFuture<R>; +template <typename R> +using Promise = WinPromise<R>; + +#else + +#include <future> + +template <typename R> +using Future = std::future<R>; +template <typename R> +using Promise = std::promise<R>; + +#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 <atomic> +#include <memory> +#include <variant> + +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<WinFutureErrc>(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<int>(errcode), win_future_category())) {} + +private: + explicit WinFutureError(std::error_code errcode) + : std::logic_error("WinFutureError: " + errcode.message()), code(errcode) {} + std::error_code code; +}; + +template <typename T> +class WinFutureState { +private: + mutable CriticalSection mutex; + WindowsCond condition; + std::variant<std::monostate, T, std::exception_ptr> result; + bool retrieved = false; + bool ready = false; + +public: + bool is_ready() const noexcept { + std::unique_lock<CriticalSection> lock(mutex); + return ready; + } + + bool already_retrieved() const noexcept { + std::unique_lock<CriticalSection> lock(mutex); + return retrieved; + } + + void wait() { + std::unique_lock<CriticalSection> lock(mutex); + condition.wait(lock, [this]() { return ready; }); + } + + template <class Rep, class Period> + WinFutureStatus + wait_for(const std::chrono::duration<Rep, Period> &timeout_duration) const { + std::unique_lock<CriticalSection> 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<CriticalSection> lock(mutex); + if (retrieved) { + throw WinFutureError(WinFutureErrc::future_already_retrieved); + } + if (auto eptr = std::get_if<std::exception_ptr>(&result)) { + std::rethrow_exception(*eptr); + } + retrieved = true; + condition.wait(lock, [this]() { return ready; }); + if (auto eptr = std::get_if<std::exception_ptr>(&result)) { + std::rethrow_exception(*eptr); + } + return *std::get_if<T>(&result); + } + + void set_value(const T &value) { + std::unique_lock<CriticalSection> lock(mutex); + if (!std::holds_alternative<std::monostate>(&result)) { + throw WinFutureError(WinFutureErrc::promise_already_satisfied); + } + result.template emplace<T>(value); + ready = true; + condition.notify_all(); + } + + void set_value(T &&value) { + std::unique_lock<CriticalSection> lock(mutex); + if (!std::holds_alternative<std::monostate>(result)) { + throw WinFutureError(WinFutureErrc::promise_already_satisfied); + } + result.template emplace<T>(std::move(value)); + ready = true; + condition.notify_all(); + } + + void set_exception(std::exception_ptr eptr) { + std::unique_lock<CriticalSection> lock(mutex); + if (!std::holds_alternative<std::monostate>(result)) { + throw WinFutureError(WinFutureErrc::promise_already_satisfied); + } + result.template emplace<std::exception_ptr>(eptr); + ready = true; + condition.notify_all(); + } +}; + +template <typename T> +class WinFutureStateManager { +public: + WinFutureStateManager() = default; + WinFutureStateManager(std::shared_ptr<WinFutureState<T>> 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<bool>(state); } + + void wait() const { + if (!valid()) { + throw WinFutureError(WinFutureErrc::no_state); + } + state->wait(); + } + + template <class Rep, class Period> + WinFutureStatus + wait_for(const std::chrono::duration<Rep, Period> &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<WinFutureState<T>> state; +}; + +template <typename T> +class WinFuture : public WinFutureStateManager<T> { + using Base = WinFutureStateManager<T>; + static_assert(!std::is_array_v<T> && std::is_object_v<T> && + std::is_destructible_v<T>, + "T in future<T> 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 <typename T> +class WinFuture<T &> : public WinFutureStateManager<T *> { + using Base = WinFutureStateManager<T *>; + +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<void> : public WinFutureStateManager<int> { + using Base = WinFutureStateManager<int>; + +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 <typename T> +class WinPromiseBase { +public: + WinPromiseBase(std::shared_ptr<WinFutureState<T>> new_state) + : state(std::move(new_state)) {} + WinPromiseBase(WinPromiseBase &&) = default; + WinPromiseBase &operator=(WinPromiseBase &&) = default; + WinPromiseBase(const WinPromiseBase &) = delete; + WinPromiseBase &operator=(const WinPromiseBase &) = delete; + + WinFutureStateManager<T> &get_state_for_set() { + if (!state.valid()) { + throw WinFutureError(WinFutureErrc::no_state); + } + return state; + } + + WinFutureStateManager<T> &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<T> state; + bool future_retrieved = false; +}; + +template <typename T> +class WinPromise { +public: + WinPromise() : base(std::make_shared<WinFutureState<T>>()) {} + WinPromise(WinPromise &&) = default; + WinPromise(const WinPromise &) = delete; + ~WinPromise() noexcept {} + [[nodiscard]] WinFuture<T> get_future() { + return WinFuture<T>(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<T>(value)); + } + void set_exception(std::exception_ptr eptr) { + base.get_state_for_set().set_exception(eptr); + } + +private: + WinPromiseBase<T> base; +}; + +template <typename T> +class WinPromise<T &> { +public: + WinPromise() : base(std::make_shared<WinFutureState<T *>>()) {} + WinPromise(WinPromise &&) = default; + WinPromise(const WinPromise &) = delete; + ~WinPromise() noexcept {} + [[nodiscard]] WinFuture<T &> get_future() { + return WinFuture<T>(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<T *> base; +}; + +template <> +class WinPromise<void> { +public: + WinPromise() : base(std::make_shared<WinFutureState<int>>()) {} + WinPromise(WinPromise &&) = default; + WinPromise(const WinPromise &) = delete; + ~WinPromise() noexcept {} + [[nodiscard]] WinFuture<void> get_future() { + return WinFuture<void>(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<int> base; +}; + +#endif