3

I have a Swing application that handles Ctrl+C using addShutdownHook(), and it works fine until one of the shutdown tasks I have calls a function that under normal circumstances changes a JLabel text, at which point it hangs.

I assume the problem is that the Swing EDT has either terminated or is waiting for something.

Is there a way to either determine that the EDT has terminated or is "done" (so I can avoid calling Swing methods), or to prevent the usual close-all-the-windows-down behavior on Ctrl-C?


Just to clarify:

I have a method in a class called stop(). Under normal circumstances this can get called (along with its complement start()) and it triggers a cascade of things that causes a JLabel to be updated, for visual feedback that a stop() has occurred.

When my shutdown hook runs, I need to call stop() to gracefully shutdown some resources.

What I'm asking is how I can detect that Swing EDT is not there, so I can rewrite stop() so that it detects a lack of Swing and avoids the call to the Swing functions.

Igor Nardin
  • 1,611
  • 14
  • 12
Jason S
  • 184,598
  • 164
  • 608
  • 970

3 Answers3

5

The following hack helps to determine whether JVM is in the process of shutting down without passing any flags around:

private static final Thread DUMMY_HOOK = new Thread();

public static boolean isShuttingDown()
{
    try {
        Runtime.getRuntime().addShutdownHook(DUMMY_HOOK);
        Runtime.getRuntime().removeShutdownHook(DUMMY_HOOK);
    } catch ( IllegalStateException e ) {
        return true;
    }

    return false;
}

But don't forget about possible concurrency issues.

Igor Nardin
  • 1,611
  • 14
  • 12
2

Shutdown hooks should not expect other services to be in a known state (e.g. the UI event-handling thread, etc.), because the application has been requested to be shut down. Writing a shutdown hook that tries to update something that isn't 100% within the hook's control can result in this, and other behavior, that will be difficult to troubleshoot.

The addShutdownHook method specifically mentions this case, and warns that you shouldn't try these types of actions, since doing so can easily result in unexpected consequences, such as the lockup you've observed.

From the documentation:

Shutdown hooks run at a delicate time in the life cycle of a virtual machine and should therefore be coded defensively. They should, in particular, be written to be thread-safe and to avoid deadlocks insofar as possible. They should also not rely blindly upon services that may have registered their own shutdown hooks and therefore may themselves in the process of shutting down.

In this case, since Swing is a multi-threaded UI system which you do not control, I would recommend not trying to alter anything in the UI at the shutdown stage of you're program's life. Doing so will result in strange, unpredictable, and sometimes non-repeatable situations that can vary from one run to the next, or from one machine to the next, depending on how the threads get scheduled to shutdown. How, and when, another thread stops its work is not designed to happen in a linear, predictable way. As such, any code you write in a multi-threaded program that relies upon another thread being in a certain state at a certain time (unless those threads are clearly communicating with each other about their state), will open you up to these kinds of issues.

I suppose my follow up question would be, "Why do you want/need to alter the JLabel during the shutdown process?" If you want to change the state of something before shutdown, the better way to do it would be catch the keyboard input as a regular event (preventing it from causing the application to close), change the JLabel text, and then start a shutdown of the application yourself via a call to System.runFinalization() and/or System.ext(int)

jefflunt
  • 33,527
  • 7
  • 88
  • 126
  • 1
    Right, that's why I asked 'Is there a way to either determine that the EDT has terminated or is "done"', so I can avoid calling the Swing functions. – Jason S Jul 25 '11 at 14:47
  • Not really, no. The whole point is that in the shutdown process, you can't rely on the state of another thread - so trying to do so is always going to lead you down a difficult path. I've updated my answer with an additional paragraph, offering an alternative. – jefflunt Jul 25 '11 at 15:02
  • Thanks. I don't want to alter the JLabel -- I'm calling a function that I use under normal circumstances that does. – Jason S Jul 25 '11 at 15:04
  • Well, either way, /something/ is running/calling methods with the assumption that everything is normal, but it's not (it's shutdown time). Gotta get that assumption out of your code, is all. – jefflunt Jul 25 '11 at 15:22
  • that's what I'm trying to do! – Jason S Jul 25 '11 at 15:44
  • Tthe answer to your question: "...how I can detect that Swing EDT is not there, so I can rewrite stop() so that it detects a lack of Swing?" is, "You can't do that." There's not some way to rig it up to do it in a thread-safe way /inside of a shutdown hook/. Once your shutdown hook is running, it's too late. – jefflunt Jul 25 '11 at 16:30
  • The best you can do is You need to capture the Ctrl+C via a KeyboardInputListener (or similar), and set a flag in a public, static variable somewhere that indicates "don't do UI updates", then start releases your resources before the VM begins its shutdown process. Doing what you want to do inside of a shutdown hook, and as a result, calling methods that rely on threads that aren't guaranteed to be running, IS the problem. – jefflunt Jul 25 '11 at 16:31
  • @JasonS let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/1801/discussion-between-normalocity-and-jason-s) – jefflunt Jul 25 '11 at 16:31
0

I ended up setting a flag at the beginning of the shutdown hook, and communicating this (via objects set up ahead of time) to the object with my stop() method, so that it can test this flag and decide whether or not to call the Swing methods.

If the shutdown flag is not set, the Swing methods get called. Otherwise they don't.

Jason S
  • 184,598
  • 164
  • 608
  • 970
  • This will work some of the time. However, it's still dependent on the order in which things are shutdown. Since you cannot control in what order threads get shutdown, nor in what order shutdown hooks are started, the order of events that caused your lockup can still happen: (1) The JVM starts to shut down due to Ctrl+C, (2) the Swing threads get closed, and your shutdown hook has NOT YET been called, (3) a call that results in the JLabel requesting to be changed is called - your application hangs (4) your shutdown hook gets called - but it's too late to prevent the invalid/lockup call. – jefflunt Jul 25 '11 at 16:54
  • @normalocity: not worried about that, at least in the context of this problem. If I find that the sequence (2), (3) as you describe occurs, then that's a separate problem I need to deal with, and it would have occurred even if I hadn't used any shutdown hooks. – Jason S Jul 25 '11 at 16:58
  • Well, either way I'm glad your code is working. You are, after all, the engineer of your own code, so if you're not worried about it, then I can't be worried about it. Reminds me of your other question: http://stackoverflow.com/questions/508850/java-concurrency-cynicism-gone-too-far I really love concurrent code, so I'm just a fan of tackling these multi-threaded questions, is all. – jefflunt Jul 25 '11 at 17:05