MessageManagerLock and addAndMakeVisible in Thread


#1

So I have a “Dialog” Component I show while loading a preset in my Plug-In… The Preset Loading is done in a Thread so when it starts loading it needs to create and show the Recall Dialog Component… so I use a MessageManagerLock as follows:

void CPlayerAudioProcessorEditor::showRecallDialog()
{
    Logger::writeToLog ("showRecallDialog");

    m_pRecallDlg = nullptr;

    const int iWidth = getWidth();

    const MessageManagerLock mml (Thread::getCurrentThread());

    if (mml.lockWasGained())
        {
        Logger::writeToLog ("Message thread lock gained.");
    
        m_pRecallDlg = new CRecallDlg (m_pProcessor);
    
        m_pRecallDlg->setBounds (20, g_iToolbarHeight - 12, iWidth - 40, getHeight() - g_iToolbarHeight + 12);
    
        addAndMakeVisible (m_pRecallDlg);
        }
    else
        {
        Logger::writeToLog ("Message thread lock NOT gained.");  // For debugging
    
        m_pRecallDlg = new CRecallDlg (m_pProcessor);
    
        m_pRecallDlg->setBounds (20, TOOLBARHEIGHT - 12, iWidth - 40, getHeight() - TOOLBARHEIGHT + 12);
    
        addAndMakeVisible (m_pRecallDlg);
        }

    m_ResizeLimits.setSizeLimits (iWidth, MAINWINHEIGHT, iWidth, MAINWINHEIGHT);
}

This works fine on MacOS and most of the time in Release in VST on Windows… but there is a good percentage of the time on Windows and 100% of the time on Windows as AAX that the Recall Dialog Component doesn’t appear.

I’ve tried an ASyncUpdater to trigger a repaint and setVisible on m_pRecallDlg but that doesn’t work. The logging shows that the lock is gained…

Since the Recall Dialog has a ProgressBar I’d like the “dialog” to be created synchronously.

Before I switched to a Thread for loading the preset I was using

MessageManager::getInstance()->runDispatchLoopUntil (400);

but I thought switching to a Thread would be better and I can’t use runDispatchLoopUntil() in my own Thread even if I have a MessageManagerLock.

Any ideas?

Thanks,

Rail


#2

So I tried changing the runDispatchLoopUntil code:

bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
{
    // jassert (isThisTheMessageThread()); // must only be called by the message thread

    if (! currentThreadHasLockedMessageManager())
        {
        jassert(isThisTheMessageThread()); // must only be called by the message thread
        }

     const int64 endTime = Time::currentTimeMillis() + millisecondsToRunFor;

    while (! quitMessageReceived)
    {
        JUCE_TRY
        {
            if (! dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0))
                 Thread::sleep (1);
        }
        JUCE_CATCH_EXCEPTION

        if (millisecondsToRunFor >= 0 && Time::currentTimeMillis() >= endTime)
            break;
    }

    return ! quitMessageReceived;
}

and changed my code to:

const MessageManagerLock mml (Thread::getCurrentThread());

if (mml.lockWasGained())
    {
    CFileLogger::writeToLog ("Message thread lock gained.");
    
    m_pRecallDlg = new CRecallDlg (m_pProcessor);
    
    m_pRecallDlg->setBounds (20, TOOLBARHEIGHT - 12, iWidth - 40, getHeight() - TOOLBARHEIGHT + 12);
    
    addAndMakeVisible (m_pRecallDlg);

    MessageManager::getInstance()->runDispatchLoopUntil(400);
    }

and that still doesn’t show the “dialog” on Windows (AAX).

Rail


#3

IIRC Windows is a bit finicky about the thread you use to create a HWND, I think it really needs to be the message thread. You could use the MessageManager::callFunctionOnMessageThread() method, but really the only truly safe way to do this is to send a message that causes the message thread to show an alert window, then make your thread wait until the window is dismissed and you send some kind of flag back to the thread that lets it continue.


#4

Thanks Jules,

Let me look into the overhead of having the Component exist for the lifetime off the plugin and try setting it’s visibility instead of dynamically creating it.

Cheers,

Rail


#5

That’d also work, though again I’d recommend avoiding the MessageManagerLock and instead posting some kind of message to the component to tell it to change its visibility. Locking against the message thread is a dangerous business, you can easily create non-obvious deadlocks!


#6

Thanks - Yeah I’m going to first try using AsyncUpdater to change the visibility

BTW - What are your thoughts about changing the code in MessageManager::runDispatchLoopUntil() to:

bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
{
    // jassert (isThisTheMessageThread()); // must only be called by the message thread

    if (! currentThreadHasLockedMessageManager())
        {
        jassert(isThisTheMessageThread()); // must only be called by the message thread
        }

: 
}

Cheers,

Rail


#7

No - the loop really must never be run by a different thread, as it’d lead to some horrible edge case race conditions.

And even running a modal loop on the message thread is a bad idea - you should only use it as an absolute last resort if there’s really no other conceivable way of doing something. And given that it’s impossible to do it on Android and should never be used in a plugin, I’m tempted to deprecate it one day…


#8

Cool – I only use runDispatchLoopUntil() on shutdown for Pro Tools… which needs the extra time for cleanup… and that’s always been on the MessageThread… Just figured while we were on the topic I’d ask.

Cheers,

Rail


#9

Yeah, that’s the kind of nasty situation where it’s a necessary hack, but for normal coding it’s best avoided.