Some of my AppleScripts can run longer. Those are getting the mailboxes from Mail or Outlook. If I run the AppleScript in an async shell and some AddHandlers I’ve managed to avoid the beachball when the scripts are running.
This works fine when I show the mailboxes for selection. Before archiving the email I need to get the mailboxes again. This does not run in a thread. But here neither the DataAvailable nor the Completed event of the shell fire.
Why do the shell events not fire and how do I debug this? I’m doing the mailboxes for Mail here which shouldn’t take longer than a second.
Main code:
ScriptResult = ""
dim theShellScript as string = "nohup /usr/bin/osascript -e '" + ScriptText + "'"
dim theShell as new Shell
theShell.ExecuteMode = Shell.ExecuteModes.Asynchronous
theShell.TimeOut = 1000 * 60 * 10
AddHandler theShell.DataAvailable, AddressOf ShellDataAvailable
AddHandler theShell.Completed, AddressOf ShellCompleted
ShellFinished = False
theShell.execute theShellScript
While not ShellFinished
Thread.SleepCurrent(10)
wend
dim theScript(-1) as string
theScript.Add "tell application id 'com.apple.mail'"
theScript.Add "return name of mailboxes"
theScript.Add "end tell"
theScript.Add("-- Function to replace commas with slash + comma")
theScript.Add("on replaceCommas(inputText)")
theScript.Add("set inputText to inputText as text")
theScript.Add("set text item delimiters to ','")
theScript.Add("set tempList to text items of inputText")
theScript.Add("set text item delimiters to '/comma'")
theScript.Add("set outputText to tempList as text")
theScript.Add("set text item delimiters to ''")
theScript.Add("return outputText")
theScript.Add("end replaceCommas")
No, not possible. The shell is async. The main goal is to get rid of the beachball when doing longer running scripts. My Outlook script takes over 10 seconds.
You’ve got that exactly backwards. An Asynchronous Shell will allow your code to continue after you call Shell.Execute, firing the Shell’s various events (such as DataAvailable).
Asynchronous Shells exist in their own separate process space. This is how Workers are implemented. Synchronous Shells also have their own process space but halt your Xojo app until they have terminated.
Yeah, I don’t see any immediate reason why you’re having issues. My instinct is to move the shell execution code into a Shell subclass and see if that helps, relying on the Shell’s events instead of polling the boolean flag.
Presumably these are 2 methods of the window, and one Boolean property of the window?
Is it possible your logic is mixed up and the ShellCompleted() event is not setting the correct ShellFinished property? (e.g. it’s doing it for the wrong window?)
ShellFinished is a boolean of the class. Here is the code for ShellCompleted and ShellDataAvailable also in the class. Everything is contained in my AppleScript class. Not really much to see:
Private Sub ShellCompleted(theShell as Shell)
#pragma unused theShell
ShellFinished = True
End Sub
Private Sub ShellDataAvailable(theShell as Shell)
ScriptResult = ScriptResult + theShell.ReadAll
End Sub
Scratch that, I think the problem is a fundamental one with the way Xojo handles events:
if you are in a while/wend loop within an Event handler, then no other event handler can run until you exit the event handler. Xojo has a strict(*) rule “only one event handler can run at a time, and it must run to completion”
if you are in a while/wend loop within a Thread (other than the Main thread), then other event handlers will run.
(*) there are some exceptions to this rule if you use ShowModal
So basically, the only way your code can possibly work is if you run it within a Thread (other than the main thread).
From your stack trace, you are in the Main thread:
And the event handler you are within is Toolbar1.Action
I’ll do an example tomorrow first and then I’ll try the thread. Getting the mailboxes is the first step in a very long algorithm. The thread comes later - after I know if something is there to do or not.
You are trying to work synchronous with an shell in asynchronous modus. That absolutely makes no sense. If you want it to be async, than use it that way optherwise not.
There is a queue in Xojo, everythings lives on one thread. When you call shell in sync mode it fires immediately. If you call it in async mode it is put in the queue and becomes some CPU-Time when the actual ttask finishes. But then you go into a loop and the task never finishes. So it never comes to the moment, when the shell gets CPU-time to do its magic.
Besides the fact that such a loop is just fundamentally bad style, it won’t work that way. It’s simply not the way to do it.
You have a fundamental misunderstanding of how Shells operate. Shells are their own process and their CPU usage is not managed by Xojo. They can and will run on a separate CPU, for example, which is how Xojo can take advantage of multiple CPUs. They communicate with Xojo apps via the various Shell events (DataAvailable, Error, etc).
Launching a shell command in asynchronous mode is exactly the correct approach here. It allows the Xojo code to proceed while the shell command executes, reporting back to the main app as it makes progress or comes to a conclusion.
Yes, but xojo needs to have some CPU-Time to give this job to the os.
Here is a short video with the loop stoped after 10 sec. As said, the shell-execution time is very low, but I can change the while-execution-time to 100 sec if you want ;). You can “see” when the os gets the job, after finishing the loop…
That’s not doing what you think it’s doing. You’re sleeping the Main thread, which handles the Shell’s events. It has nothing to do with how the Shell command itself actually executes, or how much CPU the command receives.
This is why polling in Xojo is almost always the wrong way to do things: you get wrapped around the spokes of trying to over-manage the threading model. If you can implement functionality using Events, you are much better off – the framework handles a LOT of hard stuff for you.
Yeah, and it’s not working for her, either, for reasons that aren’t clear (because the same technique reportedly works elsewhere).
I hope the Shell subclass approach works for Beatrix. I’ve done a lot of work in this space and my subclassed Shells tick along just fine. It’s often more work to implement an event-based approach but always worth it, in my experience.