Added support for forwarding exceptions via Future.
This commit is contained in:
parent
4d19752964
commit
7d6fcc60fc
@ -5,10 +5,6 @@
|
|||||||
#define MIJIN_ASYNC_COROUTINE_HPP_INCLUDED 1
|
#define MIJIN_ASYNC_COROUTINE_HPP_INCLUDED 1
|
||||||
|
|
||||||
|
|
||||||
#if !defined(MIJIN_COROUTINE_ENABLE_DEBUG_INFO)
|
|
||||||
# define MIJIN_COROUTINE_ENABLE_DEBUG_INFO 0 // Capture stack each time a coroutine is started. Warning, expensive! // TODO: maybe implement a lighter version only storing the return address?
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <any>
|
#include <any>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <coroutine>
|
#include <coroutine>
|
||||||
@ -27,6 +23,15 @@
|
|||||||
#include "../util/misc.hpp"
|
#include "../util/misc.hpp"
|
||||||
#include "../util/scope_guard.hpp"
|
#include "../util/scope_guard.hpp"
|
||||||
#include "../util/traits.hpp"
|
#include "../util/traits.hpp"
|
||||||
|
|
||||||
|
#if !defined(MIJIN_COROUTINE_ENABLE_DEBUG_INFO)
|
||||||
|
# define MIJIN_COROUTINE_ENABLE_DEBUG_INFO 0 // Capture stack each time a coroutine is started. Warning, expensive! // TODO: maybe implement a lighter version only storing the return address?
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MIJIN_COROUTINE_ENABLE_EXCEPTIONS)
|
||||||
|
# define MIJIN_COROUTINE_ENABLE_EXCEPTIONS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if MIJIN_COROUTINE_ENABLE_DEBUG_INFO
|
#if MIJIN_COROUTINE_ENABLE_DEBUG_INFO
|
||||||
#include "../debug/stacktrace.hpp"
|
#include "../debug/stacktrace.hpp"
|
||||||
#endif
|
#endif
|
||||||
@ -186,10 +191,16 @@ struct TaskReturn<void, TPromise>
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TValue>
|
template<typename TValue, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
|
||||||
|
using TaskFuture = Future<TValue, TAllocator, MIJIN_COROUTINE_ENABLE_EXCEPTIONS>;
|
||||||
|
|
||||||
|
template<typename TValue, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
|
||||||
|
using TaskFuturePtr = FuturePtr<TValue, TAllocator, MIJIN_COROUTINE_ENABLE_EXCEPTIONS>;
|
||||||
|
|
||||||
|
template<typename TValue, template<typename> typename TAllocator>
|
||||||
struct TaskAwaitableFuture
|
struct TaskAwaitableFuture
|
||||||
{
|
{
|
||||||
FuturePtr<TValue> future;
|
TaskFuturePtr<TValue, TAllocator> future;
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool await_ready() const MIJIN_NOEXCEPT { return future->ready(); }
|
[[nodiscard]] constexpr bool await_ready() const MIJIN_NOEXCEPT { return future->ready(); }
|
||||||
constexpr void await_suspend(std::coroutine_handle<>) const MIJIN_NOEXCEPT {}
|
constexpr void await_suspend(std::coroutine_handle<>) const MIJIN_NOEXCEPT {}
|
||||||
@ -329,12 +340,12 @@ struct TaskPromise : impl::TaskReturn<typename TTraits::result_t, TaskPromise<TT
|
|||||||
|
|
||||||
// constexpr void unhandled_exception() MIJIN_NOEXCEPT {}
|
// constexpr void unhandled_exception() MIJIN_NOEXCEPT {}
|
||||||
|
|
||||||
template<typename TValue>
|
template<typename TValue, template<typename> typename TAllocator>
|
||||||
auto await_transform(FuturePtr<TValue> future, std::source_location sourceLoc = std::source_location::current()) MIJIN_NOEXCEPT
|
auto await_transform(TaskFuturePtr<TValue, TAllocator> future, std::source_location sourceLoc = std::source_location::current()) MIJIN_NOEXCEPT
|
||||||
{
|
{
|
||||||
MIJIN_ASSERT(loop_ != nullptr, "Cannot await future outside of a loop!");
|
MIJIN_ASSERT(loop_ != nullptr, "Cannot await future outside of a loop!");
|
||||||
sharedState_->sourceLoc = std::move(sourceLoc);
|
sharedState_->sourceLoc = std::move(sourceLoc);
|
||||||
TaskAwaitableFuture<TValue> awaitable{future};
|
TaskAwaitableFuture<TValue, TAllocator> awaitable{future};
|
||||||
if (!awaitable.await_ready())
|
if (!awaitable.await_ready())
|
||||||
{
|
{
|
||||||
state_.status = TaskStatus::WAITING;
|
state_.status = TaskStatus::WAITING;
|
||||||
@ -635,10 +646,10 @@ public:
|
|||||||
void setUncaughtExceptionHandler(exception_handler_t handler) MIJIN_NOEXCEPT { uncaughtExceptionHandler_ = std::move(handler); }
|
void setUncaughtExceptionHandler(exception_handler_t handler) MIJIN_NOEXCEPT { uncaughtExceptionHandler_ = std::move(handler); }
|
||||||
|
|
||||||
template<typename TResult>
|
template<typename TResult>
|
||||||
FuturePtr<TResult> addTaskImpl(TaskBase<TResult, TAllocator> task, TaskHandle* outHandle) MIJIN_NOEXCEPT;
|
TaskFuturePtr<TResult, TAllocator> addTaskImpl(TaskBase<TResult, TAllocator> task, TaskHandle* outHandle) MIJIN_NOEXCEPT;
|
||||||
|
|
||||||
template<typename TResult>
|
template<typename TResult>
|
||||||
FuturePtr<TResult> addTask(TaskBase<TResult, TAllocator> task, TaskHandle* outHandle = nullptr) MIJIN_NOEXCEPT
|
TaskFuturePtr<TResult, TAllocator> addTask(TaskBase<TResult, TAllocator> task, TaskHandle* outHandle = nullptr) MIJIN_NOEXCEPT
|
||||||
{
|
{
|
||||||
static_assert(TaskAllocatorTraits<TAllocator>::default_is_valid_v, "Allocator is not valid when default constructed, use makeTask() instead.");
|
static_assert(TaskAllocatorTraits<TAllocator>::default_is_valid_v, "Allocator is not valid when default constructed, use makeTask() instead.");
|
||||||
return addTaskImpl(std::move(task), outHandle);
|
return addTaskImpl(std::move(task), outHandle);
|
||||||
@ -664,7 +675,7 @@ protected:
|
|||||||
protected:
|
protected:
|
||||||
static inline TaskLoop*& currentLoopStorage() MIJIN_NOEXCEPT;
|
static inline TaskLoop*& currentLoopStorage() MIJIN_NOEXCEPT;
|
||||||
template<typename TResult>
|
template<typename TResult>
|
||||||
static inline void setFutureHelper(StoredTask& storedTask) MIJIN_NOEXCEPT;
|
static inline void setFutureHelper(StoredTask& storedTask) MIJIN_NOEXCEPT_IF(!MIJIN_COROUTINE_ENABLE_EXCEPTIONS);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename TResult = void>
|
template<typename TResult = void>
|
||||||
@ -811,12 +822,12 @@ TaskBase<TResult, TAllocator>::~TaskBase() MIJIN_NOEXCEPT
|
|||||||
|
|
||||||
template<template<typename> typename TAllocator>
|
template<template<typename> typename TAllocator>
|
||||||
template<typename TResult>
|
template<typename TResult>
|
||||||
FuturePtr<TResult> TaskLoop<TAllocator>::addTaskImpl(TaskBase<TResult, TAllocator> task, TaskHandle* outHandle) MIJIN_NOEXCEPT
|
TaskFuturePtr<TResult, TAllocator> TaskLoop<TAllocator>::addTaskImpl(TaskBase<TResult, TAllocator> task, TaskHandle* outHandle) MIJIN_NOEXCEPT
|
||||||
{
|
{
|
||||||
MIJIN_ASSERT(!task.getLoop(), "Attempting to add task that already has a loop!");
|
MIJIN_ASSERT(!task.getLoop(), "Attempting to add task that already has a loop!");
|
||||||
task.setLoop(this);
|
task.setLoop(this);
|
||||||
|
|
||||||
FuturePtr<TResult> future = std::allocate_shared<Future<TResult>>(TAllocator<Future<TResult>>(allocator_), allocator_);
|
TaskFuturePtr<TResult, TAllocator> future = std::allocate_shared<TaskFuture<TResult, TAllocator>>(TAllocator<Future<TResult, TAllocator>>(allocator_), allocator_);
|
||||||
auto setFuture = &setFutureHelper<TResult>;
|
auto setFuture = &setFutureHelper<TResult>;
|
||||||
|
|
||||||
if (outHandle != nullptr)
|
if (outHandle != nullptr)
|
||||||
@ -855,7 +866,7 @@ TaskStatus TaskLoop<TAllocator>::tickTask(StoredTask& task)
|
|||||||
while (status == TaskStatus::RUNNING);
|
while (status == TaskStatus::RUNNING);
|
||||||
impl::gCurrentTaskState = nullptr;
|
impl::gCurrentTaskState = nullptr;
|
||||||
|
|
||||||
#if MIJIN_COROUTINE_ENABLE_EXCEPTION_HANDLING
|
#if MIJIN_COROUTINE_ENABLE_EXCEPTION_HANDLING && !MIJIN_COROUTINE_ENABLE_EXCEPTIONS
|
||||||
if (task.task && task.task->exception())
|
if (task.task && task.task->exception())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -883,7 +894,22 @@ TaskStatus TaskLoop<TAllocator>::tickTask(StoredTask& task)
|
|||||||
#endif // MIJIN_COROUTINE_ENABLE_EXCEPTION_HANDLING
|
#endif // MIJIN_COROUTINE_ENABLE_EXCEPTION_HANDLING
|
||||||
if (status == TaskStatus::YIELDED || status == TaskStatus::FINISHED)
|
if (status == TaskStatus::YIELDED || status == TaskStatus::FINISHED)
|
||||||
{
|
{
|
||||||
task.setFuture(task);
|
try
|
||||||
|
{
|
||||||
|
task.setFuture(task);
|
||||||
|
}
|
||||||
|
catch(TaskCancelled&) {}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
if (uncaughtExceptionHandler_)
|
||||||
|
{
|
||||||
|
uncaughtExceptionHandler_(std::current_exception());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -910,10 +936,22 @@ template<template<typename> typename TAllocator>
|
|||||||
|
|
||||||
template<template<typename> typename TAllocator>
|
template<template<typename> typename TAllocator>
|
||||||
template<typename TResult>
|
template<typename TResult>
|
||||||
/* static */ inline void TaskLoop<TAllocator>::setFutureHelper(StoredTask& storedTask) MIJIN_NOEXCEPT
|
/* static */ inline void TaskLoop<TAllocator>::setFutureHelper(StoredTask& storedTask) MIJIN_NOEXCEPT_IF(!MIJIN_COROUTINE_ENABLE_EXCEPTIONS)
|
||||||
{
|
{
|
||||||
TaskBase<TResult, TAllocator>& task = *static_cast<TaskBase<TResult, TAllocator>*>(storedTask.task->raw());
|
TaskBase<TResult, TAllocator>& task = *static_cast<TaskBase<TResult, TAllocator>*>(storedTask.task->raw());
|
||||||
auto future = std::any_cast<FuturePtr<TResult>>(storedTask.resultData);
|
const auto& future = std::any_cast<TaskFuturePtr<TResult, TAllocator>&>(storedTask.resultData);
|
||||||
|
#if MIJIN_COROUTINE_ENABLE_EXCEPTIONS
|
||||||
|
if (task.state().exception)
|
||||||
|
{
|
||||||
|
if (future.use_count() < 2)
|
||||||
|
{
|
||||||
|
// future has been discarded, but someone must handle the exception
|
||||||
|
std::rethrow_exception(task.state().exception);
|
||||||
|
}
|
||||||
|
future->setException(task.state().exception);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if constexpr (!std::is_same_v<TResult, void>)
|
if constexpr (!std::is_same_v<TResult, void>)
|
||||||
{
|
{
|
||||||
@ -1249,14 +1287,14 @@ inline TaskAwaitableSuspend c_suspend() {
|
|||||||
return TaskAwaitableSuspend();
|
return TaskAwaitableSuspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<template<typename...> typename TCollection, typename TType, typename... TTemplateArgs>
|
template<template<typename...> typename TCollection, FutureType TFuture, typename... TTemplateArgs>
|
||||||
Task<> c_allDone(const TCollection<FuturePtr<TType>, TTemplateArgs...>& futures)
|
Task<> c_allDone(const TCollection<TFuture, TTemplateArgs...>& futures)
|
||||||
{
|
{
|
||||||
bool allDone = true;
|
bool allDone = true;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
allDone = true;
|
allDone = true;
|
||||||
for (const FuturePtr<TType>& future : futures)
|
for (const TFuture& future : futures)
|
||||||
{
|
{
|
||||||
if (future && !future->ready()) {
|
if (future && !future->ready()) {
|
||||||
allDone = false;
|
allDone = false;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#if !defined(MIJIN_ASYNC_FUTURE_HPP_INCLUDED)
|
#if !defined(MIJIN_ASYNC_FUTURE_HPP_INCLUDED)
|
||||||
#define MIJIN_ASYNC_FUTURE_HPP_INCLUDED 1
|
#define MIJIN_ASYNC_FUTURE_HPP_INCLUDED 1
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
@ -27,43 +28,113 @@ namespace mijin
|
|||||||
//
|
//
|
||||||
// public types
|
// public types
|
||||||
//
|
//
|
||||||
template<typename TValue, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
|
template<typename TValue, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR, bool exceptions = false>
|
||||||
class Future;
|
class Future;
|
||||||
|
|
||||||
// TODO: add support for mutexes and waiting for futures
|
// TODO: add support for mutexes and waiting for futures
|
||||||
namespace impl
|
namespace impl
|
||||||
{
|
{
|
||||||
template<typename TValue>
|
template<typename TValue, bool exceptions>
|
||||||
struct FutureStorage
|
struct FutureStorage
|
||||||
{
|
{
|
||||||
Optional<TValue> value;
|
Optional<TValue> value;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool hasValue() const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
return !value.empty();
|
||||||
|
}
|
||||||
|
|
||||||
void setValue(TValue value_) MIJIN_NOEXCEPT { value = std::move(value_); }
|
void setValue(TValue value_) MIJIN_NOEXCEPT { value = std::move(value_); }
|
||||||
[[nodiscard]] TValue& getValue() MIJIN_NOEXCEPT { return value.get(); }
|
[[nodiscard]] TValue& getValue() MIJIN_NOEXCEPT { return value.get(); }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// template<typename TValue>
|
template<>
|
||||||
// struct FutureStorage<TValue&>
|
struct FutureStorage<void, false>
|
||||||
// {
|
{
|
||||||
// Optional<TValue*> value;
|
bool isSet = false;
|
||||||
//
|
|
||||||
// void setValue(TValue& value_) MIJIN_NOEXCEPT { value = &value_; }
|
[[nodiscard]]
|
||||||
// [[nodiscard]] TValue& getValue() const MIJIN_NOEXCEPT { return *value.get(); }
|
bool hasValue() const MIJIN_NOEXCEPT
|
||||||
// };
|
{
|
||||||
|
return isSet;
|
||||||
|
}
|
||||||
|
void setValue() MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
isSet = true;
|
||||||
|
}
|
||||||
|
void getValue() MIJIN_NOEXCEPT {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if MIJIN_ENABLE_EXCEPTIONS
|
||||||
|
template<typename TValue>
|
||||||
|
struct FutureStorage<TValue, true>
|
||||||
|
{
|
||||||
|
Optional<TValue> value;
|
||||||
|
std::exception_ptr exception;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool hasValue() const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
if (exception) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !value.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setException(std::exception_ptr exc) MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
exception = std::move(exc);
|
||||||
|
}
|
||||||
|
void setValue(TValue value_) MIJIN_NOEXCEPT { value = std::move(value_); }
|
||||||
|
[[nodiscard]] TValue& getValue()
|
||||||
|
{
|
||||||
|
if (exception) {
|
||||||
|
std::rethrow_exception(exception);
|
||||||
|
}
|
||||||
|
return value.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct FutureStorage<void>
|
struct FutureStorage<void, true>
|
||||||
{
|
{
|
||||||
|
bool isSet = false;
|
||||||
|
std::exception_ptr exception;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool hasValue() const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
if (exception) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return isSet;
|
||||||
|
}
|
||||||
|
void setException(std::exception_ptr exc) MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
exception = std::move(exc);
|
||||||
|
}
|
||||||
|
void setValue() MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
isSet = true;
|
||||||
|
}
|
||||||
|
void getValue()
|
||||||
|
{
|
||||||
|
if (exception) {
|
||||||
|
std::rethrow_exception(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
} // namespace impl
|
} // namespace impl
|
||||||
|
|
||||||
template<typename TValue, template<typename> typename TAllocator>
|
template<typename TValue, template<typename> typename TAllocator, bool exceptions>
|
||||||
class Future
|
class Future
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
[[no_unique_address]] impl::FutureStorage<TValue> value_;
|
impl::FutureStorage<TValue, exceptions> value_;
|
||||||
bool isSet_ = false;
|
|
||||||
public:
|
public:
|
||||||
Future() = default;
|
Future() = default;
|
||||||
Future(const Future&) = delete;
|
Future(const Future&) = delete;
|
||||||
@ -80,10 +151,12 @@ public:
|
|||||||
constexpr bool operator!() const MIJIN_NOEXCEPT { return !ready(); }
|
constexpr bool operator!() const MIJIN_NOEXCEPT { return !ready(); }
|
||||||
public: // access
|
public: // access
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr decltype(auto) get() MIJIN_NOEXCEPT
|
constexpr decltype(auto) get() MIJIN_NOEXCEPT_IF(!exceptions)
|
||||||
{
|
{
|
||||||
MIJIN_ASSERT(isSet_, "Attempting to get from future that is not ready.");
|
MIJIN_ASSERT(ready(), "Attempting to get from future that is not ready.");
|
||||||
if constexpr(std::is_same_v<TValue, void>) {
|
if constexpr(std::is_same_v<TValue, void>)
|
||||||
|
{
|
||||||
|
value_.getValue(); // in case of exceptions
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -91,10 +164,12 @@ public: // access
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr decltype(auto) get() const MIJIN_NOEXCEPT
|
constexpr decltype(auto) get() const MIJIN_NOEXCEPT_IF(!exceptions)
|
||||||
{
|
{
|
||||||
MIJIN_ASSERT(isSet_, "Attempting to get from future that is not ready.");
|
MIJIN_ASSERT(ready(), "Attempting to get from future that is not ready.");
|
||||||
if constexpr(std::is_same_v<TValue, void>) {
|
if constexpr(std::is_same_v<TValue, void>)
|
||||||
|
{
|
||||||
|
value_.getValue(); // in case of exceptions
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -104,22 +179,22 @@ public: // access
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr bool ready() const MIJIN_NOEXCEPT
|
constexpr bool ready() const MIJIN_NOEXCEPT
|
||||||
{
|
{
|
||||||
return isSet_;
|
return value_.hasValue();
|
||||||
}
|
}
|
||||||
public: // modification
|
public: // modification
|
||||||
template<typename TArg> requires (!std::is_same_v<TValue, void>)
|
template<typename TArg> requires (!std::is_same_v<TValue, void>)
|
||||||
constexpr void set(TArg&& value) MIJIN_NOEXCEPT
|
constexpr void set(TArg&& value) MIJIN_NOEXCEPT
|
||||||
{
|
{
|
||||||
MIJIN_ASSERT(!isSet_, "Trying to set a future twice!");
|
MIJIN_ASSERT(!ready(), "Trying to set a future twice!");
|
||||||
value_.setValue(std::move(value));
|
value_.setValue(std::move(value));
|
||||||
isSet_ = true;
|
|
||||||
sigSet.emit();
|
sigSet.emit();
|
||||||
}
|
}
|
||||||
constexpr void set() MIJIN_NOEXCEPT requires (std::is_same_v<TValue, void>)
|
constexpr void set() MIJIN_NOEXCEPT requires (std::is_same_v<TValue, void>)
|
||||||
{
|
{
|
||||||
MIJIN_ASSERT(!isSet_, "Trying to set a future twice!");
|
MIJIN_ASSERT(!ready(), "Trying to set a future twice!");
|
||||||
isSet_ = true;
|
if constexpr (std::is_same_v<TValue, void>)
|
||||||
if constexpr (std::is_same_v<TValue, void>) {
|
{
|
||||||
|
value_.setValue();
|
||||||
sigSet.emit();
|
sigSet.emit();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -127,12 +202,38 @@ public: // modification
|
|||||||
MIJIN_ERROR("Attempting to call set(void) on future with value.");
|
MIJIN_ERROR("Attempting to call set(void) on future with value.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
constexpr void setException(std::exception_ptr exception) requires (exceptions)
|
||||||
|
{
|
||||||
|
MIJIN_ASSERT(!ready(), "Trying to set a future twice!");
|
||||||
|
if constexpr (exceptions)
|
||||||
|
{
|
||||||
|
value_.setException(std::move(exception));
|
||||||
|
}
|
||||||
|
}
|
||||||
public: // signals
|
public: // signals
|
||||||
BaseSignal<TAllocator> sigSet;
|
BaseSignal<TAllocator> sigSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename TValue = void, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR, bool exceptions = false>
|
||||||
|
using FuturePtr = std::shared_ptr<Future<TValue, TAllocator, exceptions>>;
|
||||||
|
|
||||||
template<typename TValue = void, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
|
template<typename TValue = void, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
|
||||||
using FuturePtr = std::shared_ptr<Future<TValue, TAllocator>>;
|
using ExceptFuture = Future<TValue, TAllocator, true>;
|
||||||
|
|
||||||
|
template<typename TValue = void, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
|
||||||
|
using ExceptFuturePtr = std::shared_ptr<Future<TValue, TAllocator, true>>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct is_future : std::false_type {};
|
||||||
|
|
||||||
|
template<typename TValue, template<typename> typename TAllocator, bool exceptions>
|
||||||
|
struct is_future<Future<TValue, TAllocator, exceptions>> : std::true_type {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr bool is_future_t = is_future<T>::value;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept FutureType = is_future<T>::value;
|
||||||
|
|
||||||
//
|
//
|
||||||
// public functions
|
// public functions
|
||||||
|
Loading…
x
Reference in New Issue
Block a user