NSAppleScript not in the main thread
-
Hi,
I just read in the "Multithreading Programing Topics" that
NSAppleScript should only be used from the main thread.
My "application" is running as a plug-in in iTunes and I want to
extend the song information using NSAppleScript.
As the plug-in is not running in the main thread I could never run my
AppleScript from the main thread and as NSAppleScript is slow I want
to create a separate thread for the AppleScript calls.
What kind of problems can I expect from running AppleScript from
another thread (and accessing the same application with it)?
How can I avoid these problems?
Greeting from Germany
Christoph Vogelbusch -
Hi Christoph.
My program, iTunesSurfer uses NSAppleScript within threads other than
the main thread. (Amazing what works when you don't know what you
can't do...) I found, however, that NSApplescript itself is not
thread safe, so I wrap all uses of it with a BigFatLock(tm).
Otherwise, I had no problems. Since you are executing within another
applicaition, there is no guarantee that somebody else won't want to
use NSApplescript at the same time you do, so you are taking your
chances there.
-Kenny
On May 19, 2006, at 1:47 PM, Christoph Vogelbusch wrote:
> Hi,
>
> I just read in the "Multithreading Programing Topics" that
> NSAppleScript should only be used from the main thread.
>
> My "application" is running as a plug-in in iTunes and I want to
> extend the song information using NSAppleScript.
> As the plug-in is not running in the main thread I could never run
> my AppleScript from the main thread and as NSAppleScript is slow I
> want to create a separate thread for the AppleScript calls.
>
> What kind of problems can I expect from running AppleScript from
> another thread (and accessing the same application with it)?
> How can I avoid these problems?
>
> Greeting from Germany
> Christoph Vogelbusch
> _______________________________________________
> Do not post admin requests to the list. They will be ignored.
> Cocoa-dev mailing list (<Cocoa-dev...>)
> Help/Unsubscribe/Update your Subscription:
> http://lists.apple.com/mailman/options/cocoa-dev/kenny_leung%
> 40pobox.com
>
> This email sent to <kenny_leung...>
>
-
On May 19, 2006, at 2:47 PM, Christoph Vogelbusch wrote:
> What kind of problems can I expect from running AppleScript from
> another thread (and accessing the same application with it)?
Crashes.
> How can I avoid these problems?
You can try playing Russian Roulette with it if you'd like, but the
documentation change was made after a number of people in this group
complained that NSAppleScript was crashing while compiling/running
their script in a thread other than the main thread. For example:
<http://www.cocoabuilder.com/archive/message/cocoa/2004/8/18/114896>
The only 100% safe way to run the class is in the main thread.
Nick Zitzmann
<http://www.chronosnet.com/> -
I needed to run an applescript from my application that would send
events to my application.
I tried a number of things to make this work with no great success.
Finally I wrote a little "Script Helper" application. This faceless
app can be embedded into my main app's package. When I need to run a
script I launch the helper and send it the script. It can then send
events back to my main app as needed. If the helper is idle for
more than a few minutes it quietly goes away.
The project is still in development so this has not had a ton of
testing but, so far it has worked great for me.
On 19 May 2006, at 4:47 PM, Christoph Vogelbusch wrote:
> Hi,
>
> I just read in the "Multithreading Programing Topics" that
> NSAppleScript should only be used from the main thread.
>
> My "application" is running as a plug-in in iTunes and I want to
> extend the song information using NSAppleScript.
> As the plug-in is not running in the main thread I could never run
> my AppleScript from the main thread and as NSAppleScript is slow I
> want to create a separate thread for the AppleScript calls.
>
> What kind of problems can I expect from running AppleScript from
> another thread (and accessing the same application with it)?
> How can I avoid these problems?
>
> Greeting from Germany
> Christoph Vogelbusch
> _______________________________________________
> Do not post admin requests to the list. They will be ignored.
> Cocoa-dev mailing list (<Cocoa-dev...>)
> Help/Unsubscribe/Update your Subscription:
> http://lists.apple.com/mailman/options/cocoa-dev/<craiga...>
>
> This email sent to <craiga...>
-
NSAppleScript can only be used in the main thread because it shares a
AppleScript ComponentInstance, you need to create you own
ComponentInstance and that can not be done with NSAppleScript, you
can with my NDAppleScriptObject, which you can get at http://
homepage.mac.com/nathan_day/
On 20/05/2006, at 6:47 AM, Christoph Vogelbusch wrote:
> Hi,
>
> I just read in the "Multithreading Programing Topics" that
> NSAppleScript should only be used from the main thread.
>
> My "application" is running as a plug-in in iTunes and I want to
> extend the song information using NSAppleScript.
> As the plug-in is not running in the main thread I could never run
> my AppleScript from the main thread and as NSAppleScript is slow I
> want to create a separate thread for the AppleScript calls.
>
> What kind of problems can I expect from running AppleScript from
> another thread (and accessing the same application with it)?
> How can I avoid these problems?
>
> Greeting from Germany
> Christoph Vogelbusch
> _______________________________________________
> Do not post admin requests to the list. They will be ignored.
> Cocoa-dev mailing list (<Cocoa-dev...>)
> Help/Unsubscribe/Update your Subscription:
> http://lists.apple.com/mailman/options/cocoa-dev/<nathan_day...>
>
> This email sent to <nathan_day...>
Nathan Day
<nathan_day...>
http://homepage.mac.com/nathan_day/ -
Hi,
great! Thanks, Nathan. I will try your solution and am very curious
how you solved that problem.
Greeting from Germany
Christoph Vogelbusch
Am 20.05.2006 um 08:26 schrieb Nathan Day:
> NSAppleScript can only be used in the main thread because it shares
> a AppleScript ComponentInstance, you need to create you own
> ComponentInstance and that can not be done with NSAppleScript, you
> can with my NDAppleScriptObject, which you can get at http://
> homepage.mac.com/nathan_day/
>
> On 20/05/2006, at 6:47 AM, Christoph Vogelbusch wrote:
>
>> Hi,
>>
>> I just read in the "Multithreading Programing Topics" that
>> NSAppleScript should only be used from the main thread.
>>
>> My "application" is running as a plug-in in iTunes and I want to
>> extend the song information using NSAppleScript.
>> As the plug-in is not running in the main thread I could never run
>> my AppleScript from the main thread and as NSAppleScript is slow I
>> want to create a separate thread for the AppleScript calls.
>>
>> What kind of problems can I expect from running AppleScript from
>> another thread (and accessing the same application with it)?
>> How can I avoid these problems?
>>
>> Greeting from Germany
>> Christoph Vogelbusch
>> _______________________________________________
>> Do not post admin requests to the list. They will be ignored.
>> Cocoa-dev mailing list (<Cocoa-dev...>)
>> Help/Unsubscribe/Update your Subscription:
>> http://lists.apple.com/mailman/options/cocoa-dev/<nathan_day...>
>>
>> This email sent to <nathan_day...>
>
>
>
> Nathan Day
> <nathan_day...>
> http://homepage.mac.com/nathan_day/
>
-
Does that mean I could use NSAppleScript in a secondary thread if
that is the only thread it will ever be used in?
Mike.
On 20 May 2006, at 07:26AM, Nathan Day wrote:
> NSAppleScript can only be used in the main thread because it shares
> a AppleScript ComponentInstance, you need to create you own
> ComponentInstance and that can not be done with NSAppleScript, you
> can with my NDAppleScriptObject, which you can get at http://
> homepage.mac.com/nathan_day/
>
> On 20/05/2006, at 6:47 AM, Christoph Vogelbusch wrote:
>
>> Hi,
>>
>> I just read in the "Multithreading Programing Topics" that
>> NSAppleScript should only be used from the main thread.
>>
>> My "application" is running as a plug-in in iTunes and I want to
>> extend the song information using NSAppleScript.
>> As the plug-in is not running in the main thread I could never run
>> my AppleScript from the main thread and as NSAppleScript is slow I
>> want to create a separate thread for the AppleScript calls.
>>
>> What kind of problems can I expect from running AppleScript from
>> another thread (and accessing the same application with it)?
>> How can I avoid these problems?
>>
>> Greeting from Germany
>> Christoph Vogelbusch
>> _______________________________________________
>> Do not post admin requests to the list. They will be ignored.
>> Cocoa-dev mailing list (<Cocoa-dev...>)
>> Help/Unsubscribe/Update your Subscription:
>> http://lists.apple.com/mailman/options/cocoa-dev/<nathan_day...>
>>
>> This email sent to <nathan_day...>
>
>
>
> Nathan Day
> <nathan_day...>
> http://homepage.mac.com/nathan_day/
>
> _______________________________________________
> Do not post admin requests to the list. They will be ignored.
> Cocoa-dev mailing list (<Cocoa-dev...>)
> Help/Unsubscribe/Update your Subscription:
> http://lists.apple.com/mailman/options/cocoa-dev/mike.abdullah%
> 40gmail.com
>
> This email sent to <mike.abdullah...>
-
On 21 maj 2006, at 17.25, Mike Abdullah wrote:
> Does that mean I could use NSAppleScript in a secondary thread if
> that is the only thread it will ever be used in?
I don't know about NSAppleScript in particular (as I've never used
it), but "main thread" is not equal to "a single, specific, thread".
If the documented requirement is that you need to use the main
thread, you can't substitute it for any other thread.
j o a r -
All NSAppleScript instance seem to use the same ComponentInstance also the ComponentInstance may also be used by the main thread, to set up the application to be AppleScriptable perhaps. Each thread must have its own AppleScript ComponentInstance, in theory this you could have multiple thread each running there own AppleScript, but I have not been able to get this to work only one AppleScript in a seperate thread to the main thread. NDAppleScriptObject lets you do this, though you are probable better using my replace classes for NDAppleScriptObject, NDScriptData and its descendent class NDScriptHandler and NDScriptContext, I haven't made them available on my web site properly yet as I have not completed the documentation for them I also want to add some more feature and get the multi threading working properly, ie more than one thread running AppleScripts. You can download it here
<http://homepage.mac.com/nathan_day/.cv/nathan_day/Public/Source%20Code/NDSc
ript.dmg-binhex.hqx>
On Monday, May 22, 2006, at 01:25AM, Mike Abdullah <mike.abdullah...> wrote:
>
> <<Original Attached> >
Nathan Day
<nathan_day...>
http://homepage.mac.com/nathan_day/ -
I'd guess that the main thread could start using AppleScript at any
time---for example, if another app decided to start sending it
AppleEvents, or the user opens a document.
Nathan Day wrote:
> All NSAppleScript instance seem to use the same ComponentInstance also the ComponentInstance may also be used by the main thread, to set up the application to be AppleScriptable perhaps. Each thread must have its own AppleScript ComponentInstance, in theory this you could have multiple thread each running there own AppleScript, but I have not been able to get this to work only one AppleScript in a seperate thread to the main thread. NDAppleScriptObject lets you do this, though you are probable better using my replace classes for NDAppleScriptObject, NDScriptData and its descendent class NDScriptHandler and NDScriptContext, I haven't made them available on my web site properly yet as I have not completed the documentation for them I also want to add some more feature and get the multi threading working properly, ie more than one thread running AppleScripts. You can download it here
> <http://homepage.mac.com/nathan_day/.cv/nathan_day/Public/Source%20Code/NDSc
ript.dmg-binhex.hqx>
>
> On Monday, May 22, 2006, at 01:25AM, Mike Abdullah <mike.abdullah...> wrote:
>
>
>> <<Original Attached>>
>>
>
>
> Nathan Day
> <nathan_day...>
> http://homepage.mac.com/nathan_day/
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> Do not post admin requests to the list. They will be ignored.
> Cocoa-dev mailing list (<Cocoa-dev...>)
> Help/Unsubscribe/Update your Subscription:
> http://lists.apple.com/mailman/options/cocoa-dev/<jstiles...>
>
> This email sent to <jstiles...>
-
Appended is a simplistic-but-effective workaround for the requirement
that NSAppleScript execute on the main thread.
The method-of-interest is:
- (NSAppleEventDescriptor *) executeOnMainThreadReturningError:
(NSDictionary **) error;
-- which is modeled on the existing:
- (NSAppleEventDescriptor *)executeAndReturnError:(NSDictionary **)
errorInfo;
-- making this solution an easy drop-in.
This solution is appropriate when the goal is not expressly to run
the script in a subthread, but simply to allow a subthread to use the
script _as_if_ it could run in the subthread. I have found that
because of the event-synchronized scheduling of events run by
performSelectorOnMainThread:withObject:waitUntilDone:, the execution
of reasonably small AppleScripts by this method rarely causes any
serious disruption in user experience. In some cases even fairly
large scripts can be broken down into manageable sub-scripts that can
be executed serially.
If you are using AppleScript to do something irreducibly time-
consuming, you probably want to look elsewhere.
In this solution, the executeOnMainThreadReturningError: method
simply executes the script on the main thread using
performSelectorOnMainThread:withObject:waitUntilDone: and then
returns the resulting NSAppleEventDescriptor and error dictionary.
This is not a universal solution -- no support for separately
compiling the script _from_ the subthread is provided. It is assumed
that the script has either been precompiled on the main thread, or
will be compiled at execution.
It seems to cover the majority of real-world cases, however -- if you
have a totally "canned" script, it is trivial to precompile it on the
main thread, and if you don't, you are probably recreating the script
from scratch with each execution. Compiling from a subthread could
easily be added, using the current code as a model.
This could probably be implemented as a category on NSAppleScript by
using a NSMutableDictionary as the object for
performSelectorOnMainThread:withObject:waitUntilDone:, but when I
implemented this I chose the subclass route.
@interface KKNSAppleScript: NSAppleScript
{
NSAppleEventDescriptor * _resultDescriptor;
NSDictionary * _errorDict;
}
- (NSAppleEventDescriptor*)resultDescriptor;
- (void)setResultDescriptor:(NSAppleEventDescriptor*)inResultDescriptor;
- (NSDictionary*)errorDict;
- (void)setErrorDict:(NSDictionary*)inErrorDict;
- (NSAppleEventDescriptor *) executeOnMainThreadReturningError:
(NSDictionary **) error;
@end
@implementation KKNSAppleScript
- (NSAppleEventDescriptor*)resultDescriptor
{
return _resultDescriptor;
}
- (void)setResultDescriptor:(NSAppleEventDescriptor*)inResultDescriptor
{
if (_resultDescriptor != inResultDescriptor) {
[_resultDescriptor release];
_resultDescriptor = [inResultDescriptor retain];
}
}
- (NSDictionary*)errorDict
{
return _errorDict;
}
- (void)setErrorDict:(NSDictionary*)inErrorDict
{
if (_errorDict != inErrorDict) {
[_errorDict release];
_errorDict = [inErrorDict retain];
}
}
- (void) dealloc
{
[self setResultDescriptor: nil];
[self setErrorDict: nil];
[super dealloc];
}
- (void) mainThreadExecute:(id) ignored
{
NEWPOOL(pool);
NSDictionary * error = nil;
NSAppleEventDescriptor * aed = [self executeAndReturnError: &error];
[self setErrorDict: error];
[self setResultDescriptor: aed];
CLOSEPOOL(pool);
}
- (NSAppleEventDescriptor *) executeOnMainThreadReturningError:
(NSDictionary **) error
{
id temp;
[self performSelectorOnMainThread: @selector(mainThreadExecute:)
withObject: self waitUntilDone: YES];
*error = [[[self errorDict] retain] autorelease];
temp = [[[self resultDescriptor] retain] autorelease];
[self setResultDescriptor: nil];
[self setErrorDict: nil];
return temp;
}
@end -
As I recall, the problem is more extensive than just whether you can
get AppleScript itself to be thread-safe. Since the user may invoke
any scripting addition (including standard additions) via their
script, you have to trust that the scripting additions are themselves
all thread safe. This problem is similar to the QuickTime vs. QT
Components issue. Things are only thread safe if you can limit
exactly what gets used.
For instance, I think even the "display dialog" standard addition
command leads to problems if used on a thread other than the main
thread.
As far as I can tell there are two safe approaches to "multitasking
AppleScript":
1. Always run scripts on a separate process.
2. Mess around with AppleScript/AppleEvents callbacks that give you
time to continue running the event loop while a script is running.
In my experience #2 does not give an ideal user experience, while #1
does not give ideal performance.
Daniel
On May 22, 2006, at 2:02 AM, Nathan Day wrote:
> All NSAppleScript instance seem to use the same ComponentInstance> 20Code/NDScript.dmg-binhex.hqx>
> also the ComponentInstance may also be used by the main thread, to
> set up the application to be AppleScriptable perhaps. Each thread
> must have its own AppleScript ComponentInstance, in theory this you
> could have multiple thread each running there own AppleScript, but
> I have not been able to get this to work only one AppleScript in a
> seperate thread to the main thread. NDAppleScriptObject lets you do
> this, though you are probable better using my replace classes for
> NDAppleScriptObject, NDScriptData and its descendent class
> NDScriptHandler and NDScriptContext, I haven't made them available
> on my web site properly yet as I have not completed the
> documentation for them I also want to add some more feature and get
> the multi threading working properly, ie more than one thread
> running AppleScripts. You can download it here
> <http://homepage.mac.com/nathan_day/.cv/nathan_day/Public/Source%
>
> On Monday, May 22, 2006, at 01:25AM, Mike Abdullah
> <mike.abdullah...> wrote:
>
>>
>> <<Original Attached>>
>
>
> Nathan Day
> <nathan_day...>
> http://homepage.mac.com/nathan_day/
> _______________________________________________
> Do not post admin requests to the list. They will be ignored.
> Cocoa-dev mailing list (<Cocoa-dev...>)
> Help/Unsubscribe/Update your Subscription:
> http://lists.apple.com/mailman/options/cocoa-dev/<jalkut...>-
> sweater.com
>
> This email sent to <jalkut...>
-
My ComponentInstance class than comes with NDAppleScriptObject allows
you to catch every NSAppleEvent that the AppleScript attempts to
send, you can then resend it yourself in the main thread.
On 30/05/2006, at 3:24 AM, Daniel Jalkut wrote:
> As I recall, the problem is more extensive than just whether you>> 20Code/NDScript.dmg-binhex.hqx>
> can get AppleScript itself to be thread-safe. Since the user may
> invoke any scripting addition (including standard additions) via
> their script, you have to trust that the scripting additions are
> themselves all thread safe. This problem is similar to the
> QuickTime vs. QT Components issue. Things are only thread safe if
> you can limit exactly what gets used.
>
> For instance, I think even the "display dialog" standard addition
> command leads to problems if used on a thread other than the main
> thread.
>
> As far as I can tell there are two safe approaches to "multitasking
> AppleScript":
>
> 1. Always run scripts on a separate process.
> 2. Mess around with AppleScript/AppleEvents callbacks that give you
> time to continue running the event loop while a script is running.
>
> In my experience #2 does not give an ideal user experience, while
> #1 does not give ideal performance.
>
> Daniel
>
> On May 22, 2006, at 2:02 AM, Nathan Day wrote:
>
>> All NSAppleScript instance seem to use the same ComponentInstance
>> also the ComponentInstance may also be used by the main thread, to
>> set up the application to be AppleScriptable perhaps. Each thread
>> must have its own AppleScript ComponentInstance, in theory this
>> you could have multiple thread each running there own AppleScript,
>> but I have not been able to get this to work only one AppleScript
>> in a seperate thread to the main thread. NDAppleScriptObject lets
>> you do this, though you are probable better using my replace
>> classes for NDAppleScriptObject, NDScriptData and its descendent
>> class NDScriptHandler and NDScriptContext, I haven't made them
>> available on my web site properly yet as I have not completed the
>> documentation for them I also want to add some more feature and
>> get the multi threading working properly, ie more than one thread
>> running AppleScripts. You can download it here
>> <http://homepage.mac.com/nathan_day/.cv/nathan_day/Public/Source%
>>
>> On Monday, May 22, 2006, at 01:25AM, Mike Abdullah
>> <mike.abdullah...> wrote:
>>
>>>
>>> <<Original Attached>>
>>
>>
>> Nathan Day
>> <nathan_day...>
>> http://homepage.mac.com/nathan_day/
>> _______________________________________________
>> Do not post admin requests to the list. They will be ignored.
>> Cocoa-dev mailing list (<Cocoa-dev...>)
>> Help/Unsubscribe/Update your Subscription:
>> http://lists.apple.com/mailman/options/cocoa-dev/<jalkut...>-
>> sweater.com
>>
>> This email sent to <jalkut...>
>
Nathan Day
<nathan_day...>
http://homepage.mac.com/nathan_day/ -
That's interesting - but doesn't that present the undesirable
behavior of an AppleScript blocking on the main thread (while waiting
for an AppleEvent reply). Isn't that one motivation for moving it to
a separate thread in the first place?
Daniel
On May 30, 2006, at 10:18 AM, Nathan Day wrote:
> My ComponentInstance class than comes with NDAppleScriptObject
> allows you to catch every NSAppleEvent that the AppleScript
> attempts to send, you can then resend it yourself in the main thread.
-
Yes, but in practice it doesn't matter, you can filter all the
AppeEvent directed to your own application and only send them in the
main thread, and when your main thread handles an AppleEvent it
either only block very briefly so the user does not notice it or its
for something like display dialog, in which case you are wanting for
the user to supply input.
The point is it does not block for the entire duration of your script.
On 31/05/2006, at 12:42 AM, Daniel Jalkut wrote:
> That's interesting - but doesn't that present the undesirable
> behavior of an AppleScript blocking on the main thread (while
> waiting for an AppleEvent reply). Isn't that one motivation for
> moving it to a separate thread in the first place?
>
> Daniel
>
> On May 30, 2006, at 10:18 AM, Nathan Day wrote:
>
>> My ComponentInstance class than comes with NDAppleScriptObject
>> allows you to catch every NSAppleEvent that the AppleScript
>> attempts to send, you can then resend it yourself in the main thread.
>
Nathan Day
<nathan_day...>
http://homepage.mac.com/nathan_day/


