Memory Leak in MessageManager::stopDispatchLoop() at line 122


#1

Hi,

I am using the JUCE v3.0.1 from github and found a small object is leaked in the following situation:

class UnitTestes : public JUCEApplication
{
public:
    UnitTestes() {}

    const String getApplicationName()       { return ProjectInfo::projectName; }
    const String getApplicationVersion()    { return ProjectInfo::versionString; }
    bool moreThanOneInstanceAllowed()       { return true; }

    void initialise (const String&)
    {
        JUCEApplication::getInstance()->quit();
    }

    void shutdown()
    {
    }
};

START_JUCE_APPLICATION (UnitTestes)

By stepping through the debugger, I found the leaked object is created in MessageManager::stopDispatchLoop() line 122

(new QuitMessage())->post();

Since MessageManager::MessageBase::post() will delete itself if MessageManager::instance->quitMessagePosted is true, so I currently fix this leak by swapping line 122 and line 123 in juce_MessageManager.cpp;

and MessageManager::stopDispatchLoop() now becomes

void MessageManager::stopDispatchLoop()
{
    quitMessagePosted = true;    // line 122
    (new QuitMessage())->post(); // line 123
}

Hope this helps.

Best regards.


#2

Erm.. no! That change would mean that the message is deleted immediately, and never sent at all!!

I guess this is Windows? And I guess you must be doing something strange in your shut-down procedure because I don't see that leak, and normally I'd expect your suggested change to have prevented your app from shutting down at all. Maybe you're terminating the app with std::exit or something before the message queue has a chance to get flushed?

Anyway, it's trivial and the leak won't do any actual harm, but thanks - I'll maybe add a static reference or something to avoid it.


#3

Thank you very much for your reply.

Actually I am developing a module using JUCE and I write an unit-testing application where its Main.cpp is identical as follow


// This is the actual code of my application's Main.cpp
#include "../JuceLibraryCode/JuceHeader.h"

//==============================================================================
class UnitTests : public JUCEApplication
{
public:
    UnitTests() {}
    const String getApplicationName()
    {
        return ProjectInfo::projectName;
    }
    const String getApplicationVersion()
    {
        return ProjectInfo::versionString;
    }
    bool moreThanOneInstanceAllowed()
    {
        return true;
    }
    void initialise (const String&)
    {
        UnitTestRunner runner;
        runner.runAllTests();
        JUCEApplication::quit();
    }
    void shutdown()
    {
    }
};
//==============================================================================
// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION (UnitTests)

Since my module and unit tests does not require any GUI, so I invoke JUCEApplication::quit() immediately after all tests have been run.

When the above program is built and run in VisualC++ 2010, all tests pass but _CrtSetDbgFlag() reports that an 8-bytes-object is leaked (it does no harm, but it would be nice if it can be eliminated :-> )

 

So I am wondering if the above program is not the recommanded way to write unit testings; if so, is there any samples which does standalone unit-testings without GUI? ( I have checked the offical demo in Demo\Source\Demos\UnitTestsDemo.cpp, however it is integrated into the JUCE demo framework)

Regards.


#4

Ok - I think the only reason this is happening is because the message-loop never gets to run, since you quit immediately.

TBH I'm not keen to add a workaround, because it's only going to happen in this very specific case, and isn't actually ever going to cause any problems.

What you could do would be to call MessageManager::getInstance()->runDispatchLoopUntil (50) after calling quit(), and that'd probably flush it out. Or if you're not actually using any GUI, why not just do it all as a command-line app anyway?


#5

Thanks again for your recommendations,  and I found that the utility in extras\binarybuilder is a good starting point for writing a console application. :-)


#6

I just came across this leak because I (in some cases) call JUCEApplicationBase::quit() from JUCEApplicationBase::initialise(). Adding the runDispatchLoopUntil(50) removed the leak.

Leaks like these can take quite some time to track down (at least for a newbie) - maybe it would be worth mentioning this behavior/fix in the documentation for JUCEApplicationBase::initialise() ? The doc explicitly says:

If during the initialise() method, the application decides not to start-up after all, it can just call the quit() method and the event loop won’t be run.

This (at least to me) indicates that it is acceptable to call quit() from initalise() and I wouldn’t expect memory leaks from acceptable code.


#7

This is an ancient thread - are you using the latest version of juce? I ask because in our own code we often call quit() during initialise() (e.g. the projucer itself sometimes does this) and haven’t noticed any leaks. Maybe you could offer a short example (a PIP?) that shows the problem, so we can debug it?


#8

First time PIP’ing… I’m using JUCE 5.3.0., I’m on Windows 7 using MSVC 2015.

/*******************************************************************************
 The block below describes the properties of this PIP. A PIP is a short snippet
 of code that can be read by the Projucer and used to generate a JUCE project.

 BEGIN_JUCE_PIP_METADATA

 name:             QuitLeak
 version:          1.0.0
 vendor:           birkedal
 website:          
 description:      Demonstrates memory leak when calling JUCEApplicationBase::quit() before message loop runs

 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
                   juce_gui_basics
 exporters:        xcode_mac, vs2017, vs2015, linux_make, xcode_iphone

 type:             Component
 mainClass:        QuitLeak

 useLocalCopy:     1

 END_JUCE_PIP_METADATA

*******************************************************************************/

#pragma once


//==============================================================================
class QuitLeak : public Component
{
public:
    //==============================================================================
    QuitLeak()
    {
        JUCEApplicationBase::quit();

        // Adding this line will remove the leak
        //MessageManager::getInstance()->runDispatchLoopUntil(50);
        
        setSize (600, 300);
    }

private:
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (QuitLeak)
};

#9

I see this in the Output:

Detected memory leaks!
Dumping objects ->
{185} normal block at 0x000000000010DCC0, 16 bytes long.
 Data: <   ?            > 88 DF C7 3F 01 00 00 00 01 00 00 00 CD CD CD CD 
Object dump complete.

#10

That’s not a juce object being leaked, it’s just a win32 message that was in flight and never got delivered before the message loop stopped. Nothing to worry about or fix.


#11

OK, nice to know!

I’ll keep the runDispatchLoopUntil(50) though - I’m a treat all leaks as errors kinda guy.


#12

That’s a good attitude in general, but running the message loop synchronously is always a very dangerous, deprecated, and on some platforms impossible thing to do. Whereas this leak is completely benign.


#13

So you would advise against the runDispatchLoopUntil approach?

What I would like to avoid is that a future developer (could be future me…) of the tool I’m developing spends time on tracking down this leak. If the leak is unavoidable (with clean code) I’ll leave a comment in my code and hope this is found before too much debugging time is spent.


#14

I’d advice against ever calling that method if possible!

Yeah, this is a leak that’s been around forever and there are dozens of forum threads about it. It’s totally harmless but the only way for us to avoid it would be to burden the event handling system with a mirrored copy of the list of messages that have been posted to the system queue, which just isn’t worth it.


#15

OK, thanks a lot for the clarification.

Off topic: JUCE itself is an awesome piece of work but what is even more awesome is the combination of JUCE and a forum where the maintainers take their time to (with close to zero latency) answer questions from the users. I’m impressed.