Crash in URLConnectionState::run() while app is shutting down

When an app / plug-in is shut down while a network request is still in progress, it can crash in URLConnectionState::run (juce_mac_Network.mm).

The crash (SIGABRT) occurs in this line.

Right before the crash, Objective-C logs:

objc[31049]: Attempt to use unknown class 0x600000cb4ab0.

The address seems to point to an array:

(lldb) p (id)0x600000cb4ab0
(__NSArrayM *) $3 = 0x0000600000cb4ab0 @"1 element"

…but maybe that doesn’t mean a lot because we’re already in undefined behaviour.

Call stack:

A fix would be appreciated! :slightly_smiling_face: Or any advice on what I can do in my code to prevent this.

Bump

Can you put together a stripped down example which reliably reproduces the crash?

Seeing a very similar crash with the same exception during our tests

objc[31049]: Attempt to use unknown class

Did you have any luck figuring it out so far?

Bump. Seeing similar issue here… when deleting a VST3 filter from the AudioPluginHost. JUCE 6.0.1.

How are you managing the network request? I can trigger the crash, but only if I do the request on a background thread which I deliberately leak on shutdown. In normal circumstances where stopThread() is called everything behaves as expected.

Hello @ed95,

I am inviting myself in this topic because I am experiencing somewhat of a similar crash with URLConnectionState when trying to shutdown my app.

Essentially I’m running a download and I want to prematurely cancel it if the user decides to shutdown the app while it’s running. So I reset the unique_ptr in which the juce::URL::DownloadTask is stored. That leads me to a SIGABRT in some assembly code.

Here’s is the stack:

The failing line in URLConnection state:

And a very simple version of the class doing the download:

Header

#pragma once
#include <JuceHeader.h>

#include <atomic>
#include <memory>

class ContentManager : public juce::URL::DownloadTask::Listener
{
public:
    ContentManager();
    ~ContentManager();

    void checkAndUpdate();
    void cancelUpdate();

    bool isUpdateRunning();
    float getProgress();

    void finished(juce::URL::DownloadTask*, bool success) override;
    void progress(juce::URL::DownloadTask*, juce::int64 bytesDownloaded, juce::int64 totalLength) override;

private:
    juce::URL m_url;
    std::unique_ptr<juce::URL::DownloadTask> m_task;

    std::atomic<bool> m_running = { false };
    std::atomic<float> m_progress = { 0.0 };
};

CPP

#include "ContentManager.h"

namespace
{
    static std::string s_downloadURL = "https://somefileontheinternet.zip";
}

ContentManager::ContentManager()
: m_url(s_downloadURL)
, m_task(nullptr)
{
}

ContentManager::~ContentManager()
{
    if (isUpdateRunning())
        cancelUpdate();
}

void ContentManager::cancelUpdate()
{
    m_task.reset();
    m_running = false;
    m_progress = 0;
}

bool ContentManager::isUpdateRunning()
{
    return m_running.load();
}

float ContentManager::getProgress()
{
    return m_progress.load();
}

void ContentManager::checkAndUpdate()
{
    if (isUpdateRunning())
        return;

    m_running = true;

    m_task = m_url.downloadToFile(
        juce::File("/Users/somewhere/Desktop/somefile.zip"), juce::String(), this);
}

void ContentManager::finished(juce::URL::DownloadTask*, bool success)
{
    m_running = false;
    m_task.reset();
}

void ContentManager::progress(juce::URL::DownloadTask*, juce::int64 bytesDownloaded, juce::int64 totalLength)
{
    m_progress = totalLength != 0 ? static_cast<float>(bytesDownloaded) / static_cast<float>(totalLength) : 0;
}

I’m getting this crash in 6.0.1. Resetting the unique_ptr from the message thread (like a cancel button on the UI) seems to work alright, just this one case gives me a headache. Am I missing something ? Would you be able to give me a hand on that ?

Thanks !

That crash looks unrelated to the Obj-C++ issues that the OP is seeing.

When you say it’s working deleting the URL::DownloadTask on the message thread, how are you deleting it in the crashing code? On a background thread? If you could post the full stack trace instead of the cropped first image, and some more complete example code which reproduces the crash then that will help to track things down.

As mentionned this happens when shutting down the app. So the call initiates from the shutdown method in the application base class.

Here’s the stack:

The application shutdown also happens on the message thread so it’s unlikely to be related to threading - it’s hard to say without seeing the full code but it looks like you might be having some lifetime issues with the shared_ptr in your app.

Try stripping it down to the bare minimum code in order to narrow down where the crash is coming from - this code for example shuts down cleanly:

#pragma once

#include <JuceHeader.h>

//==============================================================================
class MainComponent  : public juce::Component,
                       public juce::URL::DownloadTask::Listener
{
public:
    //==============================================================================
    MainComponent()
    {
        task = url.downloadToFile (juce::File::getSpecialLocation (juce::File::userDesktopDirectory).getChildFile ("download.zip"),
                                   {},
                                   this);
        jassert (task != nullptr);
        
        setSize (600, 400);

    }

    //==============================================================================
    void paint (juce::Graphics& g) override
    {
        // (Our component is opaque, so we must completely fill the background with a solid colour)
        g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
    }
    
    //==============================================================================
    void finished (juce::URL::DownloadTask*, bool success) override
    {
        DBG ("finished");
        jassert (success);
    }
    
    void progress (juce::URL::DownloadTask*, juce::int64 bytesDownloaded, juce::int64 totalLength) override
    {
        DBG ("progress = " + juce::String ((float) bytesDownloaded / totalLength));
    }

private:
    //==============================================================================
    // Your private member variables go here...
    juce::URL url { "https://somefileontheinternet.zip" };
    std::unique_ptr<juce::URL::DownloadTask> task;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};