diff --git a/source/mijin/async/coroutine.hpp b/source/mijin/async/coroutine.hpp index 64a6bf8..068b3a3 100644 --- a/source/mijin/async/coroutine.hpp +++ b/source/mijin/async/coroutine.hpp @@ -455,11 +455,15 @@ public: std::function setFuture; std::any resultData; }; + + using exception_handler_t = std::function; protected: using task_vector_t = std::vector; template using wrapped_task_ptr_t = std::unique_ptr>; + + 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 inline FuturePtr addTask(TaskBase 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 @@ -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 TaskLoop::addTask(TaskBase 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()) {