mac_MessageManager: doPlatformSpecificShutdown crashes Logic

Hey Jules,
the bug spotter has another one:
juce_mac_MessageManager.mm: doPlatformSpecificShutdown() crashes Logic when you open up a plugin, close the GUI and afterwards remove the plugin from the insert -> BAD EXEC.
This doesn’t happen with the demo plugin but with any more advanced juce plugin I’ve checked.

But don’t worry, I already found a solution for this, you just have to put it to the official tip.

First the original version which leads to the crash:

[code]void MessageManager::doPlatformSpecificShutdown()
{
if (juceAppDelegate != 0)
{
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceAppDelegate];
[[NSNotificationCenter defaultCenter] removeObserver: juceAppDelegate];

    // Annoyingly, cancelPerformSelectorsWithTarget can't actually cancel the messages
    // sent by performSelectorOnMainThread, so need to manually flush these before quitting..
    juceAppDelegate->flushingMessages = true;

    for (int i = 100; --i >= 0 && numPendingMessages > 0;)
    {
        const ScopedAutoReleasePool pool;
        [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
                                 beforeDate: [NSDate dateWithTimeIntervalSinceNow: 5 * 0.001]]; //!!!!!!! CRASHES HERE BECAUSE "numPendingMessages" ISN'T DECREASED IN TIME !!!!!!!!!!!!!!
    }

    [juceAppDelegate release];
    juceAppDelegate = 0;
}

}[/code]

And this version works:

[code]void MessageManager::doPlatformSpecificShutdown()
{
if (juceAppDelegate != 0)
{
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceAppDelegate];
[[NSNotificationCenter defaultCenter] removeObserver: juceAppDelegate];

    // Annoyingly, cancelPerformSelectorsWithTarget can't actually cancel the messages
    // sent by performSelectorOnMainThread, so need to manually flush these before quitting..
    juceAppDelegate->flushingMessages = true;
	    
    int currentNumPendingMessages = numPendingMessages;
    for (int i = 100; --i >= 0 && currentNumPendingMessages > 0;)
    {
        const ScopedAutoReleasePool pool;
        [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
                                 beforeDate: [NSDate dateWithTimeIntervalSinceNow: 5 * 0.001]];
		   --currentNumPendingMessages;
    }

    [juceAppDelegate release];
    juceAppDelegate = 0;
}

}[/code]

Cheers,
Jan

Hmm. Interesting, but your fix looks a bit suspicious to me. Each time the run loop is invoked, it may or may not dispatch one or more of those messages, so to decrement the counter each time means that it’s not really a true reflection of how many messages are still pending. And if any of those messages are left behind to get delivered after the module is unloaded, it’ll definitely crash.

It’s hard to figure out what’s actually happening, but perhaps the cancelPerformSelectorsWithTarget call really does cancel them, despite what the docs say…? That’d explain why the numPendingMessages count doesn’t go down. If that’s the case, then it’d be ok to remove the entire loop, but that doesn’t sound right to me.

Yes maybe it was a little too late yesterday. I looked at it again this morning and it looked spuspicious to me, too. It’s right, doing it this way was like removing the entire loop. It seems like a timing problem to me.
Here is the relevant part of the (reproducable) crashlog but I don’t think that it helps much :

[code]
Process: Logic Pro [3935]
Path: /Applications/Logic Pro.app/Contents/MacOS/Logic Pro
Identifier: com.apple.logic.pro
Version: 8.0.2 (1502.22)
Build Info: Logic-15022200~9
Code Type: X86 (Native)
Parent Process: launchd [781]

Interval Since Last Report: 10581 sec
Crashes Since Last Report: 237
Per-App Interval Since Last Report: 5107 sec
Per-App Crashes Since Last Report: 3

Date/Time: 2009-11-13 11:13:16.343 +0100
OS Version: Mac OS X 10.5.8 (9L31a)
Report Version: 6
Anonymous UUID: 65B5460D-97A7-4D4E-B4DA-ECF2AAB099ED

Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x00000000c0000003
Crashed Thread: 0

Thread 0 Crashed:
0 com.apple.logic.pro 0x000ffe77 0x1000 + 1044087
1 com.apple.logic.pro 0x000fff31 0x1000 + 1044273
2 com.apple.logic.pro 0x00100056 0x1000 + 1044566
3 com.apple.logic.pro 0x00111b80 0x1000 + 1117056
4 com.apple.logic.pro 0x0008b30d 0x1000 + 566029
5 com.apple.logic.pro 0x0008bcb4 0x1000 + 568500
6 com.apple.logic.pro 0x0008bd5a 0x1000 + 568666
7 com.apple.logic.pro 0x002b3483 0x1000 + 2827395
8 com.apple.logic.pro 0x002b3552 0x1000 + 2827602
9 libSystem.B.dylib 0x968362bb _sigtramp + 43
10 com.apple.logic.pro 0x00084566 0x1000 + 537958
11 com.apple.logic.pro 0x00084664 0x1000 + 538212
12 com.apple.logic.pro 0x0010b1f3 0x1000 + 1090035
13 com.apple.logic.pro 0x00114348 0x1000 + 1127240
14 com.apple.logic.pro 0x00087f36 0x1000 + 552758
15 com.apple.logic.pro 0x000aa282 0x1000 + 692866
16 com.apple.logic.pro 0x002e6114 0x1000 + 3035412
17 com.apple.logic.pro 0x002e6203 0x1000 + 3035651
18 com.apple.logic.pro 0x003fb3a4 0x1000 + 4170660
19 com.apple.logic.pro 0x003ee7ef 0x1000 + 4118511
20 com.apple.logic.pro 0x0000f2cd 0x1000 + 58061
21 com.apple.logic.pro 0x0019d797 0x1000 + 1689495
22 com.apple.logic.pro 0x00014762 0x1000 + 79714
23 com.apple.logic.pro 0x00196042 0x1000 + 1658946
24 com.apple.Foundation 0x94884507 __NSFireTimer + 279
25 com.apple.CoreFoundation 0x973a88f5 CFRunLoopRunSpecific + 4469
26 com.apple.CoreFoundation 0x973a8aa8 CFRunLoopRunInMode + 88
27 com.apple.Foundation 0x948843d5 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 213
28 …rafted-gear.Multidynamix LP 0x2890320f juce::MessageManager::doPlatformSpecificShutdown() + 347 (juce_mac_MessageManager.mm:408)
29 …rafted-gear.Multidynamix LP 0x28987ee2 juce::MessageManager::~MessageManager() + 70
30 …rafted-gear.Multidynamix LP 0x28991be3 juce::shutdownJuce_GUI() + 81 (juce_Application.cpp:355)
31 …rafted-gear.Multidynamix LP 0x28b180ec JuceAU::~JuceAU() + 522
32 …rafted-gear.Multidynamix LP 0x28859334 ComponentBase::ComponentEntryDispatch(ComponentParameters*, ComponentBase*) + 126 (ComponentBase.cpp:72)
33 …rafted-gear.Multidynamix LP 0x28854821 AUBase::ComponentEntryDispatch(ComponentParameters*, AUBase*) + 2437 (AUDispatch.cpp:400)
34 …rafted-gear.Multidynamix LP 0x28864f81 AUMIDIEffectBase::ComponentEntryDispatch(ComponentParameters*, AUMIDIEffectBase*) + 111 (AUMIDIEffectBase.cpp:132)
35 …rafted-gear.Multidynamix LP 0x28b17e04 ComponentEntryPoint::Dispatch(ComponentParameters*, JuceAU*) + 174 (ComponentBase.h:91)
36 …rafted-gear.Multidynamix LP 0x28b12f74 mcgMultidynamixAUEntry + 30 (juce_AU_Wrapper.mm:1975)
37 …ple.CoreServices.CarbonCore 0x9651f935 CallComponentDispatch + 29
38 …ple.CoreServices.CarbonCore 0x96520675 CallComponentClose + 43
39 …ple.CoreServices.CarbonCore 0x96520599 CloseComponentInternal(ComponentInstanceRecord*) + 101
40 …ple.CoreServices.CarbonCore 0x9652051a CloseComponent + 46
41 com.apple.logic.pro 0x0040edf8 0x1000 + 4251128
…[/code]

Any ideas???

Forgot to mention, removing the entire loop works fine, too.

EDIT: No, sorry, doesn’t work with VST. Please don’t care about this thread for a while, I will do more tests before I get back with better infos about what’s happening.

I’m not able to find a solution for this.
So here is the current state:

  • The crash only happens in Logic, when plugins are opened -> the gui is closed -> the plugin is removed.

  • If “MessageManager::doPlatformSpecificShutdown()” is entered with “numPendingMessages = 0” then there are no problems, that’s the reasen why the juceDemoPlugin doesn’t crash (it enters with no pending messages).

  • In AU Lab and Live, there are no crashes with pending messages.

  • If I set a breakpoint somewhere in juce_mac_MessageManager.mm:

- (void) customEvent: (id) n {...}
and exeucte the same code then there is no crash with the same setup that crashes without this breakpoint. For this reason I assume that it is a timing problem.

  • If I remove the entire loop in “MessageManager::doPlatformSpecificShutdown()” there is no crash in logic, AU Lab and Live when using the AU plugin but now the VST Version crashes Live on opening the Live app.

I have no idea how to find a solution that works well in every environment.
I think I need some help to sort this out.

It’s a very difficult problem. If only the cancelPerformSelectorsWithTarget call actually worked, it’d all be easy, but there’s just no way of actually removing those events from the queue.

I wonder if there’s a way of rewriting all the messaging to avoid obj-c and use CFRunLoops instead… Really can’t think of any other way to make it safe.

Damn, I didn’t expect that it would be this complicated. Unfortunately I don’t have any experience with CFRunLoops and it’s messaging system, so I don’t think it would be a good idea to do it on my own. If there’s anything else I can help with or if you have another idea what I could try, then let me know.

I think it’s probably possible using a timer and keeping the messages in a local array… I’ll have a go, because I can’t think of any other way round this, and at least it’ll remove that messy clear up loop from the shutdown.

Cool, thanks.

Hello,

Inserting JuceDemoPlugin (or my owns) ,then closing GUI, then removing insert don’t crash here.
Logic 9 - OSX 10.6.1

Salvator

As I mentioned, it will only crash if it enters “doPlatformSpecificShutdown()” with numPendingMessages > 0. If your plugin will never have any pending messages at this point then you’re fine.
Could you set a breakpoint at the beginning of “doPlatformSpecificShutdown()” and check if there are pending messages when it hits the breakpoint? If so, then maybe it works with Logic 9 and OSX 10.6.

My latest check-in uses a completely new method for dispatching the messages on OSX, so give it a go… Should definitely avoid any of these nasty shutdown issues.

Hell yeah, it works perfectly.
But there is one more question: do you ever sleep?!

Thanks.

Of course. In fact I do some of my best coding while I’m asleep…