AAX setStateInformation ~AsyncUpdater issue


#1

So this is a crash I’m seeing with my AAX build, but no other… and is new to AAX 2.x

If you create a new Introjucer AAX plugin and add a quick Slider class:

MyFader.h

#include "../JuceLibraryCode/JuceHeader.h"

#ifndef TestPI_MyFader_h
#define TestPI_MyFader_h

class CMyFader : public Slider
{
public:
        CMyFader(const String& componentName);
        ~CMyFader();
    
private:
    
        int iValue;
};

#endif

MyFader.cpp

#include "MyFader.h"

CMyFader::CMyFader(const String& componentName) : Slider(componentName)
{

    setSize(50, 198);
    
    setRange (0, 100, 1);

    setTextBoxStyle (Slider::NoTextBox, false, 98, 12);    // Assertion occurs here so lines below don't matter

    setSliderSnapsToMousePosition(false);
    
    setTopLeftPosition(12, 10);
}

CMyFader::~CMyFader()
{
    
}

In PluginProcessor.h add

private:
    //==============================================================================
    
    ScopedPointer<CMyFader>         m_pFader;

In PluginProcessor.cpp add

void TestPiAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
    // You should use this method to restore your parameters from this memory block,
    // whose contents will have been created by the getStateInformation() call.
    
    DBG("TestPiAudioProcessor::setStateInformation")
    
    m_pFader = new CMyFader("test");
    
}

If you now save a session with the test plugin instantiated, then close and reopen the session you get an assertion:

JUCE Assertion failure in juce_AsyncUpdater.cpp:57

AsyncUpdater::~AsyncUpdater()
{
    // You're deleting this object with a background thread while there's an update
    // pending on the main event thread - that's pretty dodgy threading, as the callback could
    // happen after this destructor has finished. You should either use a MessageManagerLock while
    // deleting this object, or find some other way to avoid such a race condition.
    jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager());

    message->shouldDeliver.set (0);
}

I traced it to juce_Slider.cpp in lookAndFeelChanged() - line 586

valueBox = nullptr;

Anyone else see this or something similar?

Thanks,

Rail


#2

What’s the stack trace when it asserts? Sounds like maybe the host is deleting the plugin on a background thread…


#3

Hi Jules,

Don’t think so…

Here’s the stack trace (video): http://screencast.com/t/cCPcbApJxO

[attachment=0]juce_aax_trace.png[/attachment]

Here’s the test IntroJucer generated project: TestPI.zip

Thanks,

Rail


#4

So you’re creating a UI component in your setStateInformation callback? That could be called on any thread!


#5

In getStateInformation() I save how many tracks I have, the track types, etc…

In setStateInformation() I recreate the tracks by adding components into my:

OwnedArray TrackArry;

through something like:

case monotrack:     // DBG("Add Mono Track")
                                TrackArry.add(new CMonoTrack(iIndex));
                                break;

So you’re saying we shouldn’t do this in setStateInformation()… which seems to work for all other plugin formats… where would you suggest I do this otherwise? Create my own threaded static method to create the tracks?

Thanks,

Rail


#6

setStateInformation is not a GUI method - it could be called from any thread, and there may not even be a GUI active when it’s called. You may have been lucky that other hosts happened to mostly use the message thread to call it, but you can’t just make assumptions like that!


#7

Thanks - all fixed and working.

Cheers,

Rail