Support sending out RPCs for multiple status messages
This lets us start, stop, and restart multiple applications at once
efficiently. There is nothing in the RPC protocol that requires a
specific response.
Change-Id: If0b933aef726075a47648c9e33ab1f48b5155c0f
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/aos/starter/starter_rpc_lib.cc b/aos/starter/starter_rpc_lib.cc
index 7b86c0a..eb9a403 100644
--- a/aos/starter/starter_rpc_lib.cc
+++ b/aos/starter/starter_rpc_lib.cc
@@ -42,84 +42,119 @@
bool SendCommandBlocking(aos::starter::Command command, std::string_view name,
const aos::Configuration *config,
std::chrono::milliseconds timeout) {
+ return SendCommandBlocking(
+ std::vector<std::pair<aos::starter::Command, std::string_view>>{
+ {command, name}},
+ config, timeout);
+}
+
+bool SendCommandBlocking(
+ std::vector<std::pair<aos::starter::Command, std::string_view>> commands,
+ const aos::Configuration *config, std::chrono::milliseconds timeout) {
aos::ShmEventLoop event_loop(config);
event_loop.SkipAosLog();
::aos::Sender<aos::starter::StarterRpc> cmd_sender =
event_loop.MakeSender<aos::starter::StarterRpc>("/aos");
- // Wait until event loop starts to send command so watcher is ready
- event_loop.OnRun([&cmd_sender, command, name] {
- aos::Sender<aos::starter::StarterRpc>::Builder builder =
- cmd_sender.MakeBuilder();
+ // Wait until event loop starts to send all commands so the watcher is ready
+ event_loop.OnRun([&cmd_sender, &commands] {
+ for (const std::pair<aos::starter::Command, std::string_view>
+ &command_pair : commands) {
+ const aos::starter::Command command = command_pair.first;
+ const std::string_view name = command_pair.second;
+ aos::Sender<aos::starter::StarterRpc>::Builder builder =
+ cmd_sender.MakeBuilder();
- auto name_str = builder.fbb()->CreateString(name);
+ auto name_str = builder.fbb()->CreateString(name);
- aos::starter::StarterRpc::Builder cmd_builder =
- builder.MakeBuilder<aos::starter::StarterRpc>();
+ aos::starter::StarterRpc::Builder cmd_builder =
+ builder.MakeBuilder<aos::starter::StarterRpc>();
- cmd_builder.add_name(name_str);
- cmd_builder.add_command(command);
+ cmd_builder.add_name(name_str);
+ cmd_builder.add_command(command);
- builder.Send(cmd_builder.Finish());
+ builder.Send(cmd_builder.Finish());
+ }
});
// If still waiting after timeout milliseconds, exit the loop
event_loop.AddTimer([&event_loop] { event_loop.Exit(); })
->Setup(event_loop.monotonic_now() + timeout);
- // Fetch the last list of statuses to compare the requested application's id
- // against for commands such as restart.
+ // Fetch the last list of statuses. The id field changes every time the
+ // application restarts. By detecting when the application is running with a
+ // different ID, we can detect restarts.
auto initial_status_fetcher =
event_loop.MakeFetcher<aos::starter::Status>("/aos");
initial_status_fetcher.Fetch();
- auto initial_status =
- initial_status_fetcher.get()
- ? FindApplicationStatus(*initial_status_fetcher, name)
- : nullptr;
- const std::optional<uint64_t> initial_id =
- (initial_status != nullptr && initial_status->has_id())
- ? std::make_optional(initial_status->id())
- : std::nullopt;
+ std::vector<std::optional<uint64_t>> initial_ids;
+ for (const std::pair<aos::starter::Command, std::string_view> &command_pair :
+ commands) {
+ const std::string_view name = command_pair.second;
+ auto initial_status =
+ initial_status_fetcher.get()
+ ? FindApplicationStatus(*initial_status_fetcher, name)
+ : nullptr;
+
+ initial_ids.emplace_back(
+ (initial_status != nullptr && initial_status->has_id())
+ ? std::make_optional(initial_status->id())
+ : std::nullopt);
+ }
+
+ std::vector<bool> successes(commands.size(), false);
bool success = false;
- event_loop.MakeWatcher(
- "/aos", [&event_loop, command, name, initial_id,
- &success](const aos::starter::Status &status) {
- const aos::starter::ApplicationStatus *app_status =
- FindApplicationStatus(status, name);
+ event_loop.MakeWatcher("/aos", [&event_loop, &commands, &initial_ids, &success,
+ &successes](
+ const aos::starter::Status &status) {
+ size_t index = 0;
+ for (const std::pair<aos::starter::Command, std::string_view>
+ &command_pair : commands) {
+ const aos::starter::Command command = command_pair.first;
+ const std::string_view name = command_pair.second;
- const std::optional<aos::starter::State> state =
- (app_status != nullptr && app_status->has_state())
- ? std::make_optional(app_status->state())
- : std::nullopt;
+ const aos::starter::ApplicationStatus *app_status =
+ FindApplicationStatus(status, name);
- switch (command) {
- case aos::starter::Command::START: {
- if (state == aos::starter::State::RUNNING) {
- success = true;
- event_loop.Exit();
- }
- break;
+ const std::optional<aos::starter::State> state =
+ (app_status != nullptr && app_status->has_state())
+ ? std::make_optional(app_status->state())
+ : std::nullopt;
+
+ switch (command) {
+ case aos::starter::Command::START: {
+ if (state == aos::starter::State::RUNNING) {
+ successes[index] = true;
}
- case aos::starter::Command::STOP: {
- if (state == aos::starter::State::STOPPED) {
- success = true;
- event_loop.Exit();
- }
- break;
- }
- case aos::starter::Command::RESTART: {
- if (state == aos::starter::State::RUNNING && app_status->has_id() &&
- app_status->id() != initial_id) {
- success = true;
- event_loop.Exit();
- }
- break;
- }
+ break;
}
- });
+ case aos::starter::Command::STOP: {
+ if (state == aos::starter::State::STOPPED) {
+ successes[index] = true;
+ }
+ break;
+ }
+ case aos::starter::Command::RESTART: {
+ if (state == aos::starter::State::RUNNING && app_status->has_id() &&
+ app_status->id() != initial_ids[index]) {
+ successes[index] = true;
+ }
+ break;
+ }
+ }
+ ++index;
+ }
+
+ // Wait until all applications are ready.
+ if (std::count(successes.begin(), successes.end(), true) ==
+ static_cast<ssize_t>(successes.size())) {
+ event_loop.Exit();
+ success = true;
+ }
+ });
event_loop.Run();