From 5689ac39146775a2983432498465216b0806b6f1 Mon Sep 17 00:00:00 2001 From: Hayodea Hekol Date: Tue, 9 Jun 2026 10:43:20 -0400 Subject: [PATCH] Nursery: Don't pass exceptions to main loop --- README.md | 12 ++++---- include/spinscale/co/nonViralTaskNursery.h | 32 ++++++++-------------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 31e46a3..c7fa402 100644 --- a/README.md +++ b/README.md @@ -190,11 +190,10 @@ nursery.openAdmission(); auto lease = nursery.getNewSlotLease(); lease.getSyncCanceler().startAcceptingWork(); -std::exception_ptr &exceptionStorage = lease.getExceptionStorage(); lease.setOnSettledHook( - [&exceptionStorage]() + [](std::exception_ptr &exceptionPtr) { - sscl::co::NonViralCompletion nvc(exceptionStorage); + sscl::co::NonViralCompletion nvc(exceptionPtr); nvc.checkAndRethrowException(); }); lease.fillSlot( @@ -221,10 +220,9 @@ all slots have retired naturally and throw if admission is still open. `Slot::Lease` is commit-required: an uncommitted lease removes its reservation on destruction. `fillSlot()` takes an invoker factory (deferred construction) because non-viral coroutines may complete synchronously during invoker construction. -The factory may capture `lease` by reference; `setOnSettledHook()` may not. -The hook runs after the lease is destroyed, so capture slot-owned state such as -`lease.getExceptionStorage()` (a reference to the slot's `exceptionPtr`), not -`lease` itself. +The factory may capture `lease` by reference; `setOnSettledHook()` may not +capture `lease` itself. The nursery passes the slot's `exceptionPtr` into the +hook at retirement. `Slot::Handle` is an opaque slot pointer valid only while the slot remains in the nursery. diff --git a/include/spinscale/co/nonViralTaskNursery.h b/include/spinscale/co/nonViralTaskNursery.h index 4c9de96..4501875 100644 --- a/include/spinscale/co/nonViralTaskNursery.h +++ b/include/spinscale/co/nonViralTaskNursery.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -116,7 +115,8 @@ public: sscl::SyncCancelerForAsyncWork &getSyncCanceler() { return slot.syncCanceler; } - void setOnSettledHook(std::function hook) + void setOnSettledHook( + std::function hook) { if (slot.leaseStatus != SlotLeaseStatus::RESERVED) { @@ -214,7 +214,7 @@ public: std::exception_ptr exceptionPtr = nullptr; sscl::SyncCancelerForAsyncWork syncCanceler; std::unique_ptr memberInvoker; - std::function onSettledHook; + std::function onSettledHook; }; void openAdmission() @@ -411,6 +411,8 @@ private: void retireSlot(Slot &slot) { std::function waiterToInvoke; + std::function onSettledHook; + std::exception_ptr settledExceptionPtr; { sscl::SpinLock::Guard guard(s.lock); @@ -430,33 +432,23 @@ private: return; } - try { - if (slot.onSettledHook) { slot.onSettledHook(); } - } - catch (const std::exception &e) - { - std::cerr - << "NonViralTaskNursery: onSettled hook exception: " - << e.what() << std::endl; - } - catch (...) - { - std::cerr - << "NonViralTaskNursery: onSettled hook unknown exception" - << std::endl; - } - - if (slot.exceptionPtr) { + settledExceptionPtr = slot.exceptionPtr; + if (settledExceptionPtr) { slot.settlementStatus = SlotSettlementStatus::EXCEPTION_THROWN; } else { slot.settlementStatus = SlotSettlementStatus::COMPLETED; } + onSettledHook = std::move(slot.onSettledHook); slot.leaseStatus = SlotLeaseStatus::RETIRED; slot.memberInvoker.reset(); waiterToInvoke = takeDrainWaiterIfDrainedUnlocked(); } + if (onSettledHook) { + onSettledHook(settledExceptionPtr); + } + if (waiterToInvoke) { waiterToInvoke(); } }