callFunctionOnMessageThread problem?


#1

I think there may be a little problem with the…

void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback,
void* data) in the tip

When its not called from the message thread.

As an example…create a thread and throw an AlertWindow::showMessageBox in its run method.

I crash before the performCallback: is executed.

I’ll try to debug more and post more details…


#2

Thread 5 Crashed: 0 libobjc.A.dylib 0x91a8768c objc_msgSend + 28 1 com.apple.Foundation 0x9142f648 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] + 184 2 com.sm.rip 0x002d80a7 juce::MessageManager::callFunctionOnMessageThread(void* (*)(void*), void*) + 125 3 com.sm.rip 0x001debd9 juce::AlertWindowInfo::run() const + 47 4 com.sm.rip 0x001dc707 juce::AlertWindow::showMessageBox(juce::AlertWindow::AlertIconType, juce::String const&, juce::String const&, juce::String const&) + 179

juceAppDelegate is inited and valid…I put a breakpoint on dealloc and it hasn’t been hit


#3

The problem it appears is with

CallbackMessagePayload

changing the code to

[juceAppDelegate performSelectorOnMainThread: @selector (performCallback:) withObject: nil// (id) &cmp waitUntilDone: YES];

At least allows the performCallback: function to be called.

Does performSelectorOnMainThread try to retain the object I wonder…(casting the struct to an id would be a no-no in that case)


#4

http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/occ/intfm/NSObject/performSelector:withObject:

BUGGER!


#5

Also, for those using the tip and NOT the amalgamated file. If you are not doing a clean build, TOUCH juce_mac_NativeCode.mm before building.

I’ve noticed a few times i’ve grabbed updates from the tip and the build went super fast(as though no changes had been made), so I got in the habit of doing a clean before every build. However while debugging this problem, I noticed the changes I was making to the source file weren’t being compiled. Sure enough Jules put a little comment at the top…

// (This file gets included by juce_mac_NativeCode.mm, rather than being
// compiled on its own).
#ifdef JUCE_INCLUDED_FILE

And after I touched juce_mac_NativeCode.mm all compiled as it should!


#6

How about this as a little fix?

[code]void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback,
void* data)
{
if (isThisTheMessageThread())
{
return (*callback) (data);
}
else
{
CallbackMessagePayload cmp;
cmp.function = callback;
cmp.parameter = data;
cmp.result = 0;
cmp.hasBeenExecuted = false;

    [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:)
                                      withObject: [NSData dataWithBytesNoCopy:&cmp length:sizeof(CallbackMessagePayload)]
                                   waitUntilDone: YES];

    return cmp.result;
}

}[/code]

- (void) performCallback: (id) info
{
	CallbackMessagePayload* pl=0;
	if ([info isKindOfClass:[NSData class]])
     pl= (CallbackMessagePayload*) [(NSData*)info bytes];

    if (pl != 0)
    {
        pl->result = (*pl->function) (pl->parameter);
        pl->hasBeenExecuted = true;
    }
}


#7

OK—FINAL POST!

Does callFunctionOnMessageThread need an AutoPool Julian? As its called from a thread, the user may not have a pool in place right?

void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, 
                                                   void* data) 
{ 
    if (isThisTheMessageThread()) 
    { 
        return (*callback) (data); 
    } 
    else 
    { 
      const ScopedAutoReleasePool pool;

        CallbackMessagePayload cmp; 
        cmp.function = callback; 
        cmp.parameter = data; 
        cmp.result = 0; 
        cmp.hasBeenExecuted = false; 

        [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:) 
                                          withObject: [NSData dataWithBytesNoCopy:&cmp length:sizeof(CallbackMessagePayload)] 
                                       waitUntilDone: YES]; 

        return cmp.result; 
    } 
}

Better to be safe than sorry!


#8

[quote=“justin”]http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/occ/intfm/NSObject/performSelector:withObject:

BUGGER![/quote]

Yes… bugger indeed! I wonder how it ever worked at all!?

Your workaround looks spot-on to me, I’ll get a fix checked in right away…


#9

I see the commit message in 634 addressing it, but mac_messageManager doesn’t seem to be affected.

Not a big deal for me, but thought i’d mention it anyways…


#10

Doh, sorry. Looks like I didn’t tick the right boxes and that file was missed out. It’s checked in now.


#11

Sorry… this function needs a little fix…

void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback,
                                                   void* data)
{
    if (isThisTheMessageThread())
    {
        return (*callback) (data);
    }
    else
    {
        const ScopedAutoReleasePool pool;

        CallbackMessagePayload cmp;
        cmp.function = callback;
        cmp.parameter = data;
        cmp.result = 0;
        cmp.hasBeenExecuted = false;

        [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:)
                                          withObject: [NSData dataWithBytesNoCopy: &cmp
                                                                           length: sizeof (cmp) freeWhenDone:NO]
                                       waitUntilDone: YES];

        return cmp.result;
    }
}

(Needs the freeWhenDone:NO added to the function call, otherwise the autorelease pool tries to free cmp and spits out a

malloc: *** error for object 0xb05fae6c: Non-aligned pointer being freed
*** set a breakpoint in malloc_error_break to debug


#12

ah, quite right - thanks for that!