From 15bdc54befdda5ab7b22ff69025001a05ed3e3ac Mon Sep 17 00:00:00 2001 From: tqcq <99722391+tqcq@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:55:44 +0800 Subject: [PATCH] feat add futures --- .clang-format | 2 +- CMakeLists.txt | 2 + include/sled/futures/future.h | 149 +++++++++++++++ include/sled/futures/promise.h | 49 +++++ include/sled/futures/state.h | 34 ++++ include/sled/futures/try.h | 267 +++++++++++++++++++++++++++ include/sled/synchronization/mutex.h | 11 +- include/sled/system/pid.h | 10 + src/futures/future_test.cc | 10 + src/system/pid.cc | 10 + 10 files changed, 535 insertions(+), 9 deletions(-) create mode 100644 include/sled/futures/future.h create mode 100644 include/sled/futures/promise.h create mode 100644 include/sled/futures/state.h create mode 100644 include/sled/futures/try.h create mode 100644 include/sled/system/pid.h create mode 100644 src/futures/future_test.cc create mode 100644 src/system/pid.cc diff --git a/.clang-format b/.clang-format index a16beb6..56105dc 100644 --- a/.clang-format +++ b/.clang-format @@ -36,7 +36,7 @@ BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeColon ConstructorInitializerAllOnOneLineOrOnePerLine: true BreakInheritanceList: BeforeColon -ColumnLimit: 80 +ColumnLimit: 120 CompactNamespaces: false ContinuationIndentWidth: 4 EmptyLineBeforeAccessModifier: LogicalBlock diff --git a/CMakeLists.txt b/CMakeLists.txt index 170222d..18558ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ target_sources( src/synchronization/sequence_checker_internal.cc src/synchronization/thread_local.cc src/system/location.cc + src/system/pid.cc src/system/thread.cc src/system/thread_pool.cc src/task_queue/pending_task_safety_flag.cc @@ -100,6 +101,7 @@ if(SLED_BUILD_TESTS) FetchContent_MakeAvailable(googletest) add_executable(sled_tests src/filesystem/path_test.cc + src/futures/future_test.cc src/strings/base64_test.cc src/cleanup_test.cc src/status_or_test.cc diff --git a/include/sled/futures/future.h b/include/sled/futures/future.h new file mode 100644 index 0000000..05e6373 --- /dev/null +++ b/include/sled/futures/future.h @@ -0,0 +1,149 @@ +#pragma once +#include +#ifndef SLED_FUTURES_FUTURE_H +#define SLED_FUTURES_FUTURE_H + +#include "sled/futures/state.h" +#include "sled/futures/try.h" +#include "sled/units/time_delta.h" +#include + +namespace sled { +template +class Promise; +template +class Future; + +namespace { +template +struct IsFuture : std::false_type { + typedef T Inner; +}; + +template +struct IsFuture> : std::true_type { + typedef T Inner; +}; + +template +struct InvokeResult { + using Type = decltype(std::declval()(std::declval()...)); +}; + +template +struct CallableWith { + template::Type> + static constexpr bool check(std::nullptr_t) + { + return true; + } + + template + static constexpr bool check(...) + { + return false; + } + + static constexpr bool value = check(check(nullptr)); +}; + +template +struct ArgResult { + using Function = F; + using Result = typename InvokeResult::Type; + + static constexpr bool IsTry() { return is_try; } +}; + +template +struct CallableResult { + typedef typename std::conditional< + CallableWith::value, + ArgResult, + typename std::conditional< + CallableWith::value, + ArgResult, + typename std::conditional::value, + ArgResult, + typename std::conditional &&>::value, + ArgResult &&>, + ArgResult &>>::type>::type>::type>::type + Arg; + typedef IsFuture ReturnsFuture; + typedef Future Return; +}; +}// namespace + +template +class Future { + using TimeoutCallback = std::function; + +public: + Future() = default; + + explicit Future(std::shared_ptr> state) : state_(std::move(state)) {} + + Future(const Future &) = delete; + + Future &operator=(const Future &) = delete; + Future(Future &&) = default; + Future &operator=(Future &&) = default; + + bool Valid() const { return state_ != nullptr; } + + template> + auto Then(F &&func) -> typename R::Return + { + typedef typename R::Arg Arguments; + return ThenImpl(std::forward(func), Arguments()); + } + + // for returns Future + template + typename std::enable_if::type + ThenImpl(F &&func, typename InvokeResult::Type) + { + static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument"); + typedef typename R::Return::Inner ValueType; + Promise promise; + + auto next_future = promise.GetFuture(); + { + auto weak_parent_state = std::weak_ptr>(state_); + next_future.SetOnTimeout([weak_parent_state = weak_parent_state](TimeoutCallback &&cb) { + auto parent_state = weak_parent_state.lock(); + if (!parent_state) { return; } + + // set timeout + { + MutexLock lock(&parent_state->mutex_); + if (parent_state->progress_ != State::kNone) { return; } + parent_state->progress_ = State::kTimeout; + } + }); + + next_future.SetCallback([weak_parent_state = weak_parent_state, + func = std::forward::type>(func), + promise = std::move(promise)](typename TryWrapper::Type &&t) { + auto parent_state = weak_parent_state.lock(); + if (parent_state) { + } else { + } + }); + } + + return std::move(next_future); + } + + Future &Via() { return *this; } + + Future &OnTimeout(sled::TimeDelta duration, TimeoutCallback &&cb) { return *this; } + +private: + void SetCallback(TryWrapper &&t); + void SetOnTimeout(std::function &&func); + + std::shared_ptr> state_; +}; +}// namespace sled +#endif// SLED_FUTURES_FUTURE_H diff --git a/include/sled/futures/promise.h b/include/sled/futures/promise.h new file mode 100644 index 0000000..f0848c3 --- /dev/null +++ b/include/sled/futures/promise.h @@ -0,0 +1,49 @@ +#pragma once +#ifndef SLED_FUTURES_PROMISE_H +#define SLED_FUTURES_PROMISE_H + +#include +#include + +namespace sled { +namespace { +enum class Progress { + kNone, + kTimeout, + kDone, + kRetrieved, +}; + +template +struct State { + Progress progress_ = Progress::kNone; +}; + +}// namespace + +template +class Future; +template +class Try; + +template +class Promise { +public: + static Promise MakeEmpty() noexcept; + Promise(); + ~Promise(); + Promise(Promise const &) = delete; + Promise &operator=(Promise const &) = delete; + Promise(Promise &&other) noexcept; + Promise &operator=(Promise &&other) noexcept; + + Future GetFuture(); + + template + typename std::enable_if::value>::type + SetValue(ValueType &&value) + {} +}; + +}// namespace sled +#endif// SLED_FUTURES_PROMISE_H diff --git a/include/sled/futures/state.h b/include/sled/futures/state.h new file mode 100644 index 0000000..cf01250 --- /dev/null +++ b/include/sled/futures/state.h @@ -0,0 +1,34 @@ +#pragma once +#ifndef SLED_FUTURES_STATE_H +#define SLED_FUTURES_STATE_H +#include "sled/futures/try.h" +#include "sled/synchronization/mutex.h" +#include + +namespace sled { +template +struct State { +public: + enum InnerState { + kNone, + kTimeout, + kDone, + }; + + static_assert(std::is_same::value || std::is_copy_constructible() || std::is_move_constructible(), + "Must be copyable or movable or void"); + + State() {} + + sled::Mutex &GetMutex() { return mutex_; } + + using ValueType = typename TryWrapper::Type; + sled::Mutex mutex_; + ValueType value_ GUARDED_BY(mutex_); + InnerState progress_ GUARDED_BY(mutex_) = kNone; + std::function)> on_timeout_; + std::function on_then_; +}; + +}// namespace sled +#endif// SLED_FUTURES_STATE_H diff --git a/include/sled/futures/try.h b/include/sled/futures/try.h new file mode 100644 index 0000000..d0b9ee3 --- /dev/null +++ b/include/sled/futures/try.h @@ -0,0 +1,267 @@ +#pragma once +#ifndef SLED_FUTURES_TRY_H +#define SLED_FUTURES_TRY_H +#include +#include +#include + +namespace sled { + +template +class Try { + enum class State { + kNone, + kException, + kValue, + }; + +public: + inline Try() = default; + + Try(const T &t) : state_(State::kValue), value_(t) {} + + Try(T &&t) : state_(State::kValue), value_(std::move(t)) {} + + explicit Try(std::exception_ptr e) : state_(State::kException), exception_(std::move(e)) {} + + Try(Try &&other_try) noexcept : state_(other_try.state_) + { + if (state_ == State::Value) { + new (&value_) T(std::move(other_try.value_)); + } else { + new (&exception_) std::exception_ptr(std::move(other_try.exception_)); + } + } + + Try(const Try &other_try) : state_(other_try.state_) + { + if (state_ == State::kValue) { + new (&value_) T(other_try.value_); + } else { + new (&exception_) std::exception_ptr(other_try.exception_); + } + } + + Try &operator=(Try &&other_try) noexcept + { + if (this == &other_try) { return *this; } + this->~Try(); + state_ = other_try.state_; + if (state_ == State::kValue) { + new (&value_) T(std::move(other_try.value_)); + } else { + new (&exception_) std::exception_ptr(std::move(other_try.exception_)); + } + return *this; + } + + Try &operator=(const Try &other_try) + { + if (this == &other_try) { return *this; } + this->~Try(); + state_ = other_try.state_; + if (state_ == State::kValue) { + new (&value_) T(other_try.value_); + } else { + new (&exception_) std::exception_ptr(other_try.exception_); + } + return *this; + } + + ~Try() + { + if (state_ == State::kValue) { + value_.~T(); + } else if (state_ == State::kException) { + exception_.~exception_ptr(); + } + } + + bool HasValue() const noexcept { return state_ == State::kValue; } + + bool HasException() const noexcept { return state_ == State::kException; } + + const T &Value() const & + { + AssertHasValue(); + return value_; + } + + T &Value() & + { + AssertHasValue(); + return value_; + } + + T &&Value() && + { + AssertHasValue(); + return std::move(value_); + } + + const std::exception_ptr &Exception() const & + { + AssertHasException(); + return exception_; + } + + std::exception_ptr &Exception() & + { + AssertHasException(); + return exception_; + } + + std::exception_ptr &&Exception() && + { + AssertHasException(); + return std::move(exception_); + } + + const T &operator*() const & { return Value(); } + + T &operator*() & { return Value(); } + + T &&operator*() && { return Value(); } + + explicit operator bool() const { return HasValue(); } + +private: + void AssertHasValue() const + { + if (state_ == State::kNone) { + throw std::runtime_error("Try is empty"); + } else if (state_ == State::kException) { + std::rethrow_exception(exception_); + } + } + + void AssertHasException() const + { + if (!HasException()) { throw std::runtime_error("Not exceptioin"); } + } + + State state_ = State::kNone; + + union { + T value_; + std::exception_ptr exception_; + }; +}; + +template<> +class Try { + enum class State { kValue, kException }; + +public: + Try() : state_(State::kValue) {} + + Try(Try &&other_try) : state_(other_try.state_) + { + if (state_ == State::kException) { new (&exception_) std::exception_ptr(std::move(other_try.exception_)); } + } + + Try(const Try &other_try) : state_(other_try.state_) + { + if (state_ == State::kException) { new (&exception_) std::exception_ptr(std::move(other_try.exception_)); } + } + + explicit Try(std::exception_ptr e) : exception_(std::move(e)) {} + + ~Try() + { + if (state_ == State::kException) { exception_.~exception_ptr(); } + } + + Try &operator=(Try &&other_try) + { + if (this == &other_try) { return *this; } + this->~Try(); + state_ = other_try.state_; + if (state_ == State::kException) { new (&exception_) std::exception_ptr(std::move(other_try.exception_)); } + return *this; + } + + Try &operator=(const Try &other_try) + { + if (this == &other_try) { return *this; } + this->~Try(); + state_ = other_try.state_; + if (state_ == State::kException) { new (&exception_) std::exception_ptr(std::move(other_try.exception_)); } + return *this; + } + + bool HasValue() const noexcept { return state_ == State::kValue; } + + bool HasException() const noexcept { return state_ == State::kException; } + + const std::exception_ptr &Exception() const & + { + AssertHasException(); + return exception_; + } + + std::exception_ptr &Exception() & + { + AssertHasException(); + return exception_; + } + + std::exception_ptr &&Exception() && + { + AssertHasException(); + return std::move(exception_); + } + + explicit operator bool() const { return HasValue(); } + +private: + void AssertHasException() const + { + if (!HasException()) { throw std::runtime_error("Not exception"); } + } + + void AssertHasValue() const + { + if (!HasValue()) { throw std::runtime_error("Try is empty"); } + } + + State state_; + std::exception_ptr exception_; +}; + +template +struct TryWrapper { + using Type = Try; +}; + +template +struct TryWrapper> { + using Type = Try; +}; + +template::value> +typename std::enable_if::value, typename TryWrapper::Type>::type +WrapWithTry(F &&f, Args &&...args) +{ + try { + return typename TryWrapper::Type(std::forward(f)(std::forward(args)...)); + } catch (...) { + return typename TryWrapper::Type(std::current_exception()); + } +} + +template::value> +typename std::enable_if::value, typename TryWrapper::Type>::type +WrapWithTry(F &&f, Args &&...args) +{ + try { + + std::forward(f)(std::forward(args)...); + return Try(); + } catch (...) { + return Try(std::current_exception()); + } +} + +}// namespace sled +#endif// SLED_FUTURES_TRY_H diff --git a/include/sled/synchronization/mutex.h b/include/sled/synchronization/mutex.h index dc2cccf..208960a 100644 --- a/include/sled/synchronization/mutex.h +++ b/include/sled/synchronization/mutex.h @@ -28,8 +28,7 @@ struct HasLockAndUnlock { template static char Test(...); - static constexpr bool value = - std::is_same(0)), int>::value; + static constexpr bool value = std::is_same(0)), int>::value; }; }// namespace internal @@ -72,9 +71,7 @@ private: std::recursive_mutex impl_; }; -template::value, - TLock>::type * = nullptr> +template::value, TLock>::type * = nullptr> class LockGuard final { public: LockGuard(const LockGuard &) = delete; @@ -159,9 +156,7 @@ public: cv_.wait(lock.lock_, std::forward(pred)); return true; } else { - return cv_.wait_for(lock.lock_, - std::chrono::milliseconds(timeout.ms()), - std::forward(pred)); + return cv_.wait_for(lock.lock_, std::chrono::milliseconds(timeout.ms()), std::forward(pred)); } } diff --git a/include/sled/system/pid.h b/include/sled/system/pid.h new file mode 100644 index 0000000..07d6d61 --- /dev/null +++ b/include/sled/system/pid.h @@ -0,0 +1,10 @@ +#pragma once +#ifndef SLED_SYSTEM_PID_H +#define SLED_SYSTEM_PID_H + +#include + +namespace sled { +pid_t GetCachedPID(); +}// namespace sled +#endif// SLED_SYSTEM_PID_H diff --git a/src/futures/future_test.cc b/src/futures/future_test.cc new file mode 100644 index 0000000..b9e20df --- /dev/null +++ b/src/futures/future_test.cc @@ -0,0 +1,10 @@ +#include +#include +#include + +TEST(Future, Test1) +{ + sled::Promise promise; + sled::Future future = promise.GetFuture(); + promise.SetValue(42); +} diff --git a/src/system/pid.cc b/src/system/pid.cc new file mode 100644 index 0000000..a2ea5e8 --- /dev/null +++ b/src/system/pid.cc @@ -0,0 +1,10 @@ +#include "sled/system/pid.h" + +namespace sled { +pid_t +GetCachedPID() +{ + static pid_t cached_pid = getpid(); + return cached_pid; +} +}// namespace sled