0
0
Fork 0
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:
Jason Volk 2018-05-06 01:00:11 -07:00
parent e80c8e6f65
commit 48055cb276
9 changed files with 88 additions and 13 deletions

View file

@ -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

View file

@ -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 &notes(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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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()

View file

@ -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 << " "