-
Notifications
You must be signed in to change notification settings - Fork 61
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
Presence of a non-optional shutdown hook messes up with shutdown sequence #371
Comments
I'm not quite sure I'm following. Pi4j only stops everything, when shutdown is called. Maybe you are just calling it too soon? Do you have a more detailed example of how you are managing pi4j's lifecycle? |
So intuitively it should, but unfortunately this is not the case. As you can see, the
My point is that the global shutdown hook that you can't turn off is not a good idea to have in a library, because the application that uses the library loses full control of the shutdown sequence. Hope this is clear. |
The workaround for this is to register a pi4j shutdown listener and block the pi4j shutdown until all the components that use it have shut down (https://github.com/ylexus/jiotty/blob/master/jiotty-connector-rpi/src/main/java/net/yudichev/jiotty/connector/rpigpio/Pi4jContextProvider.java). It's rather elaborate; disabling pi4j shutdown hook would be a much preferred solution. final class Pi4jContextProvider extends BaseLifecycleComponent implements Provider<Context> {
private static final Logger logger = LoggerFactory.getLogger(Pi4jContextProvider.class);
private static final int SHUTDOWN_TIMEOUT_SECONDS = 15;
private final CountDownLatch preShutdownLatch = new CountDownLatch(1);
private final CountDownLatch postShutdownLatch = new CountDownLatch(1);
private volatile boolean pi4jShuttingDown;
private Context gpio;
@Override
public Context get() {
return whenStartedAndNotLifecycling(() -> gpio);
}
@Override
public void doStart() {
gpio = Pi4J.newAutoContext();
// TODO this is a workaround for https://github.com/Pi4J/pi4j-v2/issues/371 - remove when fixed
gpio.addListener(new ShutdownListener() {
@Override
public void beforeShutdown(ShutdownEvent event) {
pi4jShuttingDown = true;
logger.debug("Blocking pi4j shutdown until this component is closed");
asUnchecked(() -> {
var stopTriggered = preShutdownLatch.await(SHUTDOWN_TIMEOUT_SECONDS, SECONDS);
if (stopTriggered) {
logger.debug("Stopping - pi4j shutdown released");
} else {
logger.warn("Timed out waiting for this component to be stopped");
}
});
}
@Override
public void onShutdown(ShutdownEvent event) {
postShutdownLatch.countDown();
}
});
}
@Override
protected void doStop() {
logger.debug("Releasing pi4j shutdown gate and shutting down pi4j");
preShutdownLatch.countDown();
// best effort prevent double shutdown call and awaiting successful shutdown, will work if the pi4j shutdown hook fires first
if (pi4jShuttingDown) {
// pi4's own shutdown hook is doing the job - wait for shutdown to finish
logger.debug("Waiting for pi4j to shut down");
asUnchecked(() -> {
var stopped = postShutdownLatch.await(SHUTDOWN_TIMEOUT_SECONDS, SECONDS);
if (stopped) {
logger.debug("pi4j shut down");
} else {
logger.warn("Timed out waiting for pi4 to shut down");
}
});
} else {
gpio.shutdown(); // does it synchronously
}
}
} |
I never really realized, that pi4j add this shutdown hook, all my test apps, which are simple console apps, etc. add their own shutdown hook to shutdown pi4j. Apps should really control the life cycle, not the library, i totally agree. |
what should be the default then? |
Fixed in next release |
@eitch been a long time without releases - time to do one maybe? |
Related somewhat to #213. I have an application that properly manages component dependencies, including the order of initialisation and shutdown. pi4j context is just another component in the app that is initialised before it's used by other components, and is shut down via
Context.shutdown()
(which shuts down theRuntime
) just after all the components that use it have already shut down.However,
DefaultRuntime
adds a shutdown hook that is executed concurrently with my application shutdown sequence and obliterates the context before the components that use pi4j are stopped, causing exceptions (example is below, but it does not matter).I think a component based library that has clear start/stop points (which pi4j does) should not unconditionally start shutting itself down. I agree it's useful for apps don't care about shutdown sequences or exceptions, but for more organised apps like mine it would be very useful to make the shutdown hook optional.
I can raise a PR, possibly by adding a property that disables it so that a user could do:
, but please recommend the best way of doing this.
The text was updated successfully, but these errors were encountered: