juce::URL::createInputStream() inside a lambda leak?

I’ve got the following code being used inside a lambda, that is being used with that new ThreadPool::addJob(std::function<>) that was added to the latest tip a couple days ago.

void AuthenticationInterface::initializeServerConnectionThread(ServerString& postData) {
    auto ai = this;
    juce::String post = postData.value();
    auto a = [ai, post]()
    {
        int statusCode = 0;
        juce::URL serverRequestURL = juce::URL(ServerConnection::GetChordieURL()).withPOSTData(post);
        juce::ScopedPointer<juce::InputStream> stream(serverRequestURL.createInputStream(true,
                                                                                         nullptr,
                                                                                         nullptr,
                                                                                         juce::String(),
                                                                                         20000,
                                                                                         //-1, //infinite timeout
                                                                                         nullptr,
                                                                                         &statusCode )
                                                      );
        juce::String reply = "FailedToConnect";
        bool result = false;
        if( stream != nullptr ) {
            reply = stream->readEntireStreamAsString();
            result = true;
        }
        ai->receiverCallback( reply );
        return result;
    };
    MatkatMusic::OneShotThreadPoolQueue::AddToQueue(a);
}
void OneShotThreadPoolQueue::AddToQueue(std::function<bool ()> lambda) //this is a static method
{
   auto instance = OneShotThreadPoolQueue::GetInstance();
//    auto job = new OneShotThreadPoolJob("new job", lambda);
//    instance->pool.addJob(job, true);
   struct LambdaJobWrapper : public juce::ThreadPoolJob
   {
       LambdaJobWrapper(std::function<bool()> j) : juce::ThreadPoolJob("lambda"), job(j) {}
       juce::ThreadPoolJob::JobStatus runJob() override
       {
           if( shouldExit() )
               return juce::ThreadPoolJob::JobStatus::jobNeedsRunningAgain;

           if( job() )
           {
               DBG( "LambdaJobWrapper::runJob() result: OK" );
           }
           else
           {
               DBG( "LambdaJobWrapper::runJob() result: FAIL" );
           }
           return juce::ThreadPoolJob::JobStatus::jobHasFinished;
       }
       std::function<bool()> job;
   };
   instance->pool.addJob(new LambdaJobWrapper(lambda), true);
}

I can’t seem to figure out why i’m getting a leak, other than that the ScopedPointer in the lambda never gets released. But i’m just guessing when I say that. When I profile, this is what I see:

any ideas what might be causing the leak?

Are you sure it’s just the InputStream that’s leaking? Can you press continue a few times and see if there’s any other objects which may also be leaking?

One immediate thing that does jump out is the round-about way of capturing the this pointer. Unless you’re absolutely certain this object will live for the duration of your whole app this is likely to be problematic.
As a general rule, you should never capture this pointers unless this owns the thing that’s doing the capturing (this this is out of the question for async or threaded code).

I’m absolutely certain that AuthenticationInterface object will live for the duration of my whole app. it’s the content Component for my app.

I let it run for a few more minutes, and it’s always that InputStream. I’ll make a test case and report back.

Sorry, I don’t mean let it run, if you press the continue button you’ll either get more leak messages in the console or your app will exit.

I’m not getting leak messages tho. I just noticed that the memory usage kept increasing as the app ran, and the Profiler reflects the memory leak accordingly…

ok, here’s the test code for you juce devs. @dave96 @jules @fabian @ed95 no leak messages, but the memory usage keeps increasing. This should be a fun bug to solve!

Make a generic GUI project, and put this in MainComponent.h:

class MainContentComponent   : public Component, public Timer
{
public:
    //==============================================================================
    MainContentComponent();
    ~MainContentComponent() { stopTimer(); }

    void paint (Graphics&) override;
    void resized() override {}
    void timerCallback() override;
    void addJob(std::function<bool()> lambda);
private:
    ThreadPool pool;
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

put this in MainComponent.cpp:

MainContentComponent::MainContentComponent()
{
    setSize (600, 400);
    startTimer(1000 * 3);
}

void MainContentComponent::paint (Graphics& g)
{
    g.fillAll (Colour (0xff001F36));

    g.setFont (Font (16.0f));
    g.setColour (Colours::white);
    g.drawText ("Hello World!", getLocalBounds(), Justification::centred, true);
}


void MainContentComponent::timerCallback()
{
    auto a = []()
    {
        int statusCode = 0;
        juce::URL serverRequestURL = juce::URL("https://www.google.com/search?q=what+is+the+area+of+a+square&rlz=1C5CHFA_enUS699US702&oq=what+is+the+area+of+a+square&aqs=chrome.0.0l6.9325j0j8&sourceid=chrome&ie=UTF-8");
        ScopedPointer<InputStream> stream( serverRequestURL.createInputStream(true,
                                                          nullptr,
                                                          nullptr,
                                                          juce::String(),
                                                          20000,
                                                          //-1, //infinite timeout
                                                          nullptr,
                                                          &statusCode )
                                          );
        juce::String reply = "FailedToConnect";
        bool result = false;
        if( stream != nullptr ) {
            reply = stream->readEntireStreamAsString();
            result = true;
        }
        return result;
    };
    addJob(a);
}

void MainContentComponent::addJob(std::function<bool ()> lambda)
{
    struct LambdaJobWrapper : public juce::ThreadPoolJob
    {
        LambdaJobWrapper(std::function<bool()> j) : juce::ThreadPoolJob("lambda"), job(j) {}
        juce::ThreadPoolJob::JobStatus runJob() override
        {
            if( shouldExit() )
                return juce::ThreadPoolJob::JobStatus::jobNeedsRunningAgain;

            if( job() )
            {
                DBG( "LambdaJobWrapper::runJob() result: OK" );
            }
            else
            {
                DBG( "LambdaJobWrapper::runJob() result: FAIL" );
            }
            return juce::ThreadPoolJob::JobStatus::jobHasFinished;
        }
        std::function<bool()> job;
    };

    pool.addJob(new LambdaJobWrapper(lambda), true);
}

just want to add that I’m using Juce 4.3.1 here

just wanted to add that @Xenakios helped me out in the IRC channel for JUCE and this appears to be specific to OS X. Windows doesn’t have the same problem. I let it run for about 20 minutes and the memory usage climbed from 15mb to 115mb. It’s possible that it’s not a leak, just the heap for the app growing, perhaps. but that is way beyond my expertise and ability to figure out why.