Skip to content

Commit

Permalink
Merge pull request #27 from AntelopeIO/consistent_shutdown_when_quit
Browse files Browse the repository at this point in the history
Consistent shutdown when `plugin_startup()` fails.
  • Loading branch information
greg7mdp authored May 30, 2023
2 parents 65bb056 + 15db98e commit fa081b4
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 5 deletions.
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
43 changes: 38 additions & 5 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,38 @@ 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 ) {
// appbase framework will throw an exception if app.quit() is called during startup
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 All @@ -388,11 +425,7 @@ BOOST_AUTO_TEST_CASE(queue_emptied_at_quit)
std::thread app_thread( [&]() {
app->startup();
plugin_promise.set_value( {app->get_plugin<pluginA>(), app->get_plugin<pluginB>()} );
try {
app->exec();
} catch(const std::exception& e ) {
std::cout << "exception in exec (as expected): " << e.what() << "\n";
}
app->exec();
} );

auto [pA, pB] = plugin_fut.get();
Expand Down

0 comments on commit fa081b4

Please sign in to comment.