feat add futures
This commit is contained in:
parent
f8d2b051ee
commit
15bdc54bef
@ -36,7 +36,7 @@ BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
BreakInheritanceList: BeforeColon
|
||||
ColumnLimit: 80
|
||||
ColumnLimit: 120
|
||||
CompactNamespaces: false
|
||||
ContinuationIndentWidth: 4
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
|
@ -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
|
||||
|
149
include/sled/futures/future.h
Normal file
149
include/sled/futures/future.h
Normal 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
|
49
include/sled/futures/promise.h
Normal file
49
include/sled/futures/promise.h
Normal 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
|
34
include/sled/futures/state.h
Normal file
34
include/sled/futures/state.h
Normal 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
267
include/sled/futures/try.h
Normal 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
|
@ -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
10
include/sled/system/pid.h
Normal 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
|
10
src/futures/future_test.cc
Normal file
10
src/futures/future_test.cc
Normal 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
10
src/system/pid.cc
Normal 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
|
Loading…
x
Reference in New Issue
Block a user