diff --git a/include/ircd/ctx/context.h b/include/ircd/ctx/context.h index 9fda18184..6410e82d5 100644 --- a/include/ircd/ctx/context.h +++ b/include/ircd/ctx/context.h @@ -102,6 +102,7 @@ enum ircd::ctx::context::flags POST = 0x01, ///< Defers spawn with an ios.post() DISPATCH = 0x02, ///< Defers spawn with an ios.dispatch() (possibly) DETACH = 0x04, ///< Context deletes itself; see struct context constructor notes + NOINTERRUPT = 0x08, ///< Interruption points won't throw while lit. INTERRUPTED = 0x10, ///< (INDICATOR) Marked TERMINATED = 0x20, ///< (INDICATOR) diff --git a/include/ircd/ctx/ctx.h b/include/ircd/ctx/ctx.h index 7b501d452..105b6fcfb 100644 --- a/include/ircd/ctx/ctx.h +++ b/include/ircd/ctx/ctx.h @@ -55,6 +55,7 @@ namespace ircd::ctx const int64_t ¬es(const ctx &); // Peeks at internal semaphore count const uint64_t &yields(const ctx &); // Context switching counter const ulong &cycles(const ctx &); // Accumulated tsc (not counting cur slice) + bool interruptible(const ctx &); // Context can throw at interruption point bool interruption(const ctx &); // Context was marked for interruption bool termination(const ctx &); // Context was marked for termination bool finished(const ctx &); // Context function returned (or exception). @@ -62,6 +63,7 @@ namespace ircd::ctx bool running(const ctx &); // Context is the currently running ctx. bool waiting(const ctx &); // started() && !finished() && !running() + void interruptible(ctx &, const bool &); // False for interrupt suppression. void interrupt(ctx &); // Interrupt the context. void terminate(ctx &); // Interrupt for termination. void signal(ctx &, std::function); // Post function to context strand diff --git a/include/ircd/ctx/this_ctx.h b/include/ircd/ctx/this_ctx.h index 72ce6ca72..a007c3eca 100644 --- a/include/ircd/ctx/this_ctx.h +++ b/include/ircd/ctx/this_ctx.h @@ -18,6 +18,7 @@ inline namespace this_ctx struct critical_indicator; // Indicates if yielding happened for a section struct critical_assertion; // Assert no yielding for a section struct exception_handler; // Must be present to yield in a handler + struct uninterruptible; // Scope convenience for interruptible() struct ctx &cur(); ///< Assumptional reference to *current @@ -29,6 +30,8 @@ inline namespace this_ctx void interruption_point(); // throws if interruption_requested() bool interruption_requested(); // interruption(cur()) + void interruptible(const bool &); // interruptible(cur(), bool) +INTERRUPTION POINT + void interruptible(const bool &, std::nothrow_t) noexcept; struct stack_usage_assertion; // Assert safety factor (see ctx/prof.h) size_t stack_at_here() __attribute__((noinline)); @@ -134,6 +137,34 @@ struct ircd::ctx::this_ctx::exception_handler exception_handler &operator=(const exception_handler &) = delete; }; +/// An instance of uninterruptible will suppress interrupts sent to the +/// context for the scope. Suppression does not discard any interrupt, +/// it merely ignores it at all interruption points until the suppression +/// ends, after which it will be thrown. +/// +struct ircd::ctx::this_ctx::uninterruptible +{ + struct nothrow; + + uninterruptible(); + uninterruptible(uninterruptible &&) = delete; + uninterruptible(const uninterruptible &) = delete; + ~uninterruptible() noexcept(false); +}; + +/// A variant of uinterruptible for users that must guarantee the ending of +/// the suppression scope will not be an interruption point. The default +/// behavior for uninterruptible is to throw, even from its destructor, to +/// fulfill the interruption request without any more delay. +/// +struct ircd::ctx::this_ctx::uninterruptible::nothrow +{ + nothrow() noexcept; + nothrow(nothrow &&) = delete; + nothrow(const nothrow &) = delete; + ~nothrow() noexcept; +}; + /// This overload matches ::sleep() and acts as a drop-in for ircd contexts. /// interruption point. inline void diff --git a/ircd/ctx.cc b/ircd/ctx.cc index 767b7091c..e015399de 100644 --- a/ircd/ctx.cc +++ b/ircd/ctx.cc @@ -232,6 +232,12 @@ ircd::ctx::ctx::interruption_point(std::nothrow_t) // so please eat this branch misprediction. if(unlikely(flags & context::INTERRUPTED)) { + // The NOINTERRUPT flag works by pretending there is no INTERRUPTED + // flag set and also does not clear the flag. This allows the interrupt + // to remaing pending until the uninterruptible section is complete. + if(flags & context::NOINTERRUPT) + return false; + mark(prof::event::CUR_INTERRUPT); flags &= ~context::INTERRUPTED; return true; @@ -332,6 +338,20 @@ ircd::ctx::interrupt(ctx &ctx) ctx.cont->interrupted(current); } +/// Marks `ctx` for whether to allow or suppress interruption. Suppression +/// does not ignore an interrupt itself, it only ignores the interruption +/// points. Thus when a suppression ends if the interrupt flag was ever set +/// the next interruption point will throw as expected. +void +ircd::ctx::interruptible(ctx &ctx, + const bool &b) +{ + if(b) + ctx.flags &= ~context::NOINTERRUPT; + else + ctx.flags |= context::NOINTERRUPT; +} + /// started() && !finished() && !running bool ircd::ctx::waiting(const ctx &ctx) @@ -374,6 +394,13 @@ ircd::ctx::interruption(const ctx &c) return c.flags & context::INTERRUPTED; } +/// Indicates if `ctx` will suppress any interrupts. +bool +ircd::ctx::interruptible(const ctx &c) +{ + return c.flags & ~context::NOINTERRUPT; +} + /// Returns the cycle count for `ctx` const ulong & ircd::ctx::cycles(const ctx &ctx) @@ -521,6 +548,25 @@ ircd::ctx::this_ctx::stack_at_here() return cur().stack_base - uintptr_t(__builtin_frame_address(0)); } +/// Throws interrupted if the currently running context was interrupted +/// and clears the interrupt flag. +void +ircd::ctx::this_ctx::interruptible(const bool &b) +{ + interruptible(b, std::nothrow); + interruption_point(); +} + +/// Throws interrupted if the currently running context was interrupted +/// and clears the interrupt flag. +void +ircd::ctx::this_ctx::interruptible(const bool &b, + std::nothrow_t) +noexcept +{ + interruptible(cur(), b); +} + /// Throws interrupted if the currently running context was interrupted /// and clears the interrupt flag. void @@ -553,6 +599,37 @@ ircd::ctx::this_ctx::name() return current? name(cur()) : nada; } +// +// uinterruptible +// + +ircd::ctx::this_ctx::uninterruptible::uninterruptible() +{ + interruptible(false); +} + +ircd::ctx::this_ctx::uninterruptible::~uninterruptible() +noexcept(false) +{ + interruptible(true); +} + +// +// uninterruptible::nothrow +// + +ircd::ctx::this_ctx::uninterruptible::nothrow::nothrow() +noexcept +{ + interruptible(false, std::nothrow); +} + +ircd::ctx::this_ctx::uninterruptible::nothrow::~nothrow() +noexcept +{ + interruptible(true, std::nothrow); +} + // // exception_handler // diff --git a/modules/console.cc b/modules/console.cc index 86584bd87..b9b7f02f0 100644 --- a/modules/console.cc +++ b/modules/console.cc @@ -826,6 +826,7 @@ console_cmd__ctx__list(opt &out, const string_view &line) << (running(ctx)? 'R' : '-') << (waiting(ctx)? 'W' : '-') << (finished(ctx)? 'F' : '-') + << (interruptible(ctx)? '-' : 'N') << (interruption(ctx)? 'I' : '-') << (termination(ctx)? 'T' : '-') ;