Feat support via
All checks were successful
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (push) Successful in 1m42s
linux-arm-gcc / linux-gcc-armhf (push) Successful in 1m59s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Successful in 2m37s
linux-x64-gcc / linux-gcc (Release) (push) Successful in 2m35s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Successful in 2m42s
linux-x64-gcc / linux-gcc (Debug) (push) Successful in 2m45s
All checks were successful
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (push) Successful in 1m42s
linux-arm-gcc / linux-gcc-armhf (push) Successful in 1m59s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Successful in 2m37s
linux-x64-gcc / linux-gcc (Release) (push) Successful in 2m35s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Successful in 2m42s
linux-x64-gcc / linux-gcc (Debug) (push) Successful in 2m45s
This commit is contained in:
parent
b385bca8e4
commit
569d08139d
@ -2,12 +2,14 @@
|
|||||||
#define SLED_FUTURES_FUTURE_H
|
#define SLED_FUTURES_FUTURE_H
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "sled/exec/detail/invoke_result.h"
|
||||||
#include "sled/futures/internal/failure_handling.h"
|
#include "sled/futures/internal/failure_handling.h"
|
||||||
#include "sled/futures/internal/promise.h"
|
#include "sled/futures/internal/promise.h"
|
||||||
#include "sled/lang/attributes.h"
|
#include "sled/lang/attributes.h"
|
||||||
#include "sled/log/log.h"
|
#include "sled/log/log.h"
|
||||||
#include "sled/synchronization/event.h"
|
#include "sled/synchronization/event.h"
|
||||||
#include "sled/synchronization/mutex.h"
|
#include "sled/synchronization/mutex.h"
|
||||||
|
#include "sled/task_queue/task_queue_base.h"
|
||||||
#include "sled/variant.h"
|
#include "sled/variant.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <list>
|
#include <list>
|
||||||
@ -22,6 +24,9 @@ template<typename R, typename F, typename... Args>
|
|||||||
struct is_invocable_r : std::is_constructible<std::function<R(Args...)>,
|
struct is_invocable_r : std::is_constructible<std::function<R(Args...)>,
|
||||||
std::reference_wrapper<typename std::remove_reference<F>::type>> {};
|
std::reference_wrapper<typename std::remove_reference<F>::type>> {};
|
||||||
|
|
||||||
|
template<bool cond, typename T = void>
|
||||||
|
using enable_if_t = typename std::enable_if<cond, T>::type;
|
||||||
|
|
||||||
enum FutureState {
|
enum FutureState {
|
||||||
kNotCompletedFuture = 0,
|
kNotCompletedFuture = 0,
|
||||||
kSuccessFuture = 1,
|
kSuccessFuture = 1,
|
||||||
@ -177,7 +182,7 @@ public:
|
|||||||
return Future<T, FailureT>(data_);
|
return Future<T, FailureT>(data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Func, typename = typename std::enable_if<detail::is_invocable<Func, FailureT>::value>::type>
|
template<typename Func, typename = detail::enable_if_t<detail::is_invocable<Func, FailureT>::value>>
|
||||||
Future<T, FailureT> OnFailure(Func &&f) const noexcept
|
Future<T, FailureT> OnFailure(Func &&f) const noexcept
|
||||||
{
|
{
|
||||||
SLED_ASSERT(data_ != nullptr, "Future is not valid");
|
SLED_ASSERT(data_ != nullptr, "Future is not valid");
|
||||||
@ -204,7 +209,7 @@ public:
|
|||||||
return Future<T, FailureT>(data_);
|
return Future<T, FailureT>(data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Func, typename = typename std::enable_if<detail::is_invocable<Func>::value>::type>
|
template<typename Func, typename = detail::enable_if_t<detail::is_invocable<Func>::value>>
|
||||||
Future<T, FailureT> OnComplete(Func &&f) const noexcept
|
Future<T, FailureT> OnComplete(Func &&f) const noexcept
|
||||||
{
|
{
|
||||||
SLED_ASSERT(data_ != nullptr, "Future is not valid");
|
SLED_ASSERT(data_ != nullptr, "Future is not valid");
|
||||||
@ -213,12 +218,96 @@ public:
|
|||||||
return Future<T, FailureT>(data_);
|
return Future<T, FailureT>(data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// template<typename Func, typename = typename std::enable_if<detail::is_invocable<Func>::value>::type>
|
template<typename Func, typename = detail::enable_if_t<detail::is_invocable_r<bool, Func, T>::value>>
|
||||||
// Future<T, FailureT> OnComplete(Func &&F) const noexcept
|
Future<T, FailureT>
|
||||||
// {
|
Filter(Func &&f,
|
||||||
// SLED_ASSERT(data_ != nullptr, "Future is not valid");
|
const FailureT &rejected = failure::FailureFromString<FailureT>("Result wasn't good enough")) const noexcept
|
||||||
// OnSuccess([f](const auto &) noexcept { f(); })
|
{
|
||||||
// }
|
Future<T, FailureT> result = Future<T, FailureT>::Create();
|
||||||
|
OnSuccess([result, f, rejected](const T &v) mutable noexcept {
|
||||||
|
try {
|
||||||
|
if (f(v)) {
|
||||||
|
result.FillSuccess(v);
|
||||||
|
} else {
|
||||||
|
result.FillFailure(rejected);
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
result.FillFailure(detail::ExceptionFailure<FailureT>(e));
|
||||||
|
} catch (...) {
|
||||||
|
result.FillFailure(detail::ExceptionFailure<FailureT>());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
OnFailure([result](const FailureT &failure) noexcept { result.FillFailure(failure); });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Func, typename U = eggs::invoke_result_t<Func, T>>
|
||||||
|
Future<U, FailureT> Map(Func &&f) const noexcept
|
||||||
|
{
|
||||||
|
Future<U, FailureT> result = Future<U, FailureT>::Create();
|
||||||
|
OnSuccess([result, f](const T &v) mutable noexcept {
|
||||||
|
try {
|
||||||
|
result.FillSuccess(f(v));
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
result.FillFailure(detail::ExceptionFailure<FailureT>(e));
|
||||||
|
} catch (...) {
|
||||||
|
result.FillFailure(detail::ExceptionFailure<FailureT>());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
OnFailure([result](const FailureT &failure) mutable noexcept { result.FillFailure(failure); });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Func, typename OtherFailureT = eggs::invoke_result_t<Func, FailureT>>
|
||||||
|
Future<T, OtherFailureT> MapFailure(Func &&f) const noexcept
|
||||||
|
{
|
||||||
|
Future<T, OtherFailureT> result = Future<T, OtherFailureT>::Create();
|
||||||
|
OnSuccess([result](const T &v) mutable noexcept { result.FillSuccess(v); });
|
||||||
|
OnFailure([result, f](const FailureT &failure) noexcept {
|
||||||
|
try {
|
||||||
|
result.FillFailure(f(failure));
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
result.FillFailure(detail::ExceptionFailure<OtherFailureT>(e));
|
||||||
|
} catch (...) {
|
||||||
|
result.FillFailure(detail::ExceptionFailure<OtherFailureT>());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Func, typename U = decltype(std::declval<eggs::invoke_result_t<Func, T>>().Result())>
|
||||||
|
Future<U, FailureT> FlatMap(Func &&f) const noexcept
|
||||||
|
{
|
||||||
|
Future<U, FailureT> result = Future<U, FailureT>::Create();
|
||||||
|
|
||||||
|
OnSuccess([result, f](const T &v) mutable noexcept {
|
||||||
|
try {
|
||||||
|
f(v).OnSuccess([result](const U &v) mutable noexcept { result.FillSuccess(v); })
|
||||||
|
.OnFailure([result](const FailureT &failure) mutable noexcept { result.FillFailure(failure); });
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
result.FillFailure(detail::ExceptionFailure<FailureT>(e));
|
||||||
|
} catch (...) {
|
||||||
|
result.FillFailure(detail::ExceptionFailure<FailureT>());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
OnFailure([result](const FailureT &failure) mutable noexcept { result.FillFailure(failure); });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<T, FailureT> Via(TaskQueueBase *task_queue) const noexcept
|
||||||
|
{
|
||||||
|
SLED_ASSERT(task_queue != nullptr, "TaskQueue is not valid");
|
||||||
|
|
||||||
|
Future<T, FailureT> result = Future<T, FailureT>::Create();
|
||||||
|
OnSuccess([result, task_queue](const T &v) mutable noexcept {
|
||||||
|
task_queue->PostTask([result, v]() mutable noexcept { result.FillSuccess(v); });
|
||||||
|
});
|
||||||
|
OnFailure([result, task_queue](const FailureT &failure) mutable noexcept {
|
||||||
|
task_queue->PostTask([result, failure]() mutable noexcept { result.FillFailure(failure); });
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static Future<typename std::decay<T>::type, FailureT> Successful(T &&value) noexcept
|
static Future<typename std::decay<T>::type, FailureT> Successful(T &&value) noexcept
|
||||||
{
|
{
|
||||||
@ -276,7 +365,8 @@ private:
|
|||||||
if (IsCompleted()) { return; }
|
if (IsCompleted()) { return; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
data_->value.template emplace<T>(std::move(value));
|
// data_->value.template emplace<T>(std::move(value));
|
||||||
|
data_->value = std::move(value);
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
data_->state.store(detail::kSuccessFuture, std::memory_order_release);
|
data_->state.store(detail::kSuccessFuture, std::memory_order_release);
|
||||||
callbacks = std::move(data_->success_callbacks);
|
callbacks = std::move(data_->success_callbacks);
|
||||||
@ -305,7 +395,8 @@ private:
|
|||||||
sled::MutexLock lock(&data_->mutex_);
|
sled::MutexLock lock(&data_->mutex_);
|
||||||
if (IsCompleted()) { return; }
|
if (IsCompleted()) { return; }
|
||||||
try {
|
try {
|
||||||
data_->value.template emplace<FailureT>(std::move(reason));
|
// data_->value.template emplace<FailureT>(std::move(reason));
|
||||||
|
data_->value = std::move(reason);
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
data_->state.store(detail::kFailedFuture, std::memory_order_release);
|
data_->state.store(detail::kFailedFuture, std::memory_order_release);
|
||||||
callbacks = std::move(data_->failure_callbacks);
|
callbacks = std::move(data_->failure_callbacks);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <sled/futures/future.h>
|
#include <sled/futures/future.h>
|
||||||
|
#include <sled/system/thread.h>
|
||||||
|
|
||||||
TEST_SUITE("future")
|
TEST_SUITE("future")
|
||||||
{
|
{
|
||||||
@ -23,4 +24,45 @@ TEST_SUITE("future")
|
|||||||
CHECK_EQ(f.FailureReason(), "error");
|
CHECK_EQ(f.FailureReason(), "error");
|
||||||
}
|
}
|
||||||
TEST_CASE("thread success") {}
|
TEST_CASE("thread success") {}
|
||||||
|
|
||||||
|
TEST_CASE("map")
|
||||||
|
{
|
||||||
|
sled::Promise<int, std::string> p;
|
||||||
|
auto f = p.GetFuture();
|
||||||
|
auto f2 = f.Map([](int i) { return i + 1; });
|
||||||
|
p.Success(42);
|
||||||
|
CHECK(f2.Wait(-1));
|
||||||
|
CHECK_EQ(f2.Result(), 43);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("FlatMap")
|
||||||
|
{
|
||||||
|
// sled::Promise<int, std::string> p;
|
||||||
|
// auto f = p.GetFuture().FlatMap([](int i) {
|
||||||
|
// auto str = std::to_string(i);
|
||||||
|
// sled::Promise<std::string, std::string> p;
|
||||||
|
// p.Success(str);
|
||||||
|
//
|
||||||
|
// return p.GetFuture();
|
||||||
|
// });
|
||||||
|
// p.Success(42);
|
||||||
|
// CHECK(f.Wait(-1));
|
||||||
|
// CHECK_EQ(f.Result(), "42");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Via")
|
||||||
|
{
|
||||||
|
std::unique_ptr<sled::Thread> thread = sled::Thread::Create();
|
||||||
|
thread->Start();
|
||||||
|
|
||||||
|
std::thread::id tid = thread->BlockingCall([]() { return std::this_thread::get_id(); });
|
||||||
|
std::thread::id self_tid = std::this_thread::get_id();
|
||||||
|
|
||||||
|
sled::Promise<int, std::string> p;
|
||||||
|
auto f = p.GetFuture().Via(thread.get()).Map([](int i) { return std::this_thread::get_id(); });
|
||||||
|
p.Success(42);
|
||||||
|
|
||||||
|
CHECK_EQ(tid, f.Result());
|
||||||
|
CHECK_NE(self_tid, f.Result());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user