mirror of
https://github.com/matrix-construct/construct
synced 2024-11-16 15:00:51 +01:00
ircd::ctx: Support interrupt suppression; suite.
This commit is contained in:
parent
fd13e71684
commit
7e8ec1ca63
5 changed files with 112 additions and 0 deletions
|
@ -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)
|
||||
|
|
|
@ -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<void ()>); // Post function to context strand
|
||||
|
|
|
@ -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
|
||||
|
|
77
ircd/ctx.cc
77
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
|
||||
//
|
||||
|
|
|
@ -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' : '-')
|
||||
;
|
||||
|
|
Loading…
Reference in a new issue