JUCE 4.1: Slider popup window crashes Logic Pro X

Using Juce 4.1 / XCode 7.2.

Try this: create a simple GUI with a slider that has a popup menu with setPopupMenuEnabled(true).

Now open the plugin GUI in Logic Pro X, right-click on the slider to bring up the popup menu, and go directly to the Logic Pro X effects menu to close the GUI while the popup is still visible -> Logic X crashes.

 

Hi djb,

I can't reproduce your bug on my Mac. I've created a new plug-in project from the Introjucer wizard and simply added a slider:

PluginTestAudioProcessorEditor::PluginTestAudioProcessorEditor (PluginTestAudioProcessor& p)
     : AudioProcessorEditor (&p), processor (p), slider (Slider::LinearHorizontal, Slider::NoTextBox)
{
    slider.setPopupMenuEnabled (true);
    addAndMakeVisible (&slider);

    // Make sure that before the constructor has finished, you've set the
    // editor's size to whatever you need it to be.
    setSize (400, 300);
}

I've followed your steps by right-clicking on the popup and then closing the plug-in editor by selecting "No Plug-In" from the effects menu. Everything works as expected:

 

Am I doing everything correctly? Can you give me a stack trace of the crash?

Hi Fabian,

 

Thanks for looking into this! The process you describe works fine here too. The crash occurs when keeping the plugin in the effects rack, but only disable the GUI. This can be done by hovering over the effects rack, and and click on the middle section which changes its icon into this (see attached picture).

Relevant section of the crash report is pasted below. Please let me know if you need any further information on this.

FYI I have been using a rotary slider so the popup menu has an additional submenu for the rotary slider mode.

 

Process:               Logic Pro X [517]

Path:                  /Applications/Logic Pro X.app/Contents/MacOS/Logic Pro X

Identifier:            com.apple.logic10

Version:               10.2.0 (3987.29)

Build Info:            MALogic-3987029000000000~2

App Item ID:           634148309

App External ID:       812006144

Code Type:             X86-64 (Native)

Parent Process:        ??? [1]

Responsible:           Logic Pro X [517]

User ID:               501

 

PlugIn Path:             /Users/USER/Library/Audio/Plug-Ins/Components/Plugin.component/Contents/MacOS/Plugin

PlugIn Identifier:       com.company.Plugin

PlugIn Version:          1.0.0 (1.0.0)

 

Date/Time:             2016-01-05 06:20:08.391 +1100

OS Version:            Mac OS X 10.11.2 (15C50)

Report Version:        11

Anonymous UUID:        4E3122BB-D744-F87C-BCAE-EEC4580CE632

 

 

Time Awake Since Boot: 450 seconds

 

System Integrity Protection: enabled

 

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

 

Exception Type:        EXC_CRASH (SIGABRT)

Exception Codes:       0x0000000000000000, 0x0000000000000000

Exception Note:        EXC_CORPSE_NOTIFY

 

Application Specific Information:

abort() called

Pure virtual function called!

 

Global Trace Buffer (reverse chronological seconds):

299.213013   CFNetwork                     0x00007fff9f278303 TCP Conn 0x7fe39e97ab50 complete. fd: 22, err: 0

299.213189   CFNetwork                     0x00007fff9f306b1d TCP Conn 0x7fe39e97ab50 event 1. err: 0

 

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread

0   libsystem_kernel.dylib            0x00007fff8bd21002 __pthread_kill + 10

1   libsystem_pthread.dylib           0x00007fff9ffeb5c5 pthread_kill + 90

2   libsystem_c.dylib                 0x00007fff9a03f6e7 abort + 129

3   libc++abi.dylib                   0x00007fff9642cf81 abort_message + 257

4   libc++abi.dylib                   0x00007fff964514dc __cxa_pure_virtual + 18

5   com.company.Plugin       0x0000000137c7a4c9 juce::PopupMenu::HelperClasses::MenuWindow::paint(juce::Graphics&) + 153

6   com.company.Plugin       0x0000000137c060dd juce::Component::paintComponentAndChildren(juce::Graphics&) + 157

7   com.company.Plugin       0x0000000137c57ebe juce::ComponentPeer::handlePaint(juce::LowLevelGraphicsContext&) + 254

8   com.company.Plugin       0x0000000137c6d789 juce::JuceNSViewClass::drawRect(objc_object*, objc_selector*, CGRect) + 489

9   com.apple.AppKit                  0x00007fff8a7e5bd6 -[NSView _drawRect:clip:] + 3550

10  com.apple.AppKit                  0x00007fff8a7e2d21 -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 3136

11  com.apple.AppKit                  0x00007fff8a7e37b1 -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 5840

12  com.apple.AppKit                  0x00007fff8a934d84 -[NSNextStepFrame _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 284

13  com.apple.AppKit                  0x00007fff8a7de1ef -[NSView _displayRectIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:] + 2449

14  com.apple.AppKit                  0x00007fff8a798971 -[NSView displayIfNeeded] + 1950

15  com.apple.AppKit                  0x00007fff8a79819c -[NSWindow displayIfNeeded] + 232

16  com.apple.AppKit                  0x00007fff8af146da ___NSWindowGetDisplayCycleObserver_block_invoke6367 + 476

17  com.apple.AppKit                  0x00007fff8ab5b0d6 __37+[NSDisplayCycle currentDisplayCycle]_block_invoke + 738

18  com.apple.QuartzCore              0x00007fff9b596b5d CA::Transaction::run_commit_handlers(CATransactionPhase) + 85

19  com.apple.QuartzCore              0x00007fff9b430c7c CA::Context::commit_transaction(CA::Transaction*) + 160

20  com.apple.QuartzCore              0x00007fff9b430a24 CA::Transaction::commit() + 508

21  com.apple.QuartzCore              0x00007fff9b43f917 CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 71

22  com.apple.CoreFoundation          0x00007fff8ccbce37 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23

23  com.apple.CoreFoundation          0x00007fff8ccbcda7 __CFRunLoopDoObservers + 391

24  com.apple.CoreFoundation          0x00007fff8ccae358 CFRunLoopRunSpecific + 328

25  com.apple.HIToolbox               0x00007fff92e63935 RunCurrentEventLoopInMode + 235

26  com.apple.HIToolbox               0x00007fff92e6376f ReceiveNextEventCommon + 432

27  com.apple.HIToolbox               0x00007fff92e635af _BlockUntilNextEventMatchingListInModeWithFilter + 71

28  com.apple.AppKit                  0x00007fff8a7950ee _DPSNextEvent + 1067

29  com.apple.AppKit                  0x00007fff8ab61943 -[NSApplication _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 454

30  com.apple.AppKit                  0x00007fff8a78afc8 -[NSApplication run] + 682

31  com.apple.AppKit                  0x00007fff8a70d520 NSApplicationMain + 1176

32  com.apple.logic10                 0x000000010bba901e 0x10b513000 + 6905886

33  libdyld.dylib                     0x00007fff8dcb05ad start + 1

After some more experiments with a new project, and adding the slider in the same way as you did (as member instead of ScopedPointer<Slider> slider in the pluginEditor.h file) I don't see the crash, but instead, the popup menu remains on screen even after closing the GUI (while keeping the plugin in the rack) and remains active. I guess it would be nicer if the popup menu would be destroyed together with the GUI, but at least no crash.

Perhaps there is something specific with my lookAndFeel stuff that creates the crash - I'll investigate further.

Have you tried putting PopupMenu::dismissAllActiveMenus() in your destructor?

static bool JUCE_CALLTYPE PopupMenu::dismissAllActiveMenus()

Rail

Thanks for the suggestion! That seems to work in some hosts but not all.

For example, in Wavelab 8 Elements, when right-clicking on the slider to bring up the popup menu, and subsequently closing the GUI (but keeping the plugin) in the effects rack, even with a PopupMenu::dismissAllActiveMenus(); call in the editor destructor, the GUI itself dissapears, but the popup menu remains on screen. This is with the latest tip, and creating an empty project, with the following editor:

NewProjectAudioProcessorEditor::NewProjectAudioProcessorEditor (NewProjectAudioProcessor& p)
    : AudioProcessorEditor (&p), processor (p), slider (Slider::Rotary, Slider::NoTextBox)

{
    slider.setPopupMenuEnabled (true);
    addAndMakeVisible (&slider);

    setSize (400, 300);
}


NewProjectAudioProcessorEditor::~NewProjectAudioProcessorEditor()
{
    PopupMenu::dismissAllActiveMenus();
}

See attached screenshot for the remaining parts of the popup menu in Wavelab 8 elements when the GUI has been closed. I get similar things in Logic Pro X, and also in some hosts for Windows. Seems that the popup menu is not properly removed when the editor is closed but the plugin remains active (?)

Another issue with the popup window is the following (Wavelab 8): bring up the popup window of a slider, then move the mouse to the title bar and drag the GUI to a different place, and move the mouse over the popup menu. In that case, the popup menu responds to the mouse hover, while we're actually dragging the GUI underneath the popup menu!

 

Hi djb,

I don't have Wavelab but I still can't reproduce this in Logic. I've now used a ScopedPointer and a rotary slider but Logic does not crash nor does the plug-in window stay visible. I've also kept the effect in the effect rack as you mentioned. 

I don't think that PopupMenu::dismissAllActiveMenus(); should really be necessary. 

Can anybody else reproduce this bug?

Fabian

Hi Fabian,

Thanks for looking into this and my apologies for making it difficult to reproduce it.

I'm having difficulty reproducing the crash in other projects, but the "popup menu does not dissapear when closing the GUI" in Logic is easy to reproduce with the Juce demo plugin. Open that project, add ->setPopupMenuEnabled(true); to both sliders before the addAndMakeVisible(slider) statement, then open the plugin in Logic Pro X, activate the popup menu, and close the GUI from the effects section: now the popup remains on screen and remains active until one of the options is selected.

A second way to get the same result is to enable the popup menu from one of the sliders, and then switch the editor mode to 'Controls' in the upper-right corner (see result in the attached screenshot).

Adding PopupMenu::dismissAllActiveMenus() to the demo plugin editor destructor removes the popup when closing the GUI.

I can't reproduce the crash I observed earlier with the demo project or a new project, so likely there are some other dependencies in my own project - need to investigate further. However those crashes are also resolved if the popup menu is properly closed (for example with dismissAllActiveMenus()). 

 

Ok, here's how to reproduce the crash using the JuceDemoPlugin project in Logic Pro X. The crash occurs if we have a dedicated LookAndFeel instance in the editor, which is deleted upon destruction of the editor, but the popup menu that is still on the screen seems to try to refer to it.

In PluginEditor.h, add:

ScopedPointer<LookAndFeel_V2> lookAndFeel;

In PluginEditor.cpp, change the first couple of lines of the constructor into:

    lookAndFeel = new LookAndFeel_V2;
    this->setLookAndFeel(lookAndFeel);

    // add some sliders..
    addAndMakeVisible (gainSlider = new ParameterSlider (*owner.gainParam));
    gainSlider->setPopupMenuEnabled(true);
    gainSlider->setSliderStyle (Slider::Rotary);

    addAndMakeVisible (delaySlider = new ParameterSlider (*owner.delayParam));
    delaySlider->setPopupMenuEnabled(true);
    delaySlider->setSliderStyle (Slider::Rotary);

Now compile and open the plugin in Logic Pro X, enable the slider popup window, but don't select anything, instead close the GUI from the effects rack but keep the plugin in there - voila, Logic Pro X crashes.

All is resolved if we add PopupMenu::dismissAllActiveMenus() to the destructor of the editor, but this seems a bit of a bandaid rather than a proper solution? Or perhaps I'm doing something that I'm not supposed to?

To be honest, PopupMenu::dismissAllActiveMenus() in the editor destructor works GREAT for me. I just tried this in ValhallaPlate, and it fixed the "orphaned menu" bug my plugins have had since 2010. So, thanks for letting me know about this!!!

Sean Costello

All is resolved if we add PopupMenu::dismissAllActiveMenus() to the destructor of the editor, but this seems a bit of a bandaid rather than a proper solution? Or perhaps I'm doing something that I'm not supposed to?

If you leave the poor old menu using a dangling pointer to your lookandfeel class, then yes, you're doing something that you're not supposed to! There's nothing we could do in the library to deal with this situation, the requirement for using a look+feel is that you don't delete it while it's still in use!

Can you elaborate a bit on what you mean with leave the poor old menu using a dangling pointer?

In the demo project, the only change that was made re lookAndFeel is to instantiate ScopedPointer<LookAndFeel_V2> in the constructor, which is not a modified version of a lookandfeel but one of the standard supplied ones. Do you mean that we should check for old menus in the destructor of the editor then?

Or would there be a better way to detect the existence of an orphaned menu that was created by one of the Components in the editor, that may or may not use the LookAndFeel instance which is being deleted at the same time?

 

It's up to you how you choose to manage your object lifetimes, but if you create a look+feel object, give it to a popup menu (albeit indirectly), and then delete that look+feel while the menu is still using it, then you're breaking the rules, and there's nothing the menu can do to survive being left with a dangling pointer.

(It could be argued that LookAndFeel should be reference-counted.. In retrospect, that might have been a better idea, but to change it now would break a lot of code, so isn't likely to be a change we'll make)

ok, thanks Jules, fair enough !

That said, there's still the dangling/orphaned menu problem in Logic Pro X (and other hosts) with the demo plugin (without modifications or look+feel passing on) if the popup is activated, while the host closes the GUI. What would be the best way to fix that issue?

I thought Fabian looked at that but couldn't reproduce it? It's another thread, anyway, please don't go OT on this one.

..just a quick follow-up to this: I just remembered that LookAndFeel can support WeakReferences, so I've tweaked the PopupMenu so that it will detect when the L+F is deleted and will revert to a nullptr and not crash here.

BUT altough this will be safe, it won't magically make it behave the way you intended, because if the popup menu is still there after your L+F is deleted, its appearance will revert to a default look, which could look a bit messy depending on what your custom L+F does. So you'd still need to fix this, but at least in future it can't cause any actual crashes, just a few raised eyebrows from your users.

Awesome, thanks! Yes, I'll look into proper handling of the popup menu adding some code to close/delete them when necessary to resolve those raised eyebrows :-)

Is there is any reason for the Component class to not have a WeakReference<LookAndFeel> too?

(revisiting this cause I just ran into the same thing with a CallOutBox :) )

It's mainly that the Component class needs to be as minimal as possible, and this adds some overhead. But it's maybe not a bad idea.