Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consistent shutdown when plugin_startup() fails. #27

Merged
merged 2 commits into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/appbase/application_instance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class plugin : public abstract_plugin {
static_cast<Impl*>(this)->plugin_requires([&](auto& plug) { plug.startup(); });
app().plugin_started(this); // add to `running_plugins` before so it will be shutdown if we throw in `plugin_startup()`
static_cast<Impl*>(this)->plugin_startup();
if (app().is_quiting()) {
// some plugins, instead of throwing an exception when failing to start, call app().quit().
// throw exception to ensure no more plugin gets initialized, and all running plugins receive `plugin_shutdown()`
throw std::runtime_error("plugin " + name() + " quit during startup");
}
}
assert(_state == started); // if initial state was not initialized, final state cannot be started
}
Expand Down
36 changes: 36 additions & 0 deletions tests/basic_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class pluginA : public appbase::plugin<pluginA>
("dbsize", bpo::value<uint64_t>()->default_value( 8*1024 ), "Minimum size MB of database shared memory file")
("replay", "clear db and replay all blocks" )
("throw_during_startup", "throw an exception in plugin_startup()" )
("quit_during_startup", "calls app().quit() plugin_startup()" )
("log", "log messages" );
}

Expand All @@ -36,6 +37,7 @@ class pluginA : public appbase::plugin<pluginA>
replay_ = !!options.count("replay");
log_ = !!options.count("log");
throw_during_startup_ = !!options.count("throw_during_startup");
quit_during_startup_ = !!options.count("quit_during_startup");
dbsize_ = options.at("dbsize").as<uint64_t>();
log("initialize pluginA");
}
Expand All @@ -44,6 +46,8 @@ class pluginA : public appbase::plugin<pluginA>
log("starting pluginA");
if (throw_during_startup_)
do_throw("throwing as requested");
if (quit_during_startup_)
appbase::app().quit();
}

void plugin_shutdown() {
Expand All @@ -67,6 +71,7 @@ class pluginA : public appbase::plugin<pluginA>
bool readonly_ {false};
bool replay_ {false};
bool throw_during_startup_ {false};
bool quit_during_startup_ {false};
bool log_ {false};
uint64_t dbsize_ {0};
uint32_t* shutdown_counter { nullptr };
Expand Down Expand Up @@ -368,6 +373,37 @@ BOOST_AUTO_TEST_CASE(exception_in_startup)
app_thread.join();
}

// -----------------------------------------------------------------------------
// Here we make sure that if a plugin calls app().quit() during `plugin_startup()`
// plugin_shutdown is called for that plugin
// -----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(quit_in_startup)
{
appbase::application::register_plugin<pluginB>();

appbase::scoped_app app;

const char* argv[] = { bu::framework::current_test_case().p_name->c_str(),
"--plugin", "pluginA", "--log", "--quit_during_startup",
"--plugin", "pluginB", "--log2" };

BOOST_CHECK(app->initialize<pluginB>(sizeof(argv) / sizeof(char*), const_cast<char**>(argv)));

std::thread app_thread( [&]() {
auto& pA = app->get_plugin<pluginA>();
uint32_t shutdown_counter(0);
pA.set_shutdown_counter(shutdown_counter);

try {
app->startup();
} catch(const std::exception& e ) {
std::cout << "exception during startup (as expected): " << e.what() << "\n";
}
BOOST_CHECK(shutdown_counter == 1); // check that plugin_shutdown() was executed for pA
} );

app_thread.join();
}

// -----------------------------------------------------------------------------
// Make sure that queue is emptied when `app->quit()` is called, and that the
Expand Down