14

Java3D starts several system threads and doesn't set the isDaemon flag on them. When I dispose the (only) JFrame of my application it won't terminate because these threads are still running.

Calling System.exit() seems to be the only way to terminate the application. (Or killing it from outside, of course).

As I don't like to call System.exit() I have tried the following (but without success):

  • calling removeAllLocales() on the VirtualUniverse: This terminates most of the threads, but still there is one (named J3D-Renderer-1) remaining.
  • using reflection to obtain a reference to the the field ThreadGroup rootThreadGroupp in javax.media.j3d.MasterControl and settting isDeamon true on that ThreadGroup. This didn't seem to have any effect.
  • geting a reference to the ThreadGroup named "Java3D" and calling interrupt() on it: This caused the java3d threads to write InterruptedException to stderr, but nothing else.
  • locate the sources of the Java3d-core library and propose a patch: I found a repository here: https://github.com/hharrison/java3d-core and here: https://java.net/projects/j3d-core/sources . The later one looks "official" but shows the last change in it happened 5 years ago and the former one looks like a private fork to me.

I am close to giving up and make that call to System.exit(), but I still don't like it. Do you know a better way?

holgero
  • 2,640
  • 1
  • 13
  • 17
  • 1
    I haven’t touched Java3D in many years, but checking the source for one old Java3D program I wrote, there’s a `System.exit(0)` call in there :-/ Can you elaborate on why you don’t want to use that? – andrewdotn May 09 '13 at 20:58
  • 3
    Mainly because it seems somehow unclean to me. And if it is the only way to clean up after using java3d, it will be impossible to write JUnit tests that use Java3D. If the JUnit tests happen to trigger the start of the Java3D threads, I am without a good option: I obviously cannot call System.exit() from within the tests (this would make it impossible to get the result of the tests reported), so I would like to have a way out in that case. Thanks for looking! – holgero May 09 '13 at 21:07
  • Did you try `SimpleUniverse#cleanup()` method? It does a bit more than `removeAllLocales()` – hoaz May 09 '13 at 21:42
  • The problem you're facing is the fact that the JVM won't exit until all non-daemon threads have exited, unless System.exit is called. So, you either figure out how to dispose of all the threads or you all System.exit – MadProgrammer May 09 '13 at 22:09
  • @hoaz I tried your suggestion: I exchanged the `VirtualUniverse` with a SimpleUniverse and called `cleanup()` on it (both instead of and additionally to the `removeAllLocales()` call). The result was that it still didn't exit properly. (Strange thing happened: After closing my main Frame, a new (?) window without any menu etc. appeared.) – holgero May 10 '13 at 09:14
  • @MadProgrammer yes, thank you for confirming what I already suspected. I hoped I had said so much in my question, but if it wasn't clear enough: I am looking for ways to make the Java3D threads go either away or to make them not blocking the termination of the application (like daemon threads). – holgero May 10 '13 at 09:20
  • From [this link](http://www.tecgraf.puc-rio.br/~ismael/Cursos/Cidade_CG/labs/Java3D/Java3D_onlinebook_selman/Htmls/3DJava_Ch18.htm#18-2), there might be another ThreadGroup named "Main". Maybe you could try to interrupt the two ThreadGroup "Java3D" and "Main". Following [this question](http://stackoverflow.com/q/1323408], you might be able to list all threads. Hope it helps. – ncenerar May 10 '13 at 11:29
  • @ncenerar The problem with sending `interrupt()` only to the Java3D thread group is not that some other threads remain afterwards, but that the Java3D threads more or less ignore the call. I looked into the implementation (from the github repository I mentioned) and they do this with the `InterruptedException`: catch it, print its name to stderr and then simply continue with their loop. So in short: sending `interrupt()` to more threads or thread groups won't help (Have to admit I didn't try it out, though). – holgero May 10 '13 at 11:57
  • I looked into implementation too and I think you should call `Renderer#finish()` method on Java3D thread that is bothering you. Just cast it and call. – hoaz May 10 '13 at 13:15
  • @hoaz yes, this did it. Many thanks! The only drawback is that the Renderer class is package private, so I had to resort to reflection, to call it. I will write the coding I used for this in an answer. – holgero May 10 '13 at 14:51
  • @hoaz seems there are also some race conditions involved. It works only some of the time. I deleted my answer again as it is not reliably working. – holgero May 10 '13 at 19:17
  • @hoaz OK, I got a version working reliably (after fixing another bug in the application where an invisible JFrame was created and never disposed of). It is still based on your tip, so thanks for it! – holgero May 12 '13 at 19:33

2 Answers2

2

One possible solution is to call the Java3dThread.finish() method on the Java3D threads. The drawback is that one has to bypass java access rules to call this method, as it is package-private. This code did the trick for me:

public void dispose() {
    virtualUniverse.removeAllLocales();
    try {
        // give the Java3D threads the chance to terminate peacefully.
        Thread.sleep(250);
    } catch (final InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    // and now handle the threads that didn't take the hint
    finishJava3dThreads();
}

private static void finishJava3dThreads() {
    final Class<?> rendererClass;
    final Method finishMethod;
    try {
        rendererClass = Class.forName("javax.media.j3d.J3dThread");
        finishMethod = rendererClass.getDeclaredMethod("finish");
        finishMethod.setAccessible(true);
    } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) {
        throw new RuntimeException(e);
    }
    final ThreadGroup[] groups = new ThreadGroup[10];
    final int count = Thread.currentThread().getThreadGroup().getParent().enumerate(groups);
    for (int i = 0; i < count; i++) {
        final ThreadGroup threadGroup = groups[i];
        if ("Java3D".equals(threadGroup.getName())) {
            threadGroup.setDaemon(true);
            final Thread[] threads = new Thread[threadGroup.activeCount()];
            final int threadCount = threadGroup.enumerate(threads);
            for (int j = 0; j < threadCount; j++) {
                final Thread thread = threads[j];
                if (rendererClass.isInstance(thread)) {
                    try {
                        finishMethod.invoke(thread);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            Thread.yield();
            threadGroup.interrupt();
        }
    }
}

Where virtualUniverse is the instance of the VirtualUniverse created earlier in the application.

Now I call this dispose() method to terminate Java3D when terminating the application:

   addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosed(final WindowEvent e) {
            plater.dispose();
        }
    });

Where plater is the instance containing the dispose() method from above.

Everything else just disposes the main JFrame:

    actions.put(EXIT_ACTION, new AbstractAction(EXIT_ACTION) {
        @Override
        public void actionPerformed(final ActionEvent e) {
            dispose();
        }
    });

and the default close operation is also set to DISPOSE_ON_CLOSE:

    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

Not sure if this is the best option, though. (I still prefer it over the System.exit() call, but using reflection in this way is somewhat fragile.)

holgero
  • 2,640
  • 1
  • 13
  • 17
0

System.exit(0) is correct way to end program.

It's convenient way to handle shutdown in program, where parts of the program can't be aware of each other. Then, if some part wants to quit, he can simply call System.exit(), and the shutdown hooks take care of doing all necessary shutdown tasks such as: closing files, dispose opened windows, and releasing resources.

Read the docu about shutdown hooks.

1ac0
  • 2,875
  • 3
  • 33
  • 47
  • 2
    Sorry, I don't think this comment answers my question. – holgero May 12 '13 at 09:23
  • @holgero if you don't like to call system.exit() it doesn't mean it's not the correct way to terminate program. system.exit() is definitely correct way to terminate the program. – 1ac0 May 16 '13 at 07:52
  • You may even be right with your opinion, but it is still not an answer to my question. I think my question is quite specific: "Is there a way ... *without* calling System.exit()?" and it is *not*: "What is the right way to terminate an application?". – holgero May 16 '13 at 09:29
  • 1
    @holgero why to reinvent the wheel if standard system.exit(int) is correct way to terminate program? – 1ac0 May 16 '13 at 14:48
  • 1. Not being able to shutdown Java3d properly without resorting to System.exit() is a severe limitation. (See my answer to andrewdotn's comment on my question.) 2. Curiosity. 3. I don't think it is re-inventing the wheel. 4. Insisting on finding a way to terminate in a clean way has already helped me to spot another bug in my application (where an invisible JFrame was created accidentally which also blocked the clean exit). 5. I am not convinced, that you are right in saying that System.exit() is the correct way to end an application. I think it is rather a last resort than the right way – holgero May 17 '13 at 12:12