runDispatchLoop() error on plugin creation

Hi, I have this problem with lv2 format and with some plugin (that seems takes a lot to be loaded): App crash at line 337 of juce_MessageManager_mac.mm ( [NSApp run]; inside void MessageManager::runDispatchLoop()).

I’m not sure, but I think the problem is that plugin isn’t yet ready to perform processBlock, what do you think about, I’m wrong ? If my supposition is true, There’s a way to know when plugin is ready to perform it ?

Could you possibly provide a minimal example of the issue you’re seeing?

1 Like

sure, this is my func “create new node”:

void NodeGraphProcessor::createNewNode (const PluginDescriptionAndPreference& description,
                                        const Point<float>* position0to1,
                                        UndoManager* undoManager,
                                        Uuid uuid,
                                        std::function<void(AudioProcessorGraph::Node::Ptr)> callBeforeListeners)
{
    Point<float> pos = (position0to1) ? *position0to1 : Point<float>{ Random::getSystemRandom().nextFloat(),
                                                                      Random::getSystemRandom().nextFloat() };

    //===========================================================
    
    std::function<void(AudioProcessorGraph::Node::Ptr)> callback = [this, pos, uuid, callBeforeListeners] (AudioProcessorGraph::Node::Ptr node) {
                
        node->properties.set (ayra::IDs::uuid, uuid.toDashedString());
        
        if (callBeforeListeners)    { callBeforeListeners(node); }
        
        if (AudioPluginInstance* instance = dynamic_cast<AudioPluginInstance*>(node->getProcessor()))
        {
            instance->addHostedParameter(std::make_unique<AudioParameterBool>( ParameterID { "shutdown node",  1 }, "shutdown node", false));
            instance->addHostedParameter(std::make_unique<AudioParameterBool>( ParameterID { "bypass node",    2 }, "bypass node",   false));
            instance->addHostedParameter(std::make_unique<AudioParameterBool>( ParameterID { "editor open",    3 }, "editor open",   false));

            instance->addListener(this);
        }
        
        needUpdatePlayhead();
        updateEditorComponentsAsync();
                        
        for (ayra::NodeGraphProcessor::Listener* listener : graphListeners) { listener->onNodeCreated(this, node); }
    };
 
    //===========================================================
    
    if (!undoManager)
    {
        if (isGraphInitialized()) 
        {
            performInGraphShutdown([this, description, pos, callback]() {
                pluginsManager.createNewPlugin(description, graph, pos, callback);
            });
        } else {
            pluginsManager.createNewPlugin(description, graph, pos, callback);

        }

    } else {
        undoManager->perform (new CreateOrRemoveNodeAction (*this, description,
                                                            pos, uuid,
                                                            callBeforeListeners,
                                                            false));
    }
}

That calls this one:

void PluginsManager::createNewPlugin (const PluginDescriptionAndPreference& description,
                                      AudioProcessorGraph& graph, Point<float> position,
                                      std::function<void (AudioProcessorGraph::Node::Ptr)> callback)
{
    std::shared_ptr<ScopedDPIAwarenessDisabler> dpiDisabler = makeDPIAwarenessDisablerForPlugin(description.pluginDescription);
    
    formatManager.createPluginInstanceAsync (description.pluginDescription,
                                             graph.getSampleRate(),
                                             graph.getBlockSize(),
                                             [this, &graph, position, dpiDisabler, useARA = description.useARA, callback] (std::unique_ptr<AudioPluginInstance> instance, const String& error)
                                             {
                                                addPluginCallback (std::move (instance), graph, error, position, useARA, callback);
                                             });
}

That calls this one:

void PluginsManager::addPluginCallback (std::unique_ptr<AudioPluginInstance> instance,
                                        AudioProcessorGraph& graph,
                                        const String& error,
                                        Point<float> pos,
                                        PluginDescriptionAndPreference::UseARA useARA,
                                        std::function<void(AudioProcessorGraph::Node::Ptr)> callback)
{
    if (instance == nullptr)
    {
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
                                          TRANS("Couldn't create plugin"),
                                          error);
    }
    else
    {
       #if JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX
        if (useARA == PluginDescriptionAndPreference::UseARA::yes
            && instance->getPluginDescription().hasARAExtension)
        {
            instance = std::make_unique<ARAPluginInstanceWrapper> (std::move (instance));
        }
       #endif

        instance->enableAllBuses();
        
        if (AudioProcessorGraph::Node::Ptr node = graph.addNode (std::move (instance)))
        {
            AudioProcessor* processor = node->getProcessor();
            
            if (!processor)
            {
                AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
                                                                TRANS("Couldn't find processor for node selected"),
                                                                "Couldn't find processor for node selected");
                return;
            }
            
            node->properties.set (ayra::IDs::name, processor->getName());
            node->properties.set (ayra::IDs::uid,  (int)node->nodeID.uid);
            node->properties.set (ayra::IDs::shutdown, processor->isSuspended());
            node->properties.set (ayra::IDs::bypass, node->isBypassed());
            node->properties.set (ayra::IDs::posX, pos.x);
            node->properties.set (ayra::IDs::posY, pos.y);
            Component* editor      = processor->getActiveEditor();
            DocumentWindow* window = getParentWindowForComponent(editor);
            node->properties.set (ayra::IDs::editorOpened, (bool)editor);
            node->properties.set (ayra::IDs::windowBounds, ((bool)window) ? window->getWindowStateAsString() : "");
            node->properties.set (ayra::IDs::useARA, useARA == PluginDescriptionAndPreference::UseARA::yes);
            
            if (callback) { callback(node); }
            sendActionMessage(ayra::Messages::pluginAdded);
        }
    }
}

(I reused some code from AudioPluginHost)

I’m not sure I completely follow what’s going wrong here, unfortunately without a clear example I can run for myself it’s impossible to guess. I take it you’re trying to host LV2 plugins? Are you seeing the same issue in the AudioPluginHost?

You could try enabling JUCE_CATCH_UNHANDLED_EXCEPTIONS to see if any exceptions are thrown? When you say it crashes, what sort of crash is it? can you step through and see what you are doing directly before the crash?

1 Like

AudioPluginHost fails with the same exception, here an image of where it fails (I tried to load plugdata lv2).

1 Like

OK thanks I’ll see if I can reproduce the issue locally.

1 Like

Does it load successfully in other LV2 hosts, e.g. Reaper?

1 Like

I don’t have reaper, but I’ll try.
An important thing is that if I suspend process of graph that will contains plugdata lv2 before loading plugdata lv2 and after (manually) I turn on graph again all works fine

Any news about it? :blush:

Did you test the plugin in Reaper?

1 Like

yes, in reaper all works fine

I tried to work around it and I suppose that problem Is in create plugins async, I addad to audiopluginhost a button that suspend/enable graph process.

In Audiopluginhost plugins are created async while in createNodeFromXml are creating throw auto instance = formatManager.createPluginInstance (description.pluginDescription,[…].
At this point, to load the plugins without crashes I need to call graph.suspendProcess(true), load plugin and then set graph.suspendProcess (false). => Saving project / close audiopluginhost / and loading it, graph will call createNodeFromXml and plugdata.lv2 is loaded fine.

Now… I can’t understand why creating it async rise this problem…

Yesterday I tried and it fails also with the sync method, so I should suppose it’random… To solve I suspendProcess of graph before the plugin creation and after a delay (not less than 200 or it crashs) I turn on again process, but loading more instances in parallel (this because I create my plugin in various thread) fails randomly also with this method… Any news about it? :upside_down_face:

Have you tried building your program and/or AudioPluginHost with address sanitizer enabled? There are instructions to enable address sanitizer here.

This might provide some more details about what’s causing the failure.

1 Like

I tried but it doesn’t gives me any useful suggestion… So… I tried to load other lv2 on my pc different from plug data and they work, so I think it’s a problem of plugdata, I’ll write an email to manufacturers

The problem here is that AudioPluginHost seems to quickly load/unload the plugin twice when you create it. There was a bug in plugdata that caused a crash in that situation. This is now fixed on the develop branch.

2 Likes

Really thank you :heart::pray: