mirror of
https://github.com/matrix-construct/construct
synced 2024-11-29 02:02:38 +01:00
ircd::ctx: Add special termination interruption to yanagiba the stack.
This commit is contained in:
parent
e80c8e6f65
commit
48055cb276
9 changed files with 88 additions and 13 deletions
|
@ -59,6 +59,7 @@ struct ircd::ctx::context
|
|||
operator bool() const { return bool(c); }
|
||||
bool joined() const { return !c || ircd::ctx::finished(*c); }
|
||||
|
||||
void terminate() { ircd::ctx::terminate(*c); }
|
||||
void interrupt() { ircd::ctx::interrupt(*c); }
|
||||
void join(); // Blocks the current context until this one finishes
|
||||
ctx *detach(); // other calls undefined after this call
|
||||
|
@ -103,6 +104,7 @@ enum ircd::ctx::context::flags
|
|||
DETACH = 0x04, ///< Context deletes itself; see struct context constructor notes
|
||||
|
||||
INTERRUPTED = 0x10, ///< (INDICATOR) Marked
|
||||
TERMINATED = 0x20, ///< (INDICATOR)
|
||||
};
|
||||
|
||||
inline void
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace ircd::ctx
|
|||
IRCD_EXCEPTION(ircd::error, error)
|
||||
IRCD_EXCEPTION(error, interrupted)
|
||||
IRCD_EXCEPTION(error, timeout)
|
||||
struct terminated {}; // Special exception
|
||||
|
||||
IRCD_OVERLOAD(threadsafe)
|
||||
|
||||
|
@ -54,12 +55,14 @@ namespace ircd::ctx
|
|||
const int64_t ¬es(const ctx &); // Peeks at internal semaphore count
|
||||
const ulong &cycles(const ctx &); // Accumulated tsc (not counting cur slice)
|
||||
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).
|
||||
bool started(const ctx &); // Context was ever entered.
|
||||
bool running(const ctx &); // Context is the currently running ctx.
|
||||
bool waiting(const ctx &); // started() && !finished() && !running()
|
||||
|
||||
void interrupt(ctx &); // Interrupt the context for termination.
|
||||
void interrupt(ctx &); // Interrupt the context.
|
||||
void terminate(ctx &); // Interrupt for termination.
|
||||
void signal(ctx &, std::function<void ()>); // Post function to context strand
|
||||
void notify(ctx &, threadsafe_t); // Notify context with threadsafety.
|
||||
bool notify(ctx &); // Queue a context switch to arg
|
||||
|
|
|
@ -49,6 +49,7 @@ enum class ircd::ctx::prof::event
|
|||
CUR_YIELD, // Current context yielding
|
||||
CUR_CONTINUE, // Current context continuing
|
||||
CUR_INTERRUPT, // Current context detects interruption
|
||||
CUR_TERMINATE, // Current context detects termination
|
||||
};
|
||||
|
||||
struct ircd::ctx::prof::settings
|
||||
|
|
|
@ -26,7 +26,7 @@ inline namespace this_ctx
|
|||
void wait(); // Returns when context is woken up.
|
||||
void yield(); // Allow other contexts to run before returning.
|
||||
|
||||
void interruption_point(); // throws interrupted if interruption_requested()
|
||||
void interruption_point(); // throws if interruption_requested()
|
||||
bool interruption_requested(); // interruption(cur())
|
||||
|
||||
struct stack_usage_assertion; // Assert safety factor (see ctx/prof.h)
|
||||
|
|
|
@ -64,18 +64,18 @@ namespace ircd
|
|||
/// To catch any exception from a project developer's code:
|
||||
/// catch(const ircd::exception &) {}
|
||||
///
|
||||
/// Remember: not all exceptions are from project developer's code,
|
||||
/// such as std::out_of_range. In most contexts if you have to deal with this
|
||||
/// someone else was lazy, which is bad. To be sure and catch any exception:
|
||||
/// Remember: not all exceptions are from project developer's code, such as
|
||||
/// std::out_of_range. In most contexts if you have to deal with this someone
|
||||
/// else was lazy, which is bad. To be sure and catch any exception:
|
||||
/// catch(const std::exception &) {}
|
||||
///
|
||||
/// Remember: not all exceptions have to inherit from std::exception, but
|
||||
/// those are rogue exceptions. We do not allow this. To be sure nothing
|
||||
/// can possibly get through, add to the bottom:
|
||||
/// Remember: not all exceptions have to inherit from std::exception. We have
|
||||
/// only one example of this: ctx::terminated. To be sure nothing can possibly
|
||||
/// get through, add to the bottom, with care:
|
||||
/// catch(...) {}
|
||||
///
|
||||
/// Note: Prefer 'noexcept' instead of catch(...), noexcept is like an 'assert'
|
||||
/// for exceptions, and the rogue can be found-out in testing.
|
||||
/// for exceptions, and a rogue can be found-out in testing.
|
||||
///
|
||||
struct ircd::exception
|
||||
:std::exception
|
||||
|
|
64
ircd/ctx.cc
64
ircd/ctx.cc
|
@ -64,7 +64,7 @@ noexcept try
|
|||
});
|
||||
|
||||
// Check for a precocious interrupt
|
||||
if(unlikely(flags & context::INTERRUPTED))
|
||||
if(unlikely(flags & (context::INTERRUPTED | context::TERMINATED)))
|
||||
return;
|
||||
|
||||
if(likely(bool(func)))
|
||||
|
@ -88,6 +88,10 @@ catch(const std::exception &e)
|
|||
// handling the exception here with a log message and calling it a day.
|
||||
return;
|
||||
}
|
||||
catch(const terminated &)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/// Direct context switch to this context.
|
||||
///
|
||||
|
@ -185,8 +189,35 @@ catch(const boost::system::system_error &e)
|
|||
void
|
||||
ircd::ctx::ctx::interruption_point()
|
||||
{
|
||||
static const auto &flags
|
||||
{
|
||||
context::TERMINATED | context::INTERRUPTED
|
||||
};
|
||||
|
||||
if(likely((this->flags & flags) == 0))
|
||||
return;
|
||||
|
||||
if(unlikely(termination_point(std::nothrow)))
|
||||
throw terminated{};
|
||||
|
||||
if(unlikely(interruption_point(std::nothrow)))
|
||||
throw interrupted("ctx(%p) '%s'", (const void *)this, name);
|
||||
throw interrupted
|
||||
{
|
||||
"ctx(%p) '%s'", (const void *)this, name
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true if this context has been flagged for termination.
|
||||
/// Does not clear the flag.
|
||||
bool
|
||||
ircd::ctx::ctx::termination_point(std::nothrow_t)
|
||||
{
|
||||
if(unlikely(flags & context::TERMINATED))
|
||||
{
|
||||
mark(prof::event::CUR_TERMINATE);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
/// Returns true if this context has been flagged for interruption and
|
||||
|
@ -261,6 +292,23 @@ ircd::ctx::signal(ctx &ctx,
|
|||
ctx.strand.post(std::move(func));
|
||||
}
|
||||
|
||||
/// Marks `ctx` for termination. Terminate is similar to interrupt() but the
|
||||
/// exception thrown is ctx::terminate which does not participate in the
|
||||
/// std::exception hierarchy. Project code is unlikely to catch this.
|
||||
void
|
||||
ircd::ctx::terminate(ctx &ctx)
|
||||
{
|
||||
if(finished(ctx))
|
||||
return;
|
||||
|
||||
if(termination(ctx))
|
||||
return;
|
||||
|
||||
ctx.flags |= context::TERMINATED;
|
||||
if(likely(&ctx != current && ctx.cont != nullptr))
|
||||
ctx.cont->interrupted(current);
|
||||
}
|
||||
|
||||
/// Marks `ctx` for interruption and enqueues it for resumption to receive the
|
||||
/// interrupt which will be an exception coming out of the point where the
|
||||
/// `ctx` was yielding.
|
||||
|
@ -270,6 +318,9 @@ ircd::ctx::interrupt(ctx &ctx)
|
|||
if(finished(ctx))
|
||||
return;
|
||||
|
||||
if(unlikely(ircd::runlevel == runlevel::QUIT))
|
||||
return terminate(ctx);
|
||||
|
||||
if(interruption(ctx))
|
||||
return;
|
||||
|
||||
|
@ -306,6 +357,13 @@ ircd::ctx::finished(const ctx &ctx)
|
|||
return ctx.finished();
|
||||
}
|
||||
|
||||
/// Indicates if `ctx` was terminated; does not clear the flag
|
||||
bool
|
||||
ircd::ctx::termination(const ctx &c)
|
||||
{
|
||||
return c.flags & context::TERMINATED;
|
||||
}
|
||||
|
||||
/// Indicates if `ctx` was interrupted; does not clear the flag
|
||||
bool
|
||||
ircd::ctx::interruption(const ctx &c)
|
||||
|
@ -466,7 +524,7 @@ ircd::ctx::this_ctx::interruption_point()
|
|||
bool
|
||||
ircd::ctx::this_ctx::interruption_requested()
|
||||
{
|
||||
return interruption(cur());
|
||||
return interruption(cur()) || termination(cur());
|
||||
}
|
||||
|
||||
/// Returns unique ID of currently running context
|
||||
|
|
|
@ -36,7 +36,8 @@ struct ircd::ctx::ctx
|
|||
bool finished() const { return started() && yc == nullptr; }
|
||||
|
||||
bool interruption_point(std::nothrow_t); // Check for interrupt (and clear flag)
|
||||
void interruption_point(); // throws interrupted
|
||||
bool termination_point(std::nothrow_t); // Check for terminate
|
||||
void interruption_point(); // throws interrupted or terminated
|
||||
|
||||
bool wait(); // yield context to ios queue (returns on this resume)
|
||||
void jump(); // jump to context directly (returns on your resume)
|
||||
|
|
|
@ -249,6 +249,15 @@ catch(const std::exception &e)
|
|||
"IRCd main exited: %s", e.what()
|
||||
};
|
||||
}
|
||||
catch(const ctx::terminated &)
|
||||
{
|
||||
log::warning
|
||||
{
|
||||
"IRCd main terminated..."
|
||||
};
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
void
|
||||
ircd::at_main_exit()
|
||||
|
|
|
@ -764,6 +764,7 @@ console_cmd__ctx__list(opt &out, const string_view &line)
|
|||
<< (waiting(ctx)? 'W' : '-')
|
||||
<< (finished(ctx)? 'F' : '-')
|
||||
<< (interruption(ctx)? 'I' : '-')
|
||||
<< (termination(ctx)? 'T' : '-')
|
||||
;
|
||||
|
||||
out << " "
|
||||
|
|
Loading…
Reference in a new issue