Shutdown Process
The overall shutdown process looks like this:
shutdown()
on all stages is invoked- all stages with threads are expected to terminate those threads before leaving
shutdown()
- the executor waits for all message queues to fully drain
finalize()
on all stages is invoked- the pipeline exits with a status code (0 for success, anything else for failure)
Shutdown Tokens
Particularly for simulations or other offline-processing pipelines, it can be useful for a stage to request a shutdown of the pipeline.
(Normally, a pipeline only shuts down when all of its timers are done firing and its queues are empty – but for complex pipelines or pipelines with things like the log player, which intentionally keep their timers firing, this can lead to hanging on shutdown).
Any stage can request a shutdown token
, which allows you to register when you are
ready to shutdown.
You can retrieve these during your initialization stage:
#include "ark/pipeline/shutdown_token.hh"
ark::pipeline::ShutdownTokenPtr my_token_;
void initialize(ark::pipeline::StageInterface &interface)
{
my_token_ = interface.add_shutdown_token(ark::pipeline::ShutdownTokenType::Standard);
}
Once you have a token, at any time, you can request a shutdown. For normal, healthy shutdowns, use:
my_token_->request_successful_shutdown();
If an error occurred, you can give a reason:
my_token_->request_failure_shutdown("Something unrecoverable failed.");
In either case, the pipeline will shutdown as soon as possible (and shutdown will be deterministic in the case of running under the simclock executor, which is to say, the simclock executor will run until precise time that the shutdown token was invoked).
The executors execute
method will return true/false to indicate if the pipeline
exited successfully or failed. You can further use that to handle unexpected failures
or to return non-zero from your main()
API.
Token Types
You must pass in a ’type’ to the shutdown token. There are two types:
RequiredForShutdown
Standard
The RequiredForShutdown
token type indicates that you must request a shutdown
for the pipeline to exit. If other tokens request a shutdown, the pipeline will not
exit until you request a shutdown.
The Standard
token type allows you to request shutdown without holding up the rest
of the system from requesting shutdown. In other words, if anyone requests a shutdown,
the system will shutdown.
SIGINT and Requesting Shutdowns
When the executor’s interrupt
method is called (for example, this is called when you hit CTRL+C
or deliver a SIGINT and you are using the main
APIs), all of the stages in the pipeline will have
their interrupt()
method invoked in turn.
This allows you to carry out complex shutdown logic that requires the full pipeline to be operational. A common usecase is to send out UDP or CAN packets to devices to shut them down gracefully.
If you do implement the ark::pipeline::Stage::interrupt()
method, it is strongly advised that you
take out a RequiredForShutdown
token. Without this, the system will proceed to a normal shutdown
immediately after interrupt() was invoked.
A simple example of this API:
class MyStage : public ark::pipeline::Stage
{
public:
void initialize(ark::pipeline::StageInterface &interface) override
{
token_ = interface.add_shutdown_token(ark::pipeline::ShutdownTokenType::RequiredForShutdown);
}
void interrupt() override
{
token_->request_successful_shutdown();
}
private:
ShutdownTokenPtr token_;
};
This interrupt callback does no extra work; it simply requests a shutdown when a SIGINT arrives. If you
do have a required-for-shutdown token, and interrupt
is received, and you never request a shutdown
through your token, the pipeline will not exit.