mirror of
https://github.com/matrix-construct/construct
synced 2024-05-19 19:33:45 +02:00
ircd: Simplify the async main init fiasco w/ continuation callback.
This commit is contained in:
parent
2e7ede7242
commit
9655a6311f
|
@ -39,7 +39,7 @@ bool norun;
|
||||||
bool read_only;
|
bool read_only;
|
||||||
bool write_avoid;
|
bool write_avoid;
|
||||||
bool slave;
|
bool slave;
|
||||||
std::array<bool, 8> smoketest;
|
std::array<bool, 6> smoketest;
|
||||||
bool soft_assert;
|
bool soft_assert;
|
||||||
bool nomatrix;
|
bool nomatrix;
|
||||||
bool matrix {true}; // matrix server by default.
|
bool matrix {true}; // matrix server by default.
|
||||||
|
@ -202,25 +202,19 @@ noexcept try
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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
|
|
||||||
// different contexts.
|
|
||||||
ircd::ctx::latch start(2), quit(2);
|
|
||||||
std::exception_ptr eptr;
|
|
||||||
|
|
||||||
// Setup the matrix homeserver application. This will be executed on an
|
// Setup the matrix homeserver application. This will be executed on an
|
||||||
// ircd::context (dedicated stack). We construct several objects on the
|
// ircd::context (dedicated stack). We construct several objects on the
|
||||||
// stack which are the basis for our matrix homeserver. When the stack
|
// stack which are the basis for our matrix homeserver. When the stack
|
||||||
// unwinds, the homeserver will go out of service.
|
// unwinds, the homeserver will go out of service.
|
||||||
const auto homeserver{[&origin, &server_name, &start, &quit, &eptr]
|
const auto homeserver{[&origin, &server_name]
|
||||||
|
(const ircd::main_continuation &run)
|
||||||
{
|
{
|
||||||
try
|
assert(ircd::run::level == ircd::run::level::START);
|
||||||
{
|
|
||||||
// 5 Load the matrix library dynamic shared object
|
|
||||||
ircd::matrix matrix;
|
|
||||||
|
|
||||||
// 6 Run a primary homeserver based on the program options given.
|
// Load the matrix library dynamic shared object
|
||||||
|
ircd::matrix matrix; try
|
||||||
|
{
|
||||||
|
// Setup a primary homeserver based on the program options given.
|
||||||
struct ircd::m::homeserver::opts opts;
|
struct ircd::m::homeserver::opts opts;
|
||||||
opts.origin = origin;
|
opts.origin = origin;
|
||||||
opts.server_name = server_name;
|
opts.server_name = server_name;
|
||||||
|
@ -229,86 +223,29 @@ noexcept try
|
||||||
opts.autoapps = !noautoapps;
|
opts.autoapps = !noautoapps;
|
||||||
const ircd::custom_ptr<ircd::m::homeserver> homeserver
|
const ircd::custom_ptr<ircd::m::homeserver> homeserver
|
||||||
{
|
{
|
||||||
// 6.1
|
|
||||||
matrix.init(&opts), [&matrix]
|
matrix.init(&opts), [&matrix]
|
||||||
(ircd::m::homeserver *const homeserver)
|
(ircd::m::homeserver *const homeserver)
|
||||||
{
|
{
|
||||||
// 11.1
|
|
||||||
matrix.fini(homeserver);
|
matrix.fini(homeserver);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 7 Notify the loader the homeserver is ready for service.
|
// Run the homeserver (call main()'s continuation). This function
|
||||||
start.count_down_and_wait();
|
// returns (or potentially throws) to unload/quit.
|
||||||
|
run();
|
||||||
// 9 Yield until the loader notifies us; this stack will then unwind.
|
|
||||||
quit.count_down_and_wait();
|
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(const std::exception &e)
|
||||||
{
|
{
|
||||||
// ctx::exception_handler allows us to yield an ircd::ctx (stack
|
// Rethrow as ircd::error because we're about to unload the module
|
||||||
// switch) inside a catch block, which we'll need in this scope.
|
// and all m:: type exceptions won't exist anymore...
|
||||||
const ircd::ctx::exception_handler eh;
|
throw ircd::error
|
||||||
|
|
||||||
// Copy a reference to the current exception which was saved by eh.
|
|
||||||
// Note it is not safe to call std::current_exception() here.
|
|
||||||
eptr = eh;
|
|
||||||
|
|
||||||
// 7' Notify the loader the homeserver failed to start
|
|
||||||
start.count_down_and_wait();
|
|
||||||
|
|
||||||
// 9' Yield until the loader notifies us; this stack will then unwind.
|
|
||||||
quit.count_down_and_wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 11
|
|
||||||
}};
|
|
||||||
|
|
||||||
// This object registers a callback for a specific event in libircd; the
|
|
||||||
// closure is called from the main context (#1) running ircd::main().
|
|
||||||
// after libircd is ready for service in runlevel LOAD but before entering
|
|
||||||
// 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 ircd::run::changed loader
|
|
||||||
{
|
|
||||||
[&homeserver, &start, &quit, &eptr](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(level == ircd::run::level::LOAD && !context && !nomatrix)
|
|
||||||
{
|
{
|
||||||
using namespace ircd::util;
|
"%s", e.what()
|
||||||
|
};
|
||||||
// 3 Launch the homeserver context (asynchronous).
|
|
||||||
context = { "matrix", 1_MiB, ircd::context::POST, homeserver };
|
|
||||||
|
|
||||||
// 4 Yield until the homeserver function notifies `start`; waiting
|
|
||||||
// here prevents ircd::main() from entering runlevel RUN.
|
|
||||||
start.count_down_and_wait();
|
|
||||||
|
|
||||||
// 8 Check if error on start; rethrowing that here propagates
|
|
||||||
// to ircd::main() and triggers a runlevel QUIT sequence.
|
|
||||||
if(!!eptr)
|
|
||||||
std::rethrow_exception(eptr);
|
|
||||||
|
|
||||||
// 8.1
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(level != ircd::run::level::UNLOAD || !context)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// 10 Notify the waiting homeserver context to quit; this will
|
|
||||||
// start shutting down the homeserver.
|
|
||||||
quit.count_down_and_wait();
|
|
||||||
|
|
||||||
// 12 Wait for the homeserver context to finish before we return.
|
|
||||||
context.join();
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
assert(ircd::run::level == ircd::run::level::QUIT);
|
||||||
|
}};
|
||||||
|
|
||||||
// This is the sole io_context for Construct, and the ios.run() below is the
|
// This is the sole io_context for Construct, and the ios.run() below is the
|
||||||
// the only place where the program actually blocks.
|
// the only place where the program actually blocks.
|
||||||
|
@ -325,7 +262,7 @@ noexcept try
|
||||||
|
|
||||||
// Associates libircd with our io_context and posts the initial routines
|
// Associates libircd with our io_context and posts the initial routines
|
||||||
// to that io_context. Execution of IRCd will then occur during ios::run()
|
// to that io_context. Execution of IRCd will then occur during ios::run()
|
||||||
ircd::init(ios.get_executor());
|
ircd::init(ios.get_executor(), homeserver);
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -343,9 +280,9 @@ noexcept try
|
||||||
if(norun)
|
if(norun)
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
// 1
|
|
||||||
// Execution.
|
// Execution.
|
||||||
// Blocks until a clean exit from a quit() or an exception comes out of it.
|
// Loops until a clean exit from a quit() or an exception comes out of it.
|
||||||
|
// Alternatively, ios.run() could be otherwise used here.
|
||||||
size_t epoch{0};
|
size_t epoch{0};
|
||||||
for(; !ios.stopped(); ++epoch)
|
for(; !ios.stopped(); ++epoch)
|
||||||
{
|
{
|
||||||
|
|
|
@ -129,6 +129,18 @@ namespace ircd
|
||||||
extern conf::item<bool> write_avoid; // implies maintenance
|
extern conf::item<bool> write_avoid; // implies maintenance
|
||||||
extern conf::item<bool> read_only; // implies write_avoid
|
extern conf::item<bool> read_only; // implies write_avoid
|
||||||
extern conf::item<bool> defaults;
|
extern conf::item<bool> defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ircd
|
||||||
|
{
|
||||||
|
/// Prototype of the continuation supplied to the user's main function.
|
||||||
|
using main_continuation = void (*)();
|
||||||
|
|
||||||
|
/// Prototype of the user's main function.
|
||||||
|
using user_function = void (main_continuation);
|
||||||
|
|
||||||
|
/// User's main function
|
||||||
|
using user_main = std::function<user_function>;
|
||||||
|
|
||||||
// Informational
|
// Informational
|
||||||
seconds uptime();
|
seconds uptime();
|
||||||
|
@ -136,7 +148,7 @@ namespace ircd
|
||||||
// Control panel
|
// Control panel
|
||||||
void cont() noexcept;
|
void cont() noexcept;
|
||||||
bool quit() noexcept;
|
bool quit() noexcept;
|
||||||
void init(boost::asio::executor &&);
|
void init(boost::asio::executor &&, user_main);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_IRCD_IRCD_H
|
#endif // HAVE_IRCD_IRCD_H
|
||||||
|
|
|
@ -66,13 +66,11 @@ enum class ircd::run::level
|
||||||
:int
|
:int
|
||||||
{
|
{
|
||||||
FAULT = -1, ///< Unrecoverable fault.
|
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 internal subsystems.
|
START = 2, ///< | | Starting internal subsystems.
|
||||||
LOAD = 3, ///< | | Load user application.
|
RUN = 3, ///< O | IRCd in service.
|
||||||
RUN = 4, ///< O | IRCd in service.
|
QUIT = 4, ///< >---^ Clean shutdown starting.
|
||||||
QUIT = 5, ///< | | Clean shutdown starting.
|
|
||||||
UNLOAD = 6, ///< --> ^ Unload user application.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An instance of this class registers itself to be called back when
|
/// An instance of this class registers itself to be called back when
|
||||||
|
|
77
ircd/ircd.cc
77
ircd/ircd.cc
|
@ -17,7 +17,7 @@ namespace ircd
|
||||||
|
|
||||||
// Main function. This frame anchors the initialization and destruction of
|
// Main function. This frame anchors the initialization and destruction of
|
||||||
// all non-static assets provided by the library.
|
// all non-static assets provided by the library.
|
||||||
static void main() noexcept;
|
static void main(user_main) noexcept;
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal interface to ircd::run (see ircd/run.h, ircd/run.cc)
|
// internal interface to ircd::run (see ircd/run.h, ircd/run.cc)
|
||||||
|
@ -174,7 +174,8 @@ ircd::main_context;
|
||||||
///
|
///
|
||||||
/// init() can only be called from a run::level::HALT state
|
/// init() can only be called from a run::level::HALT state
|
||||||
void
|
void
|
||||||
ircd::init(boost::asio::executor &&executor)
|
ircd::init(boost::asio::executor &&executor,
|
||||||
|
user_main user)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// This function must only be called from a HALT state.
|
// This function must only be called from a HALT state.
|
||||||
|
@ -211,7 +212,10 @@ try
|
||||||
//
|
//
|
||||||
context main_context
|
context main_context
|
||||||
{
|
{
|
||||||
"main", 256_KiB, &ircd::main, context::POST | context::SLICE_EXEMPT
|
"main",
|
||||||
|
512_KiB,
|
||||||
|
std::bind(&ircd::main, std::move(user)),
|
||||||
|
context::POST | context::SLICE_EXEMPT
|
||||||
};
|
};
|
||||||
|
|
||||||
// The default behavior for ircd::context is to join the ctx on dtor. We
|
// The default behavior for ircd::context is to join the ctx on dtor. We
|
||||||
|
@ -266,7 +270,6 @@ noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
case run::level::START:
|
case run::level::START:
|
||||||
case run::level::LOAD:
|
|
||||||
{
|
{
|
||||||
ctx::terminate(*main_context);
|
ctx::terminate(*main_context);
|
||||||
main_context = nullptr;
|
main_context = nullptr;
|
||||||
|
@ -280,7 +283,6 @@ noexcept
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case run::level::UNLOAD:
|
|
||||||
case run::level::QUIT:
|
case run::level::QUIT:
|
||||||
case run::level::HALT:
|
case run::level::HALT:
|
||||||
case run::level::FAULT:
|
case run::level::FAULT:
|
||||||
|
@ -309,7 +311,6 @@ noexcept
|
||||||
|
|
||||||
switch(run::level)
|
switch(run::level)
|
||||||
{
|
{
|
||||||
case run::level::LOAD:
|
|
||||||
case run::level::RUN:
|
case run::level::RUN:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -339,22 +340,24 @@ noexcept
|
||||||
/// io_context from running more jobs.
|
/// io_context from running more jobs.
|
||||||
///
|
///
|
||||||
void
|
void
|
||||||
ircd::main()
|
ircd::main(user_main user)
|
||||||
noexcept try
|
noexcept try
|
||||||
{
|
{
|
||||||
// When this function completes without exception, subsystems are done shutting down and IRCd
|
// When this function completes without exception, subsystems are done
|
||||||
// transitions to HALT.
|
// shutting down and IRCd transitions to HALT.
|
||||||
const unwind_defer halted{[]
|
const unwind_defer halted{[]
|
||||||
{
|
{
|
||||||
run::set(run::level::HALT);
|
run::set(run::level::HALT);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
// We block interruption/termination of the main context by default;
|
||||||
|
// this covers most of the functionality performed by this function and
|
||||||
|
// its callees. This provides consistent and complete runlevel transitions.
|
||||||
|
const ctx::uninterruptible::nothrow disable_interruption {true};
|
||||||
|
|
||||||
// When this function is entered IRCd will transition to START indicating
|
// When this function is entered IRCd will transition to START indicating
|
||||||
// that subsystems are initializing.
|
// that subsystems are initializing.
|
||||||
{
|
run::set(run::level::START);
|
||||||
const ctx::uninterruptible ui;
|
|
||||||
run::set(run::level::START);
|
|
||||||
}
|
|
||||||
|
|
||||||
// These objects are the init()'s and fini()'s for each subsystem.
|
// These objects are the init()'s and fini()'s for each subsystem.
|
||||||
// Appearing here ties their life to the main context. Initialization can
|
// Appearing here ties their life to the main context. Initialization can
|
||||||
|
@ -372,31 +375,39 @@ noexcept try
|
||||||
server::init _server_; // Server related
|
server::init _server_; // Server related
|
||||||
js::init _js_; // SpiderMonkey
|
js::init _js_; // SpiderMonkey
|
||||||
|
|
||||||
// Transition to the QUIT and UNLOAD states on unwind.
|
// Continuation passed to the user's main function.
|
||||||
const unwind quit{[]
|
const auto continuation{[]
|
||||||
{
|
{
|
||||||
const ctx::uninterruptible::nothrow ui;
|
// Transition to the QUIT state on unwind.
|
||||||
ircd::run::set(run::level::QUIT);
|
const unwind quit{[]
|
||||||
ircd::run::set(run::level::UNLOAD);
|
{
|
||||||
|
const ctx::uninterruptible::nothrow disable_interruption {true};
|
||||||
|
ircd::run::set(run::level::QUIT);
|
||||||
|
}};
|
||||||
|
|
||||||
|
// Block interruptions again for runlevel transitions.
|
||||||
|
const ctx::uninterruptible disable_interruption {true};
|
||||||
|
|
||||||
|
// IRCd will now transition to the RUN state indicating full functionality.
|
||||||
|
run::set(run::level::RUN);
|
||||||
|
|
||||||
|
// Allow interrupts while running so we can be notified to quit.
|
||||||
|
const ctx::uninterruptible reenable_interruption {false};
|
||||||
|
|
||||||
|
// wait() 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.
|
||||||
|
ctx::wait();
|
||||||
}};
|
}};
|
||||||
|
|
||||||
// IRCd will now transition to the LOAD state allowing library user's to
|
if(!user)
|
||||||
// load their applications using the run::changed callback.
|
return continuation();
|
||||||
{
|
|
||||||
const ctx::uninterruptible ui;
|
|
||||||
run::set(run::level::LOAD);
|
|
||||||
}
|
|
||||||
|
|
||||||
// IRCd will now transition to the RUN state indicating full functionality.
|
// Allow interrupts again by default for the duration of the user.
|
||||||
{
|
const ctx::uninterruptible reenable_interruption {false};
|
||||||
const ctx::uninterruptible ui;
|
|
||||||
run::set(run::level::RUN);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This call blocks until the main context is notified or interrupted etc.
|
// Call user.
|
||||||
// Waiting here will hold open this stack with all of the above objects
|
user(continuation);
|
||||||
// living on it.
|
|
||||||
ctx::wait();
|
|
||||||
}
|
}
|
||||||
catch(const std::exception &e)
|
catch(const std::exception &e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -142,8 +142,7 @@ try
|
||||||
{
|
{
|
||||||
case level::HALT: break;
|
case level::HALT: break;
|
||||||
case level::QUIT: break;
|
case level::QUIT: break;
|
||||||
case level::UNLOAD: break;
|
case level::START: throw;
|
||||||
case level::LOAD: throw;
|
|
||||||
default: throw;
|
default: throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,8 +176,8 @@ catch(const std::exception &e)
|
||||||
{
|
{
|
||||||
switch(new_level)
|
switch(new_level)
|
||||||
{
|
{
|
||||||
case level::LOAD: throw;
|
case level::START: throw;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::critical
|
log::critical
|
||||||
|
@ -199,10 +198,8 @@ 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::LOAD: return "LOAD";
|
|
||||||
case level::RUN: return "RUN";
|
case level::RUN: return "RUN";
|
||||||
case level::QUIT: return "QUIT";
|
case level::QUIT: return "QUIT";
|
||||||
case level::UNLOAD: return "UNLOAD";
|
|
||||||
case level::FAULT: return "FAULT";
|
case level::FAULT: return "FAULT";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -196,7 +196,7 @@ void
|
||||||
ircd::m::on_load()
|
ircd::m::on_load()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
assert(ircd::run::level == run::level::LOAD);
|
assert(ircd::run::level == run::level::START);
|
||||||
}
|
}
|
||||||
catch(const m::error &e)
|
catch(const m::error &e)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue