Sunday, December 17, 2017

#1 2015-10-04 04:55:12 pm

Joseph25b
Member
Registered: 2015-08-31
Posts: 31

Dealing with background process

Hello,

Let's figure this schematic script :

Applescript:


on mainHandler_(sender)
   -- do some stuff
   my performSelectorInBackground_withObject_("BkgndProcess", missing value)
   -- …
end mainHandler_

on BkgndProcess()
   -- several actions, actually preformed
   repeat -- the requested times
       set calulatedValue to my subHandler(var1, var2, var3) -- FAILS to call the routine
       -- following code : not executed
   end repeat
   -- finishing statements : not executed
end BkgndProcess

on subHandler(param1, param2, param3)
   -- here processing for tempResult
   return tempResult
end subHandler

Such script will run perfectly if not using routines in the background,
but written as above the handler sent in the bkgnd is unable to call its child handler.
Where am I wrong?


Joseph from France
MacPro 10.7.5 - XCode 4.6.3

Offline

 

#2 2015-10-04 06:34:32 pm

DJ Bazzie Wazzie
Member
From:: the Netherlands
Registered: 2004-10-20
Posts: 2727
Website

Re: Dealing with background process

When performing a selector in the background it will lose scope. So the best way is sending itself as the argument to the selector and make calls from that argument object. Pretty much like IBAcions

Applescript:

on mainHandler_(sender)
-- do some stuff
my performSelectorInBackground_withObject_("BkgndProcess", me)
-- …
end mainHandler_

on BkgndProcess_(sender)
-- several actions, actually preformed
repeat -- the requested times
set calulatedValue to sender's subHandler(var1, var2, var3) -- FAILS to call the routine
-- following code : not executed
end repeat
-- finishing statements : not executed
end BkgndProcess

on subHandler(param1, param2, param3)
-- here processing for tempResult
return tempResult
end subHandler

Offline

 

#3 2015-10-04 07:28:07 pm

Joseph25b
Member
Registered: 2015-08-31
Posts: 31

Re: Dealing with background process

DJ Bazzie Wazzie wrote:

When performing a selector in the background it will lose scope. So the best way is sending itself as the argument to the selector and make calls from that argument object. Pretty much like IBAcions

Applescript:

on mainHandler_(sender)
-- do some stuff
my performSelectorInBackground_withObject_("BkgndProcess", me)
-- …
end mainHandler_


I"m new at Xcode's, I could not figure such subtleness! I'll try it.
Thank you
Best regards


Joseph from France
MacPro 10.7.5 - XCode 4.6.3

Offline

 

#4 2015-10-05 07:48:44 am

Joseph25b
Member
Registered: 2015-08-31
Posts: 31

Re: Dealing with background process

Hello,

DJ Bazzie Wazzie wrote:

When performing a selector in the background it will lose scope. So the best way is sending itself as the argument to the selector and make calls from that argument object. Pretty much like IBAcions

Applescript:

on mainHandler_(sender)
-- do some stuff
my performSelectorInBackground_withObject_("BkgndProcess", [b]me[/b])
-- …
end mainHandler_

on BkgndProcess_(sender)
-- several actions, actually preformed
repeat -- the requested times
set calulatedValue to [b]sender's[/b] subHandler(var1, var2, var3) -- FAILS to call the routine
etc.


Unfortunately I failed to make it works. The code stops at the same place when the loop is calling for the first time the /subhandler/.

For those who are interested, this the context of the problem:
It's an app which ran fine since Lion up to Yosemite.
Because of a possibly time consuming loop I put a floating "progress-window", in front of the main window, showing a progress bar and a counter. Cancelling the process was available by pressing Cmd+Ctrl keys.
All done in the unique thread.

Now with El Capitan the same app still achieves well its job but the "progress-window" doesn't appear until the loops are over. Anyway, canceling-keys still work and at this moment the window is shown with the bar and counter stopped at the state of the progression.

And another info : tested on the same machine, the original app (same version and code) was fast with Mavericks, almost 50% slower with Yosemite and regained its speed (even lightly faster than with Mavericks) when running under El Capitan. Unfortunately, there is this issue with user interface.

So I tried to use a threaded process, sending in the background the heavy work and calling UI display on a front thread. But yet I'm stuck with handlers which don't perform in background…

Someone has an idea to get around the problem?


Joseph from France
MacPro 10.7.5 - XCode 4.6.3

Offline

 

#5 2015-10-05 05:29:42 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5200

Re: Dealing with background process

Joseph25b wrote:

So I tried to use a threaded process, sending in the background the heavy work and calling UI display on a front thread.


That's what you would do in, say, Objective-C, but it doesn't work in AppleScript. The problem is that your application has one instance of AppleScript, and it can only do one thing at a time -- so when you try to run two threads, one is paused until the other is finished.

Have a look at chapter 13 of the 5th edition of my book. That's about the best you can do, I think.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Offline

 

#6 2015-10-05 06:30:20 pm

Joseph25b
Member
Registered: 2015-08-31
Posts: 31

Re: Dealing with background process

Hello Shane,

Shane Stanley wrote:

The problem is that your application has one instance of AppleScript, and it can only do one thing at a time -- so when you try to run two threads, one is paused until the other is finished.

Have a look at chapter 13 of the 5th edition of my book. That's about the best you can do, I think.


OK, unless I get rid of the process bar I have to try to apply the workaround you explain in your book…
Too bad, I succeeded to avoid it until El Capitan hmm
Thanks!


Joseph from France
MacPro 10.7.5 - XCode 4.6.3

Offline

 

#7 2015-10-06 08:41:09 am

DJ Bazzie Wazzie
Member
From:: the Netherlands
Registered: 2004-10-20
Posts: 2727
Website

Re: Dealing with background process

Joseph25b wrote:

Because of a possibly time consuming loop I put a floating "progress-window", in front of the main window, showing a progress bar and a counter. Cancelling the process was available by pressing Cmd+Ctrl keys.
All done in the unique thread.


As Shane mentioned there is one single instance of AppleScript so you can perform a selector in the background but AppleScript will block itself. Too bad the whole debugging tools of OpenScripting are removed from the cocoa/coreservices framework because you could create another AppleScript component then and then run it in there (also break-point could be supported again in Xcode), the only bottleneck would be the single instance of the event manager.

Since you mentioned you wanted to create a progress view, you could also use NSTask to run the executed code in another process using osascript (separate script). The process has probably nothing to do with your application itself and your app is nothing more than monitoring how far it exactly is right? What you need to do is looping on the main thread and reading output from your script using an NSTimer.

At least that is how I run my scripts on the background when using AppleScriptObjC. For feedback from the backrgound script into the application i use a fifo file and read it using NSFileHandle. This way I'm sure that all data from the script is only read once, you could use a file and only read the last data. The information in the file is uncompiled AppleScript code which can easily be compiled using the run script command and used right way.

Offline

 

#8 2015-10-06 05:55:06 pm

Joseph25b
Member
Registered: 2015-08-31
Posts: 31

Re: Dealing with background process

DJ Bazzie Wazzie wrote:

Too bad the whole debugging tools of OpenScripting are removed from the cocoa/coreservices framework because you could create another AppleScript component then and then run it in there (also break-point could be supported again in Xcode), the only bottleneck would be the single instance of the event manager.

Since you mentioned you wanted to create a progress view, you could also use NSTask to run the executed code in another process using osascript (separate script). The process has probably nothing to do with your application itself and your app is nothing more than monitoring how far it exactly is right? What you need to do is looping on the main thread and reading output from your script using an NSTimer.

At least that is how I run my scripts on the background when using AppleScriptObjC. For feedback from the backrgound script into the application i use a fifo file and read it using NSFileHandle. This way I'm sure that all data from the script is only read once, you could use a file and only read the last data. The information in the file is uncompiled AppleScript code which can easily be compiled using the run script command and used right way.


Ouch! So many informations which have to make their way in my poor brain smile
Anyway I'll try to explore that method which will seem the simplest for me.

Best Regards.


Joseph from France
MacPro 10.7.5 - XCode 4.6.3

Offline

 

#9 2015-10-06 06:03:59 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5200

Re: Dealing with background process

DJ Bazzie Wazzie wrote:

you could create another AppleScript component then and then run it in there


DJ,

If I'm understanding you correctly, you can create another AS component now using OSAKit. But last time I played with it, it still exhibited similar problems.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Offline

 

#10 2015-10-06 07:24:08 pm

DJ Bazzie Wazzie
Member
From:: the Netherlands
Registered: 2004-10-20
Posts: 2727
Website

Re: Dealing with background process

Joseph25b wrote:

Ouch! So many informations which have to make their way in my poor brain smile
Anyway I'll try to explore that method which will seem the simplest for me.


If you pm me your mail address I can send you a clean small xcode project with what I meant.


Shane Stanley wrote:

If I'm understanding you correctly, you can create another AS component now using OSAKit. But last time I played with it, it still exhibited similar problems.


I was maybe too brief. The component can still be created but with the 'old' framework you could call and run certain handlers from specific script objects inside components. Then you would be able to fork an component and it would feel like it's running in the background. Or am I missing something and is it still possible?

Offline

 

#11 2015-10-06 10:59:33 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5200

Re: Dealing with background process

DJ Bazzie Wazzie wrote:

I was maybe too brief. The component can still be created but with the 'old' framework you could call and run certain handlers from specific script objects inside components. Then you would be able to fork an component and it would feel like it's running in the background. Or am I missing something and is it still possible?


I'm not sure we're on the same track or not. This shows the approach I'm talking about:

Applescript:

use scripting additions
use framework "Foundation"
use framework "OSAKit"

on doStuff()
   -- create a new AppleScript instance
   set anOSALanguageInstance to current application's OSALanguageInstance's languageInstanceWithLanguage:(current application's OSALanguage's defaultLanguage())
   -- make script and run it from the new instance
   set theSource to "use framework \"Foundation\"
repeat with i from 1 to 20
delay 0.1
current application's NSLog(\"On second instance, second thread %d\", i) -- writes to Console
end repeat"

   set theScript to current application's OSAScript's alloc()'s initWithSource:theSource fromURL:(missing value) languageInstance:anOSALanguageInstance usingStorageOptions:(current application's OSANull)
   set {theResult, theError} to theScript's compileAndReturnError:(reference)
   theScript's executeAndReturnError:(reference)
end doStuff

on doItInTheBackground()
   -- call the above handler on a separate background thread
   my performSelectorInBackground:"doStuff" withObject:(missing value)
   current application's NSLog("doStuff has been called") -- writes to Console
end doItInTheBackground

-- set the above to run after 1 second
my performSelector:"doItInTheBackground" withObject:(missing value) afterDelay:1
-- start a repeat loop
repeat with i from 1 to 20
   delay 0.1
   current application's NSLog("On default instance, first thread %d", i) -- writes to Console
end repeat

Even though the second loop is run on a separate instance on a background thread, the foreground script stops while it runs.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Offline

 

#12 2015-10-07 09:12:10 am

DJ Bazzie Wazzie
Member
From:: the Netherlands
Registered: 2004-10-20
Posts: 2727
Website

Re: Dealing with background process

Shane Stanley wrote:

Even though the second loop is run on a separate instance on a background thread, the foreground script stops while it runs.


I understand what you're saying now. The outgoing call, with the immediate incoming call to the same AppleScript component that is only loaded once by AppleScriptObjC framework will not allow you run threads in the background. The lock forces AppleScript to wait until the current stack call is finished in line. Threads will behave like a normal LIFO system and scheduled tasks will behave FIFO. So it's more an AppleScriptObjC issue rather than an AppleScript issue right? In one single instance of AppleScript, multiple components can be ran at the same time in a preemptive manner, it's AppleScriptObjC that doesn't allow it.

- (void) doStuff:(NSNumber *)userInfo
{

   
    //make script and run it from the new instance
    static NSString *theSource = @\"repeat 20 times\\n\\tdelay 0.25\\nend repeat\";
    id theScript = [[OSAScript alloc] initWithSource:theSource];
   
    NSMutableDictionary *reference = [[NSMutableDictionary alloc] init];
   
    [theScript compileAndReturnError:&reference];
    NSLog(@\"DoStuff: %i started\", [userInfo intValue]);
    [theScript executeAndReturnError:&reference];
    NSLog(@\"DoStuff: %i done\", [userInfo intValue]);
    return;
}

- (void) doStuffForTimer:(NSTimer *)timer
{
    [self doStuff:timer.userInfo];
    return;
}


- (void) threadTest
{
    // Use thread
    [NSThread detachNewThreadSelector:@selector(doStuff:) toTarget:self withObject:[NSNumber numberWithInt:1]];
    sleep(1);
    [NSThread detachNewThreadSelector:@selector(doStuff:) toTarget:self withObject:[NSNumber numberWithInt:2]];
   
    // Use Perform selector
    [self performSelectorInBackground:@selector(doStuff:) withObject:[NSNumber numberWithInt:3]];
    sleep(1);
    [self performSelectorInBackground:@selector(doStuff:) withObject:[NSNumber numberWithInt:4]];
   
    // Use a timer
    [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(doStuffForTimer:) userInfo:[NSNumber numberWithInt:5] repeats:NO];
    [NSTimer scheduledTimerWithTimeInterval:3.5 target:self selector:@selector(doStuffForTimer:) userInfo:[NSNumber numberWithInt:6] repeats:NO];
   
    //user perform selector with delay
    [self performSelector:@selector(doStuff:) withObject:[NSNumber numberWithInt:7] afterDelay:4.0];
    sleep(1);
    [self performSelector:@selector(doStuff:) withObject:[NSNumber numberWithInt:8] afterDelay:4.5];
}


As you can see NSThread en PerformSelector will not block the threads and will run simultanous and not in a LIFO or FIFO queue. The log times between start and stop is a continuously 5 seconds and will not be bothered by other threads in the order they started. So in some sort of way it works preemptive. However for scheduled tasks, it will do a full block in FIFO mode as you have described because they ran on the default thread.

Last edited by DJ Bazzie Wazzie (2015-10-08 06:07:36 am)

Offline

 

#13 2015-10-07 09:50:34 am

Joseph25b
Member
Registered: 2015-08-31
Posts: 31

Re: Dealing with background process

@ Shane Stanley and DJ Bazzie Wazzie

Well, your discussion reaches height that I'm not ready to follow (I doubt I could ever)…
Anyway my app works fine with or without the progress window, that's only count.
For my modest problem, as soon I get time I'll try the simplest way possible to make that dispensable feature of a progress bar to work.
I certainly won't hit my head against walls to make it happens. I script for fun now I'm a retired man wink

Kinds regards to you.

PS: in relation with this app, I just finished to write a vanilla Applescript calculating Moon eclipses (after Jean Meeus algorithms).
If someone is interested in, I can post it in a MacScripter forum (Which one?). Let me know.


Joseph from France
MacPro 10.7.5 - XCode 4.6.3

Offline

 

#14 2015-10-07 06:05:25 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5200

Re: Dealing with background process

DJ Bazzie Wazzie wrote:

So it's more an AppleScriptObjC issue rather than an AppleScript issue right?


In a sense, yes -- it's only an AppleScript issue in terms of that being the only language the OP is familiar with, and therefore is trying to use.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Offline

 

#15 2015-10-07 06:07:01 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5200

Re: Dealing with background process

Joseph25b wrote:

If someone is interested in, I can post it in a MacScripter forum (Which one?). Let me know.


Code exchange would be ideal.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Offline

 

#16 2015-10-08 06:09:02 am

DJ Bazzie Wazzie
Member
From:: the Netherlands
Registered: 2004-10-20
Posts: 2727
Website

Re: Dealing with background process

Shane Stanley wrote:
DJ Bazzie Wazzie wrote:

So it's more an AppleScriptObjC issue rather than an AppleScript issue right?


In a sense, yes -- it's only an AppleScript issue in terms of that being the only language the OP is familiar with, and therefore is trying to use.


Thanks, I understood the problem but I wanted to make sure.

Offline

 

#17 2015-10-08 07:17:27 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5200

Re: Dealing with background process

DJ Bazzie Wazzie wrote:

Thanks, I understood the problem but I wanted to make sure.


FWIW, this gives a better result:

Applescript:

use scripting additions
use framework "Foundation"
use framework "OSAKit"

-- create a new AppleScript instance
set anOSALanguageInstance to current application's OSALanguageInstance's languageInstanceWithLanguage:(current application's OSALanguage's defaultLanguage())
-- make script and run it from the new instance
set theSource to "use framework \"Foundation\"
repeat with i from 1 to 20
delay 0.001
current application's NSLog(\"On second instance, second thread %d\", i) -- writes to Console
end repeat"

set theScript to current application's OSAScript's alloc()'s initWithSource:theSource fromURL:(missing value) languageInstance:anOSALanguageInstance usingStorageOptions:(current application's OSANull)
set {theResult, theError} to theScript's compileAndReturnError:(reference)

-- set the above running
theScript's performSelectorInBackground:"executeAndReturnError:" withObject:(missing value)

-- start a repeat loop
repeat with i from 1 to 20
   delay 1.0E-3
   current application's NSLog("On default instance, on first thread %d", i) -- writes to Console
end repeat

But I don't see it as at all practical.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Offline

 

#18 2015-10-08 12:21:31 pm

Joseph25b
Member
Registered: 2015-08-31
Posts: 31

Re: Dealing with background process

Shane Stanley wrote:
Joseph25b wrote:

If someone is interested in, I can post it in a MacScripter forum (Which one?). Let me know.


Code exchange would be ideal.


I looked at Code Exchange history and didn't find topic about Moon eclipses, so I'll post it there.
Regards


Joseph from France
MacPro 10.7.5 - XCode 4.6.3

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)