Calling quit() inside shutdown() introduces crash on macOS


#1

Hi JUCE Team,

My code is calling JUCEApplicationBase::quit() during JUCEApplication::shutdown(), and this is introducing crashes and leakages (of OwnedArray) on macOS. Obviously, this is something you shouldn’t really (have to) do, but because some classes in our system get destructed inside JUCEApplication::shutdown() and they call quit() in any case, it does happen. The crash is simple to reproduce: just create a new GUI application project and add a single quit() inside shutdown().

Proposed solutions:

  1. Either have quit() do nothing if already shutting down (by way of a boolean or something).
  2. Mention in a comment you shouldn’t do this. Right now there’s no caution whatsoever.

Kind regards,

Stijn
Moditone


#2

That’s not how you’re supposed to shut down your application. and the code generated by ProJucer whenever you create a new application creates all the proper initialization and shutdown code that you’ll ever need to follow. There’s even a big ol’ example in the documentation for JuceApplication. It seems as though you guys missed all of that if you’re trying to roll your own shutdown scheme without following the JUCE template:

https://juce.com/doc/classJUCEApplication#details

class MyJUCEApp  : public JUCEApplication
{
public:
    MyJUCEApp()  {}
    ~MyJUCEApp() {}
    void initialise (const String& commandLine) override
    {
        myMainWindow = new MyApplicationWindow();
        myMainWindow->setBounds (100, 100, 400, 500);
        myMainWindow->setVisible (true);
    }
    void shutdown() override
    {
        myMainWindow = nullptr;
    }
    const String getApplicationName() override
    {
        return "Super JUCE-o-matic";
    }
    const String getApplicationVersion() override
    {
        return "1.0";
    }
    void systemRequestedQuit() override
    {
        // This is called when the app is being asked to quit: you can ignore this
        // request and let the app carry on running, or call quit() to allow the app to close.
        quit();
    }
private:
    ScopedPointer<MyApplicationWindow> myMainWindow;
};
// this generates boilerplate code to launch our app class:
START_JUCE_APPLICATION (MyJUCEApp)

https://juce.com/doc/classJUCEApplication#a8e9b08200268216a791326635ec9321c

It would probably be a good idea for your team to create a default JUCE application, and then inspect the call hierarchy for shutdown() and quit() in the auto-generated Main.cpp


#3

Matkat, thanks for your input, though we’re perfectly aware of how the shutdown mechanism works in Juce (been working with it for several years). The problem is that we’re destructing classes in shutdown() that could possibly trigger quit() indirectly. Sometimes they’re destructed while still running the app and should trigger shutdown. It’s not necessarily a matter of rolling our own shutdown scheme, and more of behaviour of the app.

If there were something like juce::JUCEApplicationBase::isShuttingDown() the whole issue could be averted. Either way, I think it’s safer for quit() to do nothing while already shutting down, or at least jassert, throw or document that this shouldn’t happen. Crashing and burning is always the worst choice, imho. We can easily work around the issue, the post is more of a bug/feature report.


#4

You could use JUCEApplicationBase::systemRequestedQuit().


#5

There’s also MessageManager::hasStopMessageBeenSent()


#6

Right, thanks guys!

@Fabian: Erhm…how would you use that? I need a way of knowing whether shutdown has already been initialized, but isShuttingDown() doesn’t return anything.

@jules: Yes, I think this is what I need! Cool, thanks!


#7

Yes ignore my comment :-). I meant hasStopMessageBeenSent.