feat add futures

This commit is contained in:
tqcq 2024-03-14 17:55:44 +08:00
parent f8d2b051ee
commit 15bdc54bef
10 changed files with 535 additions and 9 deletions

View File

@ -36,7 +36,7 @@ BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
ConstructorInitializerAllOnOneLineOrOnePerLine: true
BreakInheritanceList: BeforeColon
ColumnLimit: 80
ColumnLimit: 120
CompactNamespaces: false
ContinuationIndentWidth: 4
EmptyLineBeforeAccessModifier: LogicalBlock

View File

@ -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

View File

@ -0,0 +1,149 @@
#pragma once
#include <exception>
#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 <type_traits>
namespace sled {
template<typename T>
class Promise;
template<typename T>
class Future;
namespace {
template<typename T>
struct IsFuture : std::false_type {
typedef T Inner;
};
template<typename T>
struct IsFuture<Future<T>> : std::true_type {
typedef T Inner;
};
template<typename F, typename... Args>
struct InvokeResult {
using Type = decltype(std::declval<F>()(std::declval<Args>()...));
};
template<typename F, typename... Args>
struct CallableWith {
template<typename T, typename = typename InvokeResult<F, Args...>::Type>
static constexpr bool check(std::nullptr_t)
{
return true;
}
template<typename>
static constexpr bool check(...)
{
return false;
}
static constexpr bool value = check(check<F>(nullptr));
};
template<bool is_try, typename F, typename... Args>
struct ArgResult {
using Function = F;
using Result = typename InvokeResult<F, Args...>::Type;
static constexpr bool IsTry() { return is_try; }
};
template<typename T, typename F>
struct CallableResult {
typedef typename std::conditional<
CallableWith<F>::value,
ArgResult<false, F>,
typename std::conditional<
CallableWith<F, T &&>::value,
ArgResult<false, F, T &&>,
typename std::conditional<CallableWith<F, T &>::value,
ArgResult<false, F, T &>,
typename std::conditional<CallableWith<F, Try<T> &&>::value,
ArgResult<true, F, Try<T> &&>,
ArgResult<true, F, Try<T> &>>::type>::type>::type>::type
Arg;
typedef IsFuture<typename Arg::Result> ReturnsFuture;
typedef Future<typename ReturnsFuture::Inner> Return;
};
}// namespace
template<typename T>
class Future {
using TimeoutCallback = std::function<void()>;
public:
Future() = default;
explicit Future(std::shared_ptr<State<T>> 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<typename F, typename R = CallableResult<F, T>>
auto Then(F &&func) -> typename R::Return
{
typedef typename R::Arg Arguments;
return ThenImpl<F, R>(std::forward<F>(func), Arguments());
}
// for returns Future<T>
template<typename F, typename R, typename... Args>
typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
ThenImpl(F &&func, typename InvokeResult<F, Args...>::Type)
{
static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
typedef typename R::Return::Inner ValueType;
Promise<ValueType> promise;
auto next_future = promise.GetFuture();
{
auto weak_parent_state = std::weak_ptr<State<T>>(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<T>::kNone) { return; }
parent_state->progress_ = State<T>::kTimeout;
}
});
next_future.SetCallback([weak_parent_state = weak_parent_state,
func = std::forward<std::decay<F>::type>(func),
promise = std::move(promise)](typename TryWrapper<T>::Type &&t) {
auto parent_state = weak_parent_state.lock();
if (parent_state) {
} else {
}
});
}
return std::move(next_future);
}
Future<T> &Via() { return *this; }
Future<T> &OnTimeout(sled::TimeDelta duration, TimeoutCallback &&cb) { return *this; }
private:
void SetCallback(TryWrapper<T> &&t);
void SetOnTimeout(std::function<void(TimeoutCallback &&)> &&func);
std::shared_ptr<State<T>> state_;
};
}// namespace sled
#endif// SLED_FUTURES_FUTURE_H

View File

@ -0,0 +1,49 @@
#pragma once
#ifndef SLED_FUTURES_PROMISE_H
#define SLED_FUTURES_PROMISE_H
#include <exception>
#include <type_traits>
namespace sled {
namespace {
enum class Progress {
kNone,
kTimeout,
kDone,
kRetrieved,
};
template<typename T>
struct State {
Progress progress_ = Progress::kNone;
};
}// namespace
template<typename T>
class Future;
template<typename T>
class Try;
template<typename T>
class Promise {
public:
static Promise<T> MakeEmpty() noexcept;
Promise();
~Promise();
Promise(Promise const &) = delete;
Promise &operator=(Promise const &) = delete;
Promise(Promise<T> &&other) noexcept;
Promise &operator=(Promise<T> &&other) noexcept;
Future<T> GetFuture();
template<typename ValueType = T>
typename std::enable_if<!std::is_void<ValueType>::value>::type
SetValue(ValueType &&value)
{}
};
}// namespace sled
#endif// SLED_FUTURES_PROMISE_H

View File

@ -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 <type_traits>
namespace sled {
template<typename T>
struct State {
public:
enum InnerState {
kNone,
kTimeout,
kDone,
};
static_assert(std::is_same<T, void>::value || std::is_copy_constructible<T>() || std::is_move_constructible<T>(),
"Must be copyable or movable or void");
State() {}
sled::Mutex &GetMutex() { return mutex_; }
using ValueType = typename TryWrapper<T>::Type;
sled::Mutex mutex_;
ValueType value_ GUARDED_BY(mutex_);
InnerState progress_ GUARDED_BY(mutex_) = kNone;
std::function<void(std::function<void()>)> on_timeout_;
std::function<void(ValueType &&)> on_then_;
};
}// namespace sled
#endif// SLED_FUTURES_STATE_H

267
include/sled/futures/try.h Normal file
View File

@ -0,0 +1,267 @@
#pragma once
#ifndef SLED_FUTURES_TRY_H
#define SLED_FUTURES_TRY_H
#include <exception>
#include <memory>
#include <stdexcept>
namespace sled {
template<typename T>
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<T> &&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<T> &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<T> &operator=(Try<T> &&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<T> &operator=(const Try<T> &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<void> {
enum class State { kValue, kException };
public:
Try() : state_(State::kValue) {}
Try(Try<void> &&other_try) : state_(other_try.state_)
{
if (state_ == State::kException) { new (&exception_) std::exception_ptr(std::move(other_try.exception_)); }
}
Try(const Try<void> &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<void> &operator=(Try<void> &&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<void> &operator=(const Try<void> &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<typename T>
struct TryWrapper {
using Type = Try<T>;
};
template<typename T>
struct TryWrapper<Try<T>> {
using Type = Try<T>;
};
template<typename F, typename... Args, typename ResultT = typename std::result_of<F(Args...)>::value>
typename std::enable_if<!std::is_same<void, ResultT>::value, typename TryWrapper<ResultT>::Type>::type
WrapWithTry(F &&f, Args &&...args)
{
try {
return typename TryWrapper<ResultT>::Type(std::forward<F>(f)(std::forward<Args>(args)...));
} catch (...) {
return typename TryWrapper<ResultT>::Type(std::current_exception());
}
}
template<typename F, typename... Args, typename ResultT = typename std::result_of<F(Args...)>::value>
typename std::enable_if<std::is_same<void, ResultT>::value, typename TryWrapper<ResultT>::Type>::type
WrapWithTry(F &&f, Args &&...args)
{
try {
std::forward<F>(f)(std::forward<Args>(args)...);
return Try<void>();
} catch (...) {
return Try<void>(std::current_exception());
}
}
}// namespace sled
#endif// SLED_FUTURES_TRY_H

View File

@ -28,8 +28,7 @@ struct HasLockAndUnlock {
template<typename>
static char Test(...);
static constexpr bool value =
std::is_same<decltype(Test<T>(0)), int>::value;
static constexpr bool value = std::is_same<decltype(Test<T>(0)), int>::value;
};
}// namespace internal
@ -72,9 +71,7 @@ private:
std::recursive_mutex impl_;
};
template<typename TLock,
typename std::enable_if<internal::HasLockAndUnlock<TLock>::value,
TLock>::type * = nullptr>
template<typename TLock, typename std::enable_if<internal::HasLockAndUnlock<TLock>::value, TLock>::type * = nullptr>
class LockGuard final {
public:
LockGuard(const LockGuard &) = delete;
@ -159,9 +156,7 @@ public:
cv_.wait(lock.lock_, std::forward<Predicate>(pred));
return true;
} else {
return cv_.wait_for(lock.lock_,
std::chrono::milliseconds(timeout.ms()),
std::forward<Predicate>(pred));
return cv_.wait_for(lock.lock_, std::chrono::milliseconds(timeout.ms()), std::forward<Predicate>(pred));
}
}

10
include/sled/system/pid.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#ifndef SLED_SYSTEM_PID_H
#define SLED_SYSTEM_PID_H
#include <unistd.h>
namespace sled {
pid_t GetCachedPID();
}// namespace sled
#endif// SLED_SYSTEM_PID_H

View File

@ -0,0 +1,10 @@
#include <gtest/gtest.h>
#include <sled/futures/future.h>
#include <sled/futures/promise.h>
TEST(Future, Test1)
{
sled::Promise<int> promise;
sled::Future<int> future = promise.GetFuture();
promise.SetValue(42);
}

10
src/system/pid.cc Normal file
View File

@ -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