Why is my finally block not firing after projectDCThread.join() is called? This method IS in a class that implements Runnable, however it's not overriding the deprecated stop() method.

public synchronized void stop()
    {
        if(isRunning || safeCloseRequested)
        {
            try
            {
                isRunning = false;
                projectDCThread.join();
            }
            catch (InterruptedException e)
            {
                Logger.append("Exception thrown when attempting to join Thread '" + projectDCThread.getName() + "'");
                Logger.append(e);
            }
            finally
            {
                EventDispatcher.getInstance().dispatchEvent(new GameClosingEvent().setEventPhase(EventPhase.POST), EventPhase.POST);
            }
        }
    }

When you execute the join your thread. Is suspended, so the finally is not executed then. When projectDCThread dies execution of your code will continue and the finally should be executed

The program terminates before finally is executed though...

Let me guess ... Do you exit your program via a System.exit(0) before projecDCThread has died?

Nope, it executes perfectly up to when the thread joins. Then the program terminates because it exits the GameLoop, disposes everything it needs to, and then the stop() method gets fired, which it's supposed to. It just doesn't execute the finally block.

Simply exiting a loop won't terminate a program! We need more info. Why does the program exit after that loop - is the loop running on the only non-daemon thread? What is projectDCThread? How is projectDCThread terminated? On what thread do you call stop()? Do you have anywhere in your program a System.exit or Runtime.exit?

Basically the only way to prevent a finally from running is to call either of those exit methods, to interrupt or kill the thread on which the try was running, or to crash the runtime somehow.

Everything you should need:

public class ProjectDC implements Runnable, KeyListener, IEventListener
{
    private static ProjectDC instance;

    private boolean isRunning = false;
    private boolean safeCloseRequested = false;

    private ProjectDCWindow projectDCWindow;
    private GameStateManager projectDCGSMInstance;
    private Thread projectDCThread;

    protected ProjectDC()
    {
        instance = this;
        start();
    }

    @Override
    public void run()
    {
        EventDispatcher.init();

        EventDispatcher.getInstance().registerEventListener(this, new GameStartingEvent().setEventPhase(EventPhase.PRE));
        EventDispatcher.getInstance().registerEventListener(this, new GameStartingEvent().setEventPhase(EventPhase.INVOKE));
        EventDispatcher.getInstance().registerEventListener(this, new GameStartingEvent().setEventPhase(EventPhase.POST));
        EventDispatcher.getInstance().registerEventListener(this, new GameClosingEvent().setEventPhase(EventPhase.POST));

        EventDispatcher.getInstance().dispatchEvent(new GameStartingEvent().setEventPhase(EventPhase.PRE), EventPhase.PRE);
        EventDispatcher.getInstance().dispatchEvent(new GameStartingEvent().setEventPhase(EventPhase.INVOKE), EventPhase.INVOKE);
        EventDispatcher.getInstance().dispatchEvent(new GameStartingEvent().setEventPhase(EventPhase.POST), EventPhase.POST);

        long lastTime = System.nanoTime();
        double delta = 0.0;
        double ns = 1000000000.0 / 60.0;
        long timer = System.currentTimeMillis();
        int updates = 0;
        int frames = 0;

        while(isRunning && !safeCloseRequested)
        {
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;

            if (delta >= 1.0)
            {
                delta--;
                update(delta);
                updates++;
            }

            render();
            frames++;

            if (System.currentTimeMillis() - timer > 1000)
            {
                if(ProjectDCInfo.SHOULD_DEBUG_FPS)
                    System.out.println("Updates: " + updates + ", Frames: " + frames);

                timer += 1000;
                updates = 0;
                frames = 0;
            }
        }

        dispose();
        stop();
    }

    @Override
    public void handleEvent(Event event)
    {
        if(event instanceof GameStartingEvent)
        {
            if (event.getEventPhase() == EventPhase.PRE)
            {
                preInit();
            }
            else if (event.getEventPhase() == EventPhase.POST)
            {
                postInit();
            }
            else if (event.getEventPhase() == EventPhase.INVOKE)
            {
                init();
            }
        }
        else if(event instanceof GameClosingEvent)
        {
            if(event.getEventPhase() == EventPhase.POST)
                Logger.writeLog();
        }
    }

    private void dispose()
    {
        projectDCGSMInstance.dispose();

        Keys.writeKeys();
        Logger.writeLog(); // TODO: Remove this after GameClosingEvent.POST is handled CORRECTLY
    }

    public void requestClose()
    {
        EventDispatcher.getInstance().dispatchEvent(new GameClosingEvent().setEventPhase(EventPhase.PRE), EventPhase.PRE);
        EventDispatcher.getInstance().dispatchEvent(new GameClosingEvent().setEventPhase(EventPhase.INVOKE), EventPhase.INVOKE);
        Logger.append("ProjectDC: Close Requested, Preparing to stop GameLoop");
        safeCloseRequested = true;
    }

    public synchronized void start()
    {
        if(!isRunning)
        {
            isRunning = true;
            projectDCThread = new Thread(this, ProjectDCInfo.WINDOW_TITLE);
            projectDCThread.start();
        }
    }

    public synchronized void stop()
    {
        if(isRunning || safeCloseRequested)
        {
            try
            {
                isRunning = false;
                projectDCThread.join();
            }
            catch (InterruptedException e)
            {
                Logger.append("Exception thrown when attempting to join Thread '" + projectDCThread.getName() + "'");
                Logger.append(e);
            }
            finally
            {
                EventDispatcher.getInstance().dispatchEvent(new GameClosingEvent().setEventPhase(EventPhase.POST), EventPhase.POST);
            }
        }
    }
}

Simply calling a new ProjectDC() from the main method in another class (only method in the class, only statement in the method)

It looks to me like the main loop runs in a new Thread (projectDCThread), and you call stop() from that, so when stop() calls join, it's calling it on the same thread as the one it's running in. This means your thread waits until it terminates, which it never does because it's waiting. Because you never exit the join, you never get to the finally. If I'm right then that join is simply a mistake.

Ps one way to call System.exit is to use exit on close on your JFrame - that would close your app without the join deadlock ever being resolved.

I added an event listener for the window closing, and called requestClose which safely stops the program.

Can I do Future.get to get the result of the thread closing? Then call it if it actually did successfully stop?

I meant I already have a event listener which calls requestClose that safely stops the program when the JFrame is closed, in case I explained that wrong in my previous post.

I'm guessing that requestClose sets safeCloseRequeseted then the main loop exits? In which case you do have the thread problem I described above.

Yes, that's how it works. How would you solve it?

The join is pointless when it's always called on the current thread, do just delete it.
If it's possible that the close method could be called on a different thread, and the join is logically necessary, then test that the current thread is not projectDCThread before calling join.

You're working some strange hours! (My excuse is that I'm in Thailand this week)

I don't think I'll ever be adding anymore threads into this, so I think just deleting it will work.

Haha, I've been modding Minecraft with forge alot recently and I've missed programming from the ground up so I've started a new project that I can push myself through and learn even more from. It's gotten me really excited! I've been using Interfaces (something I almost never used), learning different systems (like an Event System), and just starting to work on algorithms.

The project I'm currently working on is a 2D Dungeon Crawler (hence ProjectDC) with 3D Dungeons. I can honestly say I've pushed myself further this time around than I have with any other Game I've started (and dropped haha), and I'm making quick progress too!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.