Added (pretty limited) support for uncaught exceptions in coroutines.

This commit is contained in:
Patrick 2023-11-13 11:34:47 +01:00
parent dff7b30b2d
commit 0b8772c952

View File

@ -455,11 +455,15 @@ public:
std::function<void(StoredTask&)> setFuture;
std::any resultData;
};
using exception_handler_t = std::function<void(std::exception_ptr)>;
protected:
using task_vector_t = std::vector<StoredTask>;
template<typename TTask>
using wrapped_task_ptr_t = std::unique_ptr<WrappedTask<TTask>>;
exception_handler_t uncaughtExceptionHandler_;
public:
TaskLoop() = default;
TaskLoop(const TaskLoop&) = delete;
@ -469,6 +473,8 @@ public:
TaskLoop& operator=(const TaskLoop&) = delete;
TaskLoop& operator=(TaskLoop&&) = delete;
void setUncaughtExceptionHandler(exception_handler_t handler) noexcept { uncaughtExceptionHandler_ = std::move(handler); }
template<typename TResult>
inline FuturePtr<TResult> addTask(TaskBase<TResult> task, TaskHandle* outHandle = nullptr) noexcept;
@ -477,7 +483,7 @@ public:
[[nodiscard]] static TaskLoop& current() noexcept;
protected:
inline TaskStatus tickTask(StoredTask& task) noexcept;
inline TaskStatus tickTask(StoredTask& task);
protected:
static inline TaskLoop*& currentLoopStorage() noexcept;
template<typename TResult>
@ -502,8 +508,8 @@ public: // TaskLoop implementation
public: // public interface
[[nodiscard]] constexpr bool empty() const noexcept { return tasks_.empty() && newTasks_.empty(); }
inline CanContinue tick() noexcept;
inline void runUntilDone(IgnoreWaiting ignoreWaiting = IgnoreWaiting::NO) noexcept;
inline CanContinue tick();
inline void runUntilDone(IgnoreWaiting ignoreWaiting = IgnoreWaiting::NO);
private:
inline void assertCorrectThread() { MIJIN_ASSERT(threadId_ == std::thread::id() || threadId_ == std::this_thread::get_id(), "Unsafe to TaskLoop from different thread!"); }
};
@ -582,7 +588,7 @@ inline FuturePtr<TResult> TaskLoop::addTask(TaskBase<TResult> task, TaskHandle*
return future;
}
inline TaskStatus TaskLoop::tickTask(StoredTask& task) noexcept
inline TaskStatus TaskLoop::tickTask(StoredTask& task)
{
TaskStatus status = {};
impl::gCurrentTask = &task;
@ -596,6 +602,23 @@ inline TaskStatus TaskLoop::tickTask(StoredTask& task) noexcept
if (task.task && task.task->exception())
{
try
{
std::rethrow_exception(task.task->exception());
}
catch(impl::TaskCancelled&) {} // ignore those
catch(...)
{
if (uncaughtExceptionHandler_)
{
uncaughtExceptionHandler_(std::current_exception());
}
else
{
throw;
}
}
// TODO: handle the exception somehow, others may be waiting
return TaskStatus::FINISHED;
}
@ -644,7 +667,7 @@ inline std::suspend_always switchContext(TaskLoop& taskLoop)
return {};
}
inline auto SimpleTaskLoop::tick() noexcept -> CanContinue
inline auto SimpleTaskLoop::tick() -> CanContinue
{
// set current taskloop
MIJIN_ASSERT(currentLoopStorage() == nullptr, "Trying to tick a loop from a coroutine, this is not supported.");
@ -705,7 +728,7 @@ inline auto SimpleTaskLoop::tick() noexcept -> CanContinue
return canContinue;
}
inline void SimpleTaskLoop::runUntilDone(IgnoreWaiting ignoreWaiting) noexcept
inline void SimpleTaskLoop::runUntilDone(IgnoreWaiting ignoreWaiting)
{
while (!tasks_.empty() || !newTasks_.empty())
{