home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.mactech.com 2010
/
ftp.mactech.com.tar
/
ftp.mactech.com
/
util
/
cscriptthread.sit.hqx
/
CScriptThread
/
CScriptThread.cp
next >
Wrap
Text File
|
1997-08-13
|
15KB
|
520 lines
/*---------------------------------------------------------------------------
Copyright © 1996 CPTech Ltd. All rights reserved.
-----------------------------------------------------------------------------
PROGRAM DOCUMENTATION
Program Name : CScriptThread.cp
Module : Script execution
Description : This module runs an OSA script in a thread.
In theory with out erroring on AESend because
the events are suspended in communications.
There seems to be a few problems with executing AppleScripts in Threads at
the moment. The idea of acalling OSALOad and OSAExecute works fine in
68K code and I'm running multiple concurrent scripts. The 68K application
works fine on a PowerPC but recompile for PPC and things go awry as soon
as I try to run the second threaded script.
A fix has been supplied by Mac DTS in the form of Preserve68KRegsActiveProc.
I also have the following mail from the same:
On 10/31/96, Caerwyn Pearce, 100024.355@compuserve.com wrote:
>I seem to be having a few problems with executing AppleScripts in Threads.
>The idea of calling OSALoad and OSAExecute works fine in 68K code and
>I'm running multiple concurrent scripts.
>
>The 68K version of the application works fine on a PowerPC but recompile
>for PPC and things go awry as soon as I try to run a second threaded script.
>The threads are co-operative for both 68K and PowerPC.
The problem isn't with A5, but with the Thread Manager. There is a bug in
the Thread Manager with regards to native PPC threads and the OSA. When
you call your native active proc, the PPC registers are saved and
restored, but the emulated 68K registers aren't. Problem is, the OSA
assumes they are.
So, to work around the problem I put together a PPC library. It basically
a piece of 68K code wrapped up in a PEF container that you link into your
project. You install this library as the active proc, which then saves
and restores the emulated registers around a call to the real PPC active
proc (which is doing the yielding).
BTW, the active proc is the proper place to be making any yield calls.
The idle proc is for getting periodic time; you should never call any
toolbox routine that gives up control (WaitNextEvent, Yield, etc).
And then another
>You mention that the active proc is the best place to Yield, not the idle
>proc. Do you exclude the send proc as well.
Well, yes and no. If you want to yield time in the send proc, before or
after you have send the event, that's OK. But once you call AESend, and
if you specify kAEWaitReply (which is the reason you have an idle proc),
you should never call anything that yields time to other processes (any
Yield call, WaitNextEvent, EventAvail, etc.)
The reason for this is that when you call AESend, the Apple Event Manager
takes over responsibility for calling WaitNextEvent for you. That's why
the parameters to the idle proc are an event record and a sleep value.
The Apple Event manager gives you the event record, and you give it back
the sleep time. If you do cause a process switch by yielding things can
and do get messed up, and a crash is often the result.
BTW, the only events you will receive in the idle proce are events like
update and suspend. You will never receive any high level events (like
Apple Events). That's what the filter proc (in the call to AESend) is
for. And you MUST service any update events you receive, because no other
process will get any time until they are services.
The Active Proc, on the other hand, is there to provide a way for the
process that is executing the script to yield time to other processes.
That's why the active proc is the right (and only) place to yield time to
other processes
>Does your library include all three procs, should I wish to
>implement my own idle proc too.
The library is simply a wrapper that saves the emulated registers, calls
a UPP for the real active proc (where you yield), and then restores the
registers when the active proc returns. It's general purpose enough that
I suppose it could be used for other procs, but since you shouldn't yield
in any other procs, I never tested it with them.
-----------------------------------------------------------------------------
REVISION HISTORY
Rev Date Who Description
-----------------------------------------------------------------------------
VCS01 23-OCT-96 CRP Incept Date
-----------------------------------------------------------------------------
*/
//---------------------------------------------------------------------------
#pragma mark Includes
// System headers
#include <OSA.h>
#include <AppleEvents.h>
#include <MixedMode.h>
// PowerPlant headers
#include <UAppleEventsMgr.h>
#include <UExtractFromAEDesc.h>
// Project headers
#include "CScriptThread.h"
//---------------------------------------------------------------------------
#pragma mark Prototypes
pascal OSErr CustomOSASendProc(const AppleEvent *theAppleEvent,
AppleEvent *reply,
AESendMode sendMode,
AESendPriority sendPriority,
long timeOutInTicks,
AEIdleUPP idleProc,
AEFilterUPP filterProc,
long refCon);
pascal OSErr CustomOSAActiveProc (Int32 refCon);
//---------------------------------------------------------------------------
#pragma mark Globals
// === Static Members ===
OSAActiveUPP CScriptThread::sAEActiveProcUPP = NewOSAActiveProc(CustomOSAActiveProc);
OSASendUPP CScriptThread::sAESendProcUPP = NewOSASendProc(CustomOSASendProc);
#pragma mark -
#pragma mark ----- Constructors/Destructors -----
// ---------------------------------------------------------------------------
// • CScriptThread
// ---------------------------------------------------------------------------
// Constructor
CScriptThread::CScriptThread(FSSpec &inSpec)
: LThread(false),
mFileSpec(inSpec)
{
mComponent = nil;
mScriptID = nil;
mResultID = nil;
mSendProc = nil;
mSendProcRefCon = nil;
mActiveProc = nil;
mActiveProcRefCon = nil;
mContextAppleEvent.descriptorType = typeNull;
}
// ---------------------------------------------------------------------------
// • ~CScriptThread
// ---------------------------------------------------------------------------
CScriptThread::~CScriptThread(void)
{
OSErr osError = noErr;
OSErr osaError = noErr;
osaError = ::OSADispose(mComponent,mScriptID);
osaError = ::OSADispose(mComponent,mResultID);
osError = ::CloseComponent(mComponent);
}
// ---------------------------------------------------------------------------
// • Run
// ---------------------------------------------------------------------------
// This is the code executed by the script thread.
// Open a scripting component and set it up for concurrency.
// Open the file
// Get the script handle
// Load it into the component
// Close the file
// Execute it.
void *CScriptThread::Run(void)
{
Handle scriptResH = nil;
short fRefNum = -1;
OSErr error = noErr;
Try_
{
/*
** Need to have an instance of a component for each thread
*/
mComponent = ::OpenDefaultComponent(kOSAComponentType,
kOSAGenericScriptingComponentSubtype);
ThrowIfNil_(mComponent);
// The apple event manager calls the idle function
// only after an event has been sent.
// A scripting component calls the active proc at regular intervals.
// open resource fork
fRefNum = ::FSpOpenResFile(&mFileSpec,fsRdPerm);
ThrowIfResError_();
// get the first script resource in the file
scriptResH = ::Get1IndResource(kOSAScriptResourceType,1);
FailNIL_(scriptResH);
// Load it
AEDesc scriptDesc;
scriptDesc.descriptorType = typeOSAGenericStorage;
scriptDesc.dataHandle = scriptResH;
error = ::OSALoad(mComponent,&scriptDesc,kOSAModeNull,&mScriptID);
ThrowIfOSErr_(error);
::CloseResFile(fRefNum);
fRefNum = -1;
// Use my own Active and send procedures
error = SetupOSACallBacks();
// run the script(finally)
error = ::OSAExecute(mComponent,mScriptID,kOSANullScript,kOSAModeNull,&mResultID);
if(errOSAScriptError == error)
{
StAEDescriptor theErrorDesc;
Int16 theError;
error = ::OSAScriptError(mComponent, kOSAErrorNumber,
typeShortInteger, &theErrorDesc.mDesc);
ThrowIfOSErr_(error);
UExtractFromAEDesc::TheInt16(theErrorDesc, theError);
}
}
Catch_(catchErr)
{
}
EndCatch_
if (fRefNum != -1)
::CloseResFile(fRefNum);
return (NULL);
}
// ---------------------------------------------------------------------------
// • SwapContext
// ---------------------------------------------------------------------------
// This function defines the output thread's behaviour
void CScriptThread::SwapContext(Boolean swappingIn)
{
OSErr error;
if (swappingIn)
{
if(mContextAppleEvent.descriptorType != typeNull)
{
error = ::AESetTheCurrentEvent(&mContextAppleEvent);
}
LThread::SwapContext(swappingIn);
}
else
{
error = ::AEGetTheCurrentEvent (&mContextAppleEvent);
LThread::SwapContext(swappingIn);
}
}
// ---------------------------------------------------------------------------------
// • SetupOSACallBacks
// ---------------------------------------------------------------------------------
//
// Get and Set send and active functions for the component
// Get because we're going to call the default one anyhow.
// I have to implement my own send cos otherwise the AESend
// fails when I suspend events, causing the script to bomb out.
// The alternative would be to put 'ignoring errors' in all the
// scripts, messy.
OSErr CScriptThread::SetupOSACallBacks(void)
{
OSErr finalErr = noErr;
OSErr error;
error = ::OSAGetSendProc(mComponent, &mSendProc, &mSendProcRefCon);
#if GENERATINGCFM
mSendRefRec.procEntry = sAESendProcUPP;
mSendRefRec.refCon = (long)this;
error = ::OSASetSendProc( mComponent, &Preserve68KRegistersSendProc, (long)&mSendRefRec);
#else
error = ::OSASetSendProc(mComponent, sAESendProcUPP, (long)this);
#endif
// Get and Set the Active proc for the component
// In theory the active proc is called periodically during
// script compilation and execution.
error = ::OSAGetActiveProc(mComponent, &mActiveProc,& mActiveProcRefCon);
/*
** For 68k we just install a straight call into our Active proc.
** If we are PPC then we have to install the DTS fudge to call
** back a PPC routine from the 68k emulated OSA.
*/
#if GENERATINGCFM
mActiveRefRec.procEntry = sAEActiveProcUPP;
mActiveRefRec.refCon = (long)this;
error = ::OSASetActiveProc( mComponent, &Preserve68KRegsActiveProc, (long)&mActiveRefRec);
#else
error = ::OSASetActiveProc(mComponent, sAEActiveProcUPP, (long)this);
#endif
return(finalErr);
}
// ---------------------------------------------------------------------------------
// • CallOldActiveProc
// ---------------------------------------------------------------------------------
OSErr CScriptThread::CallOldActiveProc(void) const
{
OSErr theError = noErr;
if(mActiveProc)
{
#if GENERATINGCFM
theError = CallUniversalProc(mActiveProc, uppOSAActiveProcInfo, mActiveProcRefCon);
#else
theError = mActiveProc(mActiveProcRefCon);
#endif
}
return(theError);
}
// ---------------------------------------------------------------------------------
// • CallOldSendProc
// ---------------------------------------------------------------------------------
OSErr CScriptThread::CallOldSendProc(const AppleEvent *theAppleEvent,
AppleEvent *reply,
AESendMode sendMode,
AESendPriority sendPriority,
long timeOutInTicks,
AEIdleUPP idleProc,
AEFilterUPP filterProc)
{
OSErr theError = noErr;
if(mSendProc)
{
#if GENERATINGCFM
theError = CallUniversalProc(mSendProc, uppOSASendProcInfo,
theAppleEvent, reply, sendMode, sendPriority,
timeOutInTicks, idleProc, filterProc, mSendProcRefCon);
#else
theError = mSendProc(theAppleEvent, reply, sendMode, sendPriority,
timeOutInTicks, idleProc, filterProc, mSendProcRefCon);
#endif
}
else
{
theError = ::AESend(theAppleEvent, reply, sendMode, sendPriority,
timeOutInTicks, idleProc, filterProc);
}
return(theError);
}
// ---------------------------------------------------------------------------------
// • TestAEReply
// ---------------------------------------------------------------------------------
OSErr CScriptThread::TestAEReply(AppleEvent *reply)
{
OSErr theError = noErr;
StAEDescriptor temp;
theError = ::AEGetParamDesc(reply, keyAEResult, typeWildCard, &temp.mDesc);
return(theError);
}
// ---------------------------------------------------------------------------------
// • CustomOSAActiveProc
// ---------------------------------------------------------------------------------
pascal OSErr CustomOSAActiveProc (Int32 refCon)
{
CScriptThread *myThread = (CScriptThread*)refCon;
OSErr theError = noErr;
if(myThread)
{
theError = myThread->CallOldActiveProc();
myThread->Yield();
}
return(theError);
}
// ---------------------------------------------------------------------------------
// • CustomOSASendProc
// ---------------------------------------------------------------------------------
pascal OSErr CustomOSASendProc(const AppleEvent *theAppleEvent,
AppleEvent *reply,
AESendMode sendMode,
AESendPriority sendPriority,
long timeOutInTicks,
AEIdleUPP idleProc,
AEFilterUPP filterProc,
long refCon)
{
CScriptThread *myThread = (CScriptThread*)refCon;
OSErr theError = memFullErr;
Boolean bUseAESend = true;
/*
** As far as hanging about a bit goes the sendMode can contain one
** of the following three options..
**
** kAENoReply sender doesn't want a reply to event
** kAEQueueReply sender wants a reply but won't wait
** kAEWaitReply sender wants a reply and will wait
**
** The only one which we can wait on is kAEWaitReply.
** What happens if the user says kAENoReply and I suspend the event?
**
*/
if (myThread)
{
/*
** May want to check the target, if it's me then set the
** timeOutInTicks to kNoTimeout, cos some of my events have
** integrated timeout parameters.
**
** Possibly may want to supply an alternate idleProc
** The idle proc is used to handle other incoming events!
** and can also be used to yield whilst the event is being handled.
*/
theError = myThread->CallOldSendProc(theAppleEvent, reply, sendMode, sendPriority,
timeOutInTicks, idleProc, filterProc);
AESendMode smReplayFlags = sendMode & kAEWaitReply;
if((theError == errAETimeout) && (smReplayFlags != kAENoReply))
{
Int16 tempAEErr;
do
{
tempAEErr = myThread->TestAEReply(reply);
myThread->Yield();
} while(tempAEErr == errAEReplyNotArrived);
theError = noErr;
}
}
else
{
theError = ::AESend(theAppleEvent, reply, sendMode, sendPriority,
timeOutInTicks, idleProc, filterProc);
}
return(theError);
}