Loading src/sled/futures/future.h +101 −10 Original line number Diff line number Diff line Loading @@ -2,12 +2,14 @@ #define SLED_FUTURES_FUTURE_H #pragma once #include "sled/exec/detail/invoke_result.h" #include "sled/futures/internal/failure_handling.h" #include "sled/futures/internal/promise.h" #include "sled/lang/attributes.h" #include "sled/log/log.h" #include "sled/synchronization/event.h" #include "sled/synchronization/mutex.h" #include "sled/task_queue/task_queue_base.h" #include "sled/variant.h" #include <atomic> #include <list> Loading @@ -22,6 +24,9 @@ template<typename R, typename F, typename... Args> struct is_invocable_r : std::is_constructible<std::function<R(Args...)>, 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 { kNotCompletedFuture = 0, kSuccessFuture = 1, Loading Loading @@ -177,7 +182,7 @@ public: 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 { SLED_ASSERT(data_ != nullptr, "Future is not valid"); Loading @@ -204,7 +209,7 @@ public: 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 { SLED_ASSERT(data_ != nullptr, "Future is not valid"); Loading @@ -213,12 +218,96 @@ public: return Future<T, FailureT>(data_); } // template<typename Func, typename = typename std::enable_if<detail::is_invocable<Func>::value>::type> // Future<T, FailureT> OnComplete(Func &&F) const noexcept // { // SLED_ASSERT(data_ != nullptr, "Future is not valid"); // OnSuccess([f](const auto &) noexcept { f(); }) // } template<typename Func, typename = detail::enable_if_t<detail::is_invocable_r<bool, Func, T>::value>> Future<T, FailureT> Filter(Func &&f, const FailureT &rejected = failure::FailureFromString<FailureT>("Result wasn't good enough")) const noexcept { 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 { Loading Loading @@ -276,7 +365,8 @@ private: if (IsCompleted()) { return; } try { data_->value.template emplace<T>(std::move(value)); // data_->value.template emplace<T>(std::move(value)); data_->value = std::move(value); } catch (...) {} data_->state.store(detail::kSuccessFuture, std::memory_order_release); callbacks = std::move(data_->success_callbacks); Loading Loading @@ -305,7 +395,8 @@ private: sled::MutexLock lock(&data_->mutex_); if (IsCompleted()) { return; } try { data_->value.template emplace<FailureT>(std::move(reason)); // data_->value.template emplace<FailureT>(std::move(reason)); data_->value = std::move(reason); } catch (...) {} data_->state.store(detail::kFailedFuture, std::memory_order_release); callbacks = std::move(data_->failure_callbacks); Loading src/sled/futures/future_test.cc +42 −0 Original line number Diff line number Diff line #include <sled/futures/future.h> #include <sled/system/thread.h> TEST_SUITE("future") { Loading @@ -23,4 +24,45 @@ TEST_SUITE("future") CHECK_EQ(f.FailureReason(), "error"); } 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
src/sled/futures/future.h +101 −10 Original line number Diff line number Diff line Loading @@ -2,12 +2,14 @@ #define SLED_FUTURES_FUTURE_H #pragma once #include "sled/exec/detail/invoke_result.h" #include "sled/futures/internal/failure_handling.h" #include "sled/futures/internal/promise.h" #include "sled/lang/attributes.h" #include "sled/log/log.h" #include "sled/synchronization/event.h" #include "sled/synchronization/mutex.h" #include "sled/task_queue/task_queue_base.h" #include "sled/variant.h" #include <atomic> #include <list> Loading @@ -22,6 +24,9 @@ template<typename R, typename F, typename... Args> struct is_invocable_r : std::is_constructible<std::function<R(Args...)>, 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 { kNotCompletedFuture = 0, kSuccessFuture = 1, Loading Loading @@ -177,7 +182,7 @@ public: 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 { SLED_ASSERT(data_ != nullptr, "Future is not valid"); Loading @@ -204,7 +209,7 @@ public: 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 { SLED_ASSERT(data_ != nullptr, "Future is not valid"); Loading @@ -213,12 +218,96 @@ public: return Future<T, FailureT>(data_); } // template<typename Func, typename = typename std::enable_if<detail::is_invocable<Func>::value>::type> // Future<T, FailureT> OnComplete(Func &&F) const noexcept // { // SLED_ASSERT(data_ != nullptr, "Future is not valid"); // OnSuccess([f](const auto &) noexcept { f(); }) // } template<typename Func, typename = detail::enable_if_t<detail::is_invocable_r<bool, Func, T>::value>> Future<T, FailureT> Filter(Func &&f, const FailureT &rejected = failure::FailureFromString<FailureT>("Result wasn't good enough")) const noexcept { 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 { Loading Loading @@ -276,7 +365,8 @@ private: if (IsCompleted()) { return; } try { data_->value.template emplace<T>(std::move(value)); // data_->value.template emplace<T>(std::move(value)); data_->value = std::move(value); } catch (...) {} data_->state.store(detail::kSuccessFuture, std::memory_order_release); callbacks = std::move(data_->success_callbacks); Loading Loading @@ -305,7 +395,8 @@ private: sled::MutexLock lock(&data_->mutex_); if (IsCompleted()) { return; } try { data_->value.template emplace<FailureT>(std::move(reason)); // data_->value.template emplace<FailureT>(std::move(reason)); data_->value = std::move(reason); } catch (...) {} data_->state.store(detail::kFailedFuture, std::memory_order_release); callbacks = std::move(data_->failure_callbacks); Loading
src/sled/futures/future_test.cc +42 −0 Original line number Diff line number Diff line #include <sled/futures/future.h> #include <sled/system/thread.h> TEST_SUITE("future") { Loading @@ -23,4 +24,45 @@ TEST_SUITE("future") CHECK_EQ(f.FailureReason(), "error"); } 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()); } }