0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-25 16:22:35 +01:00

ircd::run: Add another runlevel; eliminate main callback; simplify.

This commit is contained in:
Jason Volk 2019-10-02 16:49:30 -07:00
parent dbb3c55db5
commit e7c66d86d9
4 changed files with 118 additions and 160 deletions

View file

@ -36,7 +36,7 @@ bool write_avoid;
bool soft_assert;
bool nomatrix;
const char *execute;
std::array<bool, 6> smoketest;
std::array<bool, 7> smoketest;
lgetopt opts[]
{
@ -170,23 +170,6 @@ noexcept try
}
};
// This is the sole io_context for Construct, and the ios.run() below is the
// the only place where the program actually blocks.
boost::asio::io_context ios;
// libircd does no signal handling (or at least none that you ever have to
// care about); reaction to all signals happens out here instead. Handling
// is done properly through the io_context which registers the handler for
// the platform and then safely posts the received signal to the io_context
// event loop. This means we lose the true instant hardware-interrupt gratitude
// of signals but with the benefit of unconditional safety and cross-
// platformness with windows etc.
const construct::signals signals{ios};
// Associates libircd with our io_context and posts the initial routines
// to that io_context. Execution of IRCd will then occur during ios::run()
ircd::init(ios);
// Setup synchronization primitives on this stack for starting and stopping
// the application (matrix homeserver). Note this stack cannot actually use
// these; they'll be used to synchronize the closures below running in
@ -238,15 +221,15 @@ noexcept try
// runlevel RUN. It is called again immediately after entering runlevel
// QUIT, but before any functionality of libircd destructs. This cues us
// to start and stop the homeserver.
const decltype(ircd::run::main)::callback loader
const ircd::run::changed loader
{
ircd::run::main, [&homeserver, &start, &quit]
[&homeserver, &start, &quit](const auto &level)
{
static ircd::context context;
// 2 This branch is taken the first time this function is called,
// and not taken the second time.
if(!context)
if(level == ircd::run::level::IDLE && !context)
{
// 3 Launch the homeserver context (asynchronous).
context = { "matrix", homeserver };
@ -259,6 +242,9 @@ noexcept try
return;
}
if(level != ircd::run::level::QUIT)
return;
// 11
// Notify the waiting homeserver context to quit; this will
// start shutting down the homeserver.
@ -270,6 +256,23 @@ noexcept try
}
};
// This is the sole io_context for Construct, and the ios.run() below is the
// the only place where the program actually blocks.
boost::asio::io_context ios;
// libircd does no signal handling (or at least none that you ever have to
// care about); reaction to all signals happens out here instead. Handling
// is done properly through the io_context which registers the handler for
// the platform and then safely posts the received signal to the io_context
// event loop. This means we lose the true instant hardware-interrupt gratitude
// of signals but with the benefit of unconditional safety and cross-
// platformness with windows etc.
const construct::signals signals{ios};
// Associates libircd with our io_context and posts the initial routines
// to that io_context. Execution of IRCd will then occur during ios::run()
ircd::init(ios);
// If the user wants to immediately drop to an interactive command line
// without having to send a ctrl-c for it, that is provided here. This does
// not actually take effect until it's processed in the ios.run() below.

View file

@ -30,40 +30,31 @@ namespace ircd::run
// Access to the current runlevel indicator.
extern const enum level &level;
// Another callback list (albeit simply using util::callbacks) which is
// called during level::START on the main context after all subsystems have
// constructed, and again in level::QUIT before all subsystems have
// destructed. The purpose here is to allow extensions to the START and
// QUIT runlevels as if another subsystem was being listed in ircd::main().
// These callbacks are intended to block the main context from moving to
// the next runlevel until each one completes (run::changed doesn't quite
// work this way).
extern callbacks main;
// Access to the desired runlevel. When this differs from the current
// level, a command to change the runlevel has been given but not all
// tasks have completed at the current runlevel.
extern const enum level &chadburn;
}
/// An instance of this class registers itself to be called back when
/// the ircd::run::level has changed.
/// the ircd::run::level has changed. The context for this callback differs
/// based on the level argument; not all invocations are on an ircd::ctx, etc.
///
/// Note: Its destructor will access a list in libircd; after a callback
/// for a HALT do not unload libircd.so until destructing this object.
///
/// A static ctx::dock is also available for contexts to wait for a run::level
/// change notification.
///
struct ircd::run::changed
:instance_list<ircd::run::changed>
,std::function<void (const enum level &)>
{
using handler = std::function<void (const enum level &)>;
// Users on an ircd::ctx who wish to use the dock interface to wait for
// a run::level change can directly access this static instance.
static ctx::dock dock;
handler function;
/// The handler function will be called back for any run::level change while
/// this instance remains in scope.
changed(handler function);
~changed() noexcept;
changed(handler function) noexcept;
};
/// The run::level allows all observers to know the coarse state of IRCd and to
@ -82,6 +73,9 @@ struct ircd::run::changed
/// * START indicates the daemon is executing its startup procedures. Leaving
/// the START state occurs internally when there is success or a fatal error.
///
/// * IDLE indicates the library is ready for use. The user can load their
/// application in this mode before transitioning to RUN.
///
/// * RUN is the service mode. Full client and application functionality exists
/// in this mode. Leaving the RUN mode is done with ircd::quit();
///
@ -94,10 +88,17 @@ struct ircd::run::changed
enum class ircd::run::level
:int
{
FAULT = -1, ///< Unrecoverable fault.
HALT = 0, ///< x <-- IRCd Powered off.
READY = 1, ///< | | Ready for user to run ios event loop.
START = 2, ///< | | Starting up subsystems for service.
RUN = 3, ///< O | IRCd in service.
QUIT = 4, ///< --> ^ Clean shutdown in progress.
FAULT = -1, ///< Unrecoverable fault.
START = 2, ///< | | Starting internal subsystems.
IDLE = 3, ///< | | Ready for user to load application.
RUN = 4, ///< O | IRCd in service.
QUIT = 5, ///< --> ^ Clean shutdown in progress.
};
inline
ircd::run::changed::changed(handler function)
noexcept
:function(std::move(function))
{}

View file

@ -8,6 +8,13 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
// internal interface to ircd::run (see ircd/run.h)
namespace ircd::run
{
// change the current runlevel
static bool set(const enum level &);
}
namespace ircd
{
// Fundamental context #1; all subsystems live as objects on this stack.
@ -20,22 +27,6 @@ namespace ircd
static void main() noexcept;
}
// internal interface to ircd::run (see ircd/run.h)
namespace ircd::run
{
struct main_event;
// change the current runlevel
static bool set(const enum level &);
}
/// Placed on the main stack after all subsystems; this object represents
/// a user's extension to the main stack via callbacks (see ircd/run.h)
struct ircd::run::main_event
{
main_event(), ~main_event() noexcept;
};
decltype(ircd::version_api)
ircd::version_api
{
@ -191,11 +182,11 @@ noexcept
return true;
}
case run::level::IDLE:
case run::level::START:
{
ctx::terminate(*main_context);
main_context = nullptr;
ircd::run::set(run::level::QUIT);
return true;
}
@ -242,6 +233,7 @@ noexcept
case run::level::QUIT:
return;
case run::level::IDLE:
case run::level::RUN:
break;
}
@ -301,10 +293,6 @@ noexcept try
server::init _server_; // Server related
client::init _client_; // Client related
js::init _js_; // SpiderMonkey
run::main_event _me_; // Additional user callbacks
// IRCd will now transition to the RUN state indicating full functionality.
run::set(run::level::RUN);
// Transition to the QUIT state on unwind.
const unwind quit{[]
@ -312,13 +300,17 @@ noexcept try
ircd::run::set(run::level::QUIT);
}};
// IRCd will now transition to the IDLE state allowing library user's to
// load their applications using the run::changed callback.
run::set(run::level::IDLE);
// IRCd will now transition to the RUN state indicating full functionality.
run::set(run::level::RUN);
// This call blocks until the main context is notified or interrupted etc.
// Waiting here will hold open this stack with all of the above objects
// living on it.
run::changed::dock.wait([]
{
return !main_context;
});
ctx::wait();
}
catch(const http::error &e) // <-- m::error
{
@ -360,7 +352,7 @@ ircd::uptime()
namespace ircd::run
{
static enum level _level;
static enum level _level, _chadburn;
}
decltype(ircd::run::level)
@ -369,26 +361,11 @@ ircd::run::level
_level
};
decltype(ircd::run::main)
ircd::run::main;
//
// run::main_event
//
ircd::run::main_event::main_event()
decltype(ircd::run::chadburn)
ircd::run::chadburn
{
assert(level == level::START);
run::main();
}
ircd::run::main_event::~main_event()
noexcept
{
assert(level != level::RUN);
assert(level == level::QUIT);
run::main();
}
_chadburn
};
//
// run::changed
@ -409,23 +386,6 @@ ircd::util::instance_list<ircd::run::changed>::list
decltype(ircd::run::changed::dock)
ircd::run::changed::dock;
//
// run::changed::changed
//
ircd::run::changed::changed(handler function)
:handler
{
std::move(function)
}
{
}
ircd::run::changed::~changed()
noexcept
{
}
/// The notification will be posted to the io_context. This is important to
/// prevent the callback from continuing execution on some ircd::ctx stack and
/// instead invoke their function on the main stack in their own io_context
@ -434,72 +394,65 @@ bool
ircd::run::set(const enum level &new_level)
try
{
if(level == new_level)
if(new_level == chadburn)
return false;
log::debug
{
"IRCd level transition from '%s' to '%s' (notifying %zu)",
reflect(level),
reflect(new_level),
changed::list.size()
};
_level = new_level;
// This latch is used to block this call when setting the level
// from an ircd::ctx. If the level is set from the main stack then
// the caller will have to do synchronization themselves.
ctx::latch latch
{
bool(ctx::current) // latch has count of 1 if we're on an ircd::ctx
};
// This function will notify the user of the change to IRCd. When there
// are listeners, function is posted to the io_context ensuring THERE IS
// NO CONTINUATION ON THIS STACK by the user.
const auto call_users{[new_level, &latch, latching(!latch.is_ready())]
{
assert(new_level == run::level);
log::notice
if(!ctx::current && level != chadburn)
throw panic
{
"IRCd %s", reflect(new_level)
"Transition '%s' -> '%s' already in progress; cannot wait without ctx",
reflect(chadburn),
reflect(level),
};
log::flush();
changed::dock.notify_all();
for(const auto &handler : changed::list) try
{
(*handler)(new_level);
}
catch(const std::exception &e)
{
log::critical
{
"Runlevel change to %s handler(%p) :%s",
reflect(new_level),
handler,
e.what()
};
}
// Wait for
changed::dock.wait([]
{
return level == chadburn;
});
if(latching)
latch.count_down();
_level = new_level;
const unwind chadburn_{[]
{
_chadburn = level;
}};
static ios::descriptor descriptor
changed::dock.notify_all();
log::debug
{
"ircd::run::set"
"IRCd level transition from '%s' to '%s' (dock:%zu callbacks:%zu)",
reflect(chadburn),
reflect(level),
changed::dock.size(),
changed::list.size(),
};
if(changed::list.size() && ctx::current)
ircd::post(descriptor, call_users);
else
call_users();
log::notice
{
"IRCd %s", reflect(level)
};
if(ctx::current)
latch.wait();
log::flush();
for(const auto &handler : changed::list) try
{
handler->function(new_level);
}
catch(const std::exception &e)
{
switch(level)
{
case level::IDLE: throw;
default: break;
}
log::critical
{
"Runlevel change to %s handler(%p) :%s",
reflect(new_level),
handler,
e.what()
};
}
return true;
}
@ -524,6 +477,7 @@ ircd::run::reflect(const enum run::level &level)
case level::HALT: return "HALT";
case level::READY: return "READY";
case level::START: return "START";
case level::IDLE: return "IDLE";
case level::RUN: return "RUN";
case level::QUIT: return "QUIT";
case level::FAULT: return "FAULT";

View file

@ -190,7 +190,7 @@ void
ircd::m::on_load()
try
{
assert(ircd::run::level == run::level::START);
assert(ircd::run::level == run::level::IDLE);
//reload_conf();
_fetch = std::make_unique<fetch::init>();
//_modules = std::make_unique<init::modules>();