realbasic-plugins
[Top] [All Lists]

Re: Plugin design vs threading

To: REALbasic Plugins <realbasic-plugins at lists dot realsoftware dot com>
Subject: Re: Plugin design vs threading
From: Steve Roy <sroy at mac dot com>
Date: Fri, 24 Sep 2004 14:14:45 -0400
Delivered-to: realbasic-plugins at lists dot realsoftware dot com
References: <S dot 0000369895 at mail dot tempel dot org> <3704875D-0D9C-11D9-A8F5-00039347B912 at mac dot com> <797A6734-0D9E-11D9-862F-0003937BDA2C at realsoftware dot com> <874D08B2-0DA1-11D9-A8F5-00039347B912 at mac dot com> <265090C4-0DA2-11D9-8BA1-00039378202C at realsoftware dot com>
On Sep 23, 2004, at 4:50 PM, Aaron Ballman wrote:

You cannot do any allocations whatsoever at interrupt time in Classic. Doing so causes the your application to crash. So the short answer is -- don't call anything that has even the most remote chance of allocating memory, moving memory or freeing memory. If you need to do a memory allocation, you either 1) defer the task until system time, or 2) use interrupt-safe memory functions provided by the OS.

Well I coded something, and it seems to work on Mac OS X but one part of it crashes on Mac OS 9. I'm not familiar with doing this kind of thing in C, so I don't know if what I did fits in the scope of what you're saying above. Here's what I have, commented, in the hope that a kind soul can take 2 minutes to look at it and tell me what they think, and who knows, it looks like I'm not the only one wrestling with this, maybe it's going to help someone. So here goes:

// This is called by RB from my custom class
static void Foo(REALobject obj, ...)
{
        // Stuff the parameters into a pointer we can pass to the parallel task
        FooParams *params = (FooParams *)malloc(sizeof(FooParams));
        params->object = obj;
        params->... = ...

        // Do the actual work in a parallel task so this function can return
        // immediately and so the UI stays responsive in the REALbasic app
#if TARGET_CARBON || TARGET_PPC
        MPTaskID tsk;
        OSErr err = MPCreateTask(MyTask, params, 0, 0, 0, 0, 0, &tsk);
#elif TARGET_WIN32
        // TODO
#endif
}

This works and looks safe to me. It returns immediately and the MyTask() function is called.

static OSStatus MyTask(void *data)
{
        // Extract the passed parameters
        FooParams *params = (FooParams *)data;
        REALobject obj = params->object;
        ... = params->...;
        free(params);

// Now this is where I crash on Mac OS 9. I'm calling a function of a private framework // which takes another callback. This call blocks until it's done, and the function calls // the provided callback as it finds stuff. As I see it, I'm in a callback here, and I'm // calling yet another callback. Why does this crash? Even if the callback does // nothing (it's just an empty function), it still crashes on Mac OS 9. It works on
        // Mac OS X though, no matter what I do in the callback. Any thoughts?
        GoFindStuff(MyFindStuffCallback, ...);

// Now here I call an event in my REALobject to let it know that searching is done. // I used to call directly into RB, but since you told me I shouldn't do this from another // thread, I'm using a timer instead. It seems to works, on Mac OS 9 and Mac OS X.
        // Is this what you meant?
        DoneParams *params2 = (DoneParams *)malloc(sizeof(DoneParams));
        params2->object = obj;
#if TARGET_CARBON
        params2->timerUPP = NewEventLoopTimerUPP(DoneCallback);
        EventLoopTimerRef timer;
InstallEventLoopTimer(GetMainEventLoop(), 0, 0, params2->timerUPP, params2, &timer);
#elif TARGET_PPC
        params2->tmTask.qLink = NULL;
        params2->tmTask.qType = 0;
        params2->tmTask.tmAddr = NewTimerUPP(DoneCallback);
        params2->tmTask.tmCount = 0;
        params2->tmTask.tmWakeUp = 0;
        params2->tmTask.tmReserved = 0;
        OSErr err = InstallTimeTask((QElemPtr)params2);
        if (err == noErr)
                PrimeTimeTask((QElemPtr)params2, 0);
#endif

        return noErr;
}

static void MyFindStuffCallback(...)
{
        // This can be empty, it always crashes on Mac OS 9.
// On Mac OS X I do have code here that calls back into RB, by also installing // a timer as in MyTask() above, and it's sweet, although this all seems like a
        // lot of work, doesn't it?
}

#if TARGET_CARBON
static pascal void DoneCallback(EventLoopTimerRef timer, void *userData)
#elif TARGET_PPC
static pascal void DoneCallback(TMTaskPtr tmTaskPtr)
#elif TARGET_WIN32
// TODO
#endif
{
        // Uninstall the timer since we only needed it once
        ...
        
#if TARGET_CARBON
        DoneParams *params = (DoneParams *)userData;
#elif TARGET_PPC
        DoneParams *params = (DoneParams *)tmTaskPtr;
#elif TARGET_WIN32
        // TODO
#endif
        
        // Call the ServerFound event of the REALobject
        ServerFoundEventProc fn = (ServerFoundEventProc)REALGetEventInstance(
                (REALcontrolInstance)params->object, &ServerLookupEvents[1]);
        if (fn)
                fn(params->object, ...);
        
        // Clean up
        ...
        free(params);
}

Steve

--
Steve Roy <sroy at mac dot com>
Personal homepage: <http://homepage.mac.com/sroy>
Projects homepage: <http://www.roydesign.net>

_______________________________________________
Unsubscribe or switch delivery mode:
<http://www.realsoftware.com/support/listmanager/>

Search the archives of this list here:
<http://www.realsoftware.com/listarchives/lists.html>

<Prev in Thread] Current Thread [Next in Thread>