mirror of
https://github.com/matrix-construct/construct
synced 2024-11-12 04:51:08 +01:00
ircd::run: Add another runlevel; eliminate main callback; simplify.
This commit is contained in:
parent
dbb3c55db5
commit
e7c66d86d9
4 changed files with 118 additions and 160 deletions
|
@ -36,7 +36,7 @@ bool write_avoid;
|
||||||
bool soft_assert;
|
bool soft_assert;
|
||||||
bool nomatrix;
|
bool nomatrix;
|
||||||
const char *execute;
|
const char *execute;
|
||||||
std::array<bool, 6> smoketest;
|
std::array<bool, 7> smoketest;
|
||||||
|
|
||||||
lgetopt opts[]
|
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
|
// Setup synchronization primitives on this stack for starting and stopping
|
||||||
// the application (matrix homeserver). Note this stack cannot actually use
|
// the application (matrix homeserver). Note this stack cannot actually use
|
||||||
// these; they'll be used to synchronize the closures below running in
|
// 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
|
// runlevel RUN. It is called again immediately after entering runlevel
|
||||||
// QUIT, but before any functionality of libircd destructs. This cues us
|
// QUIT, but before any functionality of libircd destructs. This cues us
|
||||||
// to start and stop the homeserver.
|
// 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;
|
static ircd::context context;
|
||||||
|
|
||||||
// 2 This branch is taken the first time this function is called,
|
// 2 This branch is taken the first time this function is called,
|
||||||
// and not taken the second time.
|
// and not taken the second time.
|
||||||
if(!context)
|
if(level == ircd::run::level::IDLE && !context)
|
||||||
{
|
{
|
||||||
// 3 Launch the homeserver context (asynchronous).
|
// 3 Launch the homeserver context (asynchronous).
|
||||||
context = { "matrix", homeserver };
|
context = { "matrix", homeserver };
|
||||||
|
@ -259,6 +242,9 @@ noexcept try
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(level != ircd::run::level::QUIT)
|
||||||
|
return;
|
||||||
|
|
||||||
// 11
|
// 11
|
||||||
// Notify the waiting homeserver context to quit; this will
|
// Notify the waiting homeserver context to quit; this will
|
||||||
// start shutting down the homeserver.
|
// 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
|
// 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
|
// 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.
|
// not actually take effect until it's processed in the ios.run() below.
|
||||||
|
|
|
@ -30,40 +30,31 @@ namespace ircd::run
|
||||||
// Access to the current runlevel indicator.
|
// Access to the current runlevel indicator.
|
||||||
extern const enum level &level;
|
extern const enum level &level;
|
||||||
|
|
||||||
// Another callback list (albeit simply using util::callbacks) which is
|
// Access to the desired runlevel. When this differs from the current
|
||||||
// called during level::START on the main context after all subsystems have
|
// level, a command to change the runlevel has been given but not all
|
||||||
// constructed, and again in level::QUIT before all subsystems have
|
// tasks have completed at the current runlevel.
|
||||||
// destructed. The purpose here is to allow extensions to the START and
|
extern const enum level &chadburn;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An instance of this class registers itself to be called back when
|
/// 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
|
/// Note: Its destructor will access a list in libircd; after a callback
|
||||||
/// for a HALT do not unload libircd.so until destructing this object.
|
/// 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
|
struct ircd::run::changed
|
||||||
:instance_list<ircd::run::changed>
|
:instance_list<ircd::run::changed>
|
||||||
,std::function<void (const enum level &)>
|
|
||||||
{
|
{
|
||||||
using handler = 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;
|
static ctx::dock dock;
|
||||||
|
|
||||||
|
handler function;
|
||||||
|
|
||||||
/// The handler function will be called back for any run::level change while
|
/// The handler function will be called back for any run::level change while
|
||||||
/// this instance remains in scope.
|
/// this instance remains in scope.
|
||||||
changed(handler function);
|
changed(handler function) noexcept;
|
||||||
~changed() noexcept;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The run::level allows all observers to know the coarse state of IRCd and to
|
/// 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
|
/// * START indicates the daemon is executing its startup procedures. Leaving
|
||||||
/// the START state occurs internally when there is success or a fatal error.
|
/// 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
|
/// * RUN is the service mode. Full client and application functionality exists
|
||||||
/// in this mode. Leaving the RUN mode is done with ircd::quit();
|
/// 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
|
enum class ircd::run::level
|
||||||
:int
|
:int
|
||||||
{
|
{
|
||||||
|
FAULT = -1, ///< Unrecoverable fault.
|
||||||
HALT = 0, ///< x <-- IRCd Powered off.
|
HALT = 0, ///< x <-- IRCd Powered off.
|
||||||
READY = 1, ///< | | Ready for user to run ios event loop.
|
READY = 1, ///< | | Ready for user to run ios event loop.
|
||||||
START = 2, ///< | | Starting up subsystems for service.
|
START = 2, ///< | | Starting internal subsystems.
|
||||||
RUN = 3, ///< O | IRCd in service.
|
IDLE = 3, ///< | | Ready for user to load application.
|
||||||
QUIT = 4, ///< --> ^ Clean shutdown in progress.
|
RUN = 4, ///< O | IRCd in service.
|
||||||
FAULT = -1, ///< Unrecoverable fault.
|
QUIT = 5, ///< --> ^ Clean shutdown in progress.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
ircd::run::changed::changed(handler function)
|
||||||
|
noexcept
|
||||||
|
:function(std::move(function))
|
||||||
|
{}
|
||||||
|
|
154
ircd/ircd.cc
154
ircd/ircd.cc
|
@ -8,6 +8,13 @@
|
||||||
// copyright notice and this permission notice is present in all copies. The
|
// copyright notice and this permission notice is present in all copies. The
|
||||||
// full license for this software is available in the LICENSE file.
|
// 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
|
namespace ircd
|
||||||
{
|
{
|
||||||
// Fundamental context #1; all subsystems live as objects on this stack.
|
// Fundamental context #1; all subsystems live as objects on this stack.
|
||||||
|
@ -20,22 +27,6 @@ namespace ircd
|
||||||
static void main() noexcept;
|
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)
|
decltype(ircd::version_api)
|
||||||
ircd::version_api
|
ircd::version_api
|
||||||
{
|
{
|
||||||
|
@ -191,11 +182,11 @@ noexcept
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case run::level::IDLE:
|
||||||
case run::level::START:
|
case run::level::START:
|
||||||
{
|
{
|
||||||
ctx::terminate(*main_context);
|
ctx::terminate(*main_context);
|
||||||
main_context = nullptr;
|
main_context = nullptr;
|
||||||
ircd::run::set(run::level::QUIT);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,6 +233,7 @@ noexcept
|
||||||
case run::level::QUIT:
|
case run::level::QUIT:
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case run::level::IDLE:
|
||||||
case run::level::RUN:
|
case run::level::RUN:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -301,10 +293,6 @@ noexcept try
|
||||||
server::init _server_; // Server related
|
server::init _server_; // Server related
|
||||||
client::init _client_; // Client related
|
client::init _client_; // Client related
|
||||||
js::init _js_; // SpiderMonkey
|
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.
|
// Transition to the QUIT state on unwind.
|
||||||
const unwind quit{[]
|
const unwind quit{[]
|
||||||
|
@ -312,13 +300,17 @@ noexcept try
|
||||||
ircd::run::set(run::level::QUIT);
|
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.
|
// 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
|
// Waiting here will hold open this stack with all of the above objects
|
||||||
// living on it.
|
// living on it.
|
||||||
run::changed::dock.wait([]
|
ctx::wait();
|
||||||
{
|
|
||||||
return !main_context;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch(const http::error &e) // <-- m::error
|
catch(const http::error &e) // <-- m::error
|
||||||
{
|
{
|
||||||
|
@ -360,7 +352,7 @@ ircd::uptime()
|
||||||
|
|
||||||
namespace ircd::run
|
namespace ircd::run
|
||||||
{
|
{
|
||||||
static enum level _level;
|
static enum level _level, _chadburn;
|
||||||
}
|
}
|
||||||
|
|
||||||
decltype(ircd::run::level)
|
decltype(ircd::run::level)
|
||||||
|
@ -369,26 +361,11 @@ ircd::run::level
|
||||||
_level
|
_level
|
||||||
};
|
};
|
||||||
|
|
||||||
decltype(ircd::run::main)
|
decltype(ircd::run::chadburn)
|
||||||
ircd::run::main;
|
ircd::run::chadburn
|
||||||
|
|
||||||
//
|
|
||||||
// run::main_event
|
|
||||||
//
|
|
||||||
|
|
||||||
ircd::run::main_event::main_event()
|
|
||||||
{
|
{
|
||||||
assert(level == level::START);
|
_chadburn
|
||||||
run::main();
|
};
|
||||||
}
|
|
||||||
|
|
||||||
ircd::run::main_event::~main_event()
|
|
||||||
noexcept
|
|
||||||
{
|
|
||||||
assert(level != level::RUN);
|
|
||||||
assert(level == level::QUIT);
|
|
||||||
run::main();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// run::changed
|
// run::changed
|
||||||
|
@ -409,23 +386,6 @@ ircd::util::instance_list<ircd::run::changed>::list
|
||||||
decltype(ircd::run::changed::dock)
|
decltype(ircd::run::changed::dock)
|
||||||
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
|
/// 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
|
/// 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
|
/// instead invoke their function on the main stack in their own io_context
|
||||||
|
@ -434,47 +394,57 @@ bool
|
||||||
ircd::run::set(const enum level &new_level)
|
ircd::run::set(const enum level &new_level)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(level == new_level)
|
if(new_level == chadburn)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
log::debug
|
if(!ctx::current && level != chadburn)
|
||||||
|
throw panic
|
||||||
{
|
{
|
||||||
"IRCd level transition from '%s' to '%s' (notifying %zu)",
|
"Transition '%s' -> '%s' already in progress; cannot wait without ctx",
|
||||||
|
reflect(chadburn),
|
||||||
reflect(level),
|
reflect(level),
|
||||||
reflect(new_level),
|
|
||||||
changed::list.size()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Wait for
|
||||||
|
changed::dock.wait([]
|
||||||
|
{
|
||||||
|
return level == chadburn;
|
||||||
|
});
|
||||||
|
|
||||||
_level = new_level;
|
_level = new_level;
|
||||||
|
const unwind chadburn_{[]
|
||||||
// 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
|
_chadburn = level;
|
||||||
|
}};
|
||||||
|
|
||||||
|
changed::dock.notify_all();
|
||||||
|
log::debug
|
||||||
|
{
|
||||||
|
"IRCd level transition from '%s' to '%s' (dock:%zu callbacks:%zu)",
|
||||||
|
reflect(chadburn),
|
||||||
|
reflect(level),
|
||||||
|
changed::dock.size(),
|
||||||
|
changed::list.size(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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
|
log::notice
|
||||||
{
|
{
|
||||||
"IRCd %s", reflect(new_level)
|
"IRCd %s", reflect(level)
|
||||||
};
|
};
|
||||||
|
|
||||||
log::flush();
|
log::flush();
|
||||||
changed::dock.notify_all();
|
|
||||||
for(const auto &handler : changed::list) try
|
for(const auto &handler : changed::list) try
|
||||||
{
|
{
|
||||||
(*handler)(new_level);
|
handler->function(new_level);
|
||||||
}
|
}
|
||||||
catch(const std::exception &e)
|
catch(const std::exception &e)
|
||||||
{
|
{
|
||||||
|
switch(level)
|
||||||
|
{
|
||||||
|
case level::IDLE: throw;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
log::critical
|
log::critical
|
||||||
{
|
{
|
||||||
"Runlevel change to %s handler(%p) :%s",
|
"Runlevel change to %s handler(%p) :%s",
|
||||||
|
@ -484,23 +454,6 @@ try
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if(latching)
|
|
||||||
latch.count_down();
|
|
||||||
}};
|
|
||||||
|
|
||||||
static ios::descriptor descriptor
|
|
||||||
{
|
|
||||||
"ircd::run::set"
|
|
||||||
};
|
|
||||||
|
|
||||||
if(changed::list.size() && ctx::current)
|
|
||||||
ircd::post(descriptor, call_users);
|
|
||||||
else
|
|
||||||
call_users();
|
|
||||||
|
|
||||||
if(ctx::current)
|
|
||||||
latch.wait();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch(const std::exception &e)
|
catch(const std::exception &e)
|
||||||
|
@ -524,6 +477,7 @@ ircd::run::reflect(const enum run::level &level)
|
||||||
case level::HALT: return "HALT";
|
case level::HALT: return "HALT";
|
||||||
case level::READY: return "READY";
|
case level::READY: return "READY";
|
||||||
case level::START: return "START";
|
case level::START: return "START";
|
||||||
|
case level::IDLE: return "IDLE";
|
||||||
case level::RUN: return "RUN";
|
case level::RUN: return "RUN";
|
||||||
case level::QUIT: return "QUIT";
|
case level::QUIT: return "QUIT";
|
||||||
case level::FAULT: return "FAULT";
|
case level::FAULT: return "FAULT";
|
||||||
|
|
|
@ -190,7 +190,7 @@ void
|
||||||
ircd::m::on_load()
|
ircd::m::on_load()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
assert(ircd::run::level == run::level::START);
|
assert(ircd::run::level == run::level::IDLE);
|
||||||
//reload_conf();
|
//reload_conf();
|
||||||
_fetch = std::make_unique<fetch::init>();
|
_fetch = std::make_unique<fetch::init>();
|
||||||
//_modules = std::make_unique<init::modules>();
|
//_modules = std::make_unique<init::modules>();
|
||||||
|
|
Loading…
Reference in a new issue