How to stop 2 or 3 Applescripts that are running simultaneously from playing tug of war w/each other

Hi, I sometimes have 2 or 3 applescripts running at the same time with different intervals, from 5 mins to 8 hours that trigger Automator “Watch Me Do” applications. Inevitably the countdown timers will have more than one run at the same time and they play “tug of war” with each other with no way to stop them.

My question: Is there code that can be added to the Applescripts below that would allow for the scripts to wait their turn, if one is already running? Each of these scripts are saved as Applications with the “Stay open after run Handler” box checked.

on idle
	tell application "System Events"
		tell application "Pickup5min"
			run
		end tell
		return 306
	end tell
end idle

on idle
	tell application "System Events"
		tell application "Pickup4hr"
			run
		end tell
		return 14400
	end tell
end idle

on idle
	tell application "System Events"
		tell application "Pickup8hr"
			run
		end tell
		return 28800
	end tell
end idle

This hander should tell you if the applet is running (including idle)

Does that help?

set appNames to {"Pickup4hr", "Pickup5min", "Pickup8hr"}
repeat with appletName in appNames
   my IsThisAppletRunning(appletName)
end repeat

on IsThisAppletRunning(processName)
   tell application "System Events"
      try
         get process processName
         return true
      on error errMsg number errNum
         {errMsg, errNum}
         if errNum = -1728 then
            return false
         else
            return missing value
         end if
      end try
   end tell
end IsThisAppletRunning

Hi Ed, thanks for the reply. I did 2 test scripts with your code added, shown below. It took awhile for them to conflict…they wont do it when you want them to, but both scripts did eventually run at the same time and wildly pulled my cursor around until they finished their steps. Could a “Delay” command be inserted for say 20secs? That would give the already running script time to finish. Does your code go in before the “on idle” or after “end idle”?

set appNames to {“Pickup5 1”, “Pickup5 2”}
repeat with appletName in appNames
my IsThisAppletRunning(appletName)
end repeat

on IsThisAppletRunning(processName)
tell application “System Events”
try
get process processName
return true
on error errMsg number errNum
{errMsg, errNum}
if errNum = -1728 then
return false
else
return missing value
end if
end try
end tell
end IsThisAppletRunning
on idle
tell application “System Events”
tell application “Pickup5 1”
run
end tell
return 308
end tell
end idle

set appNames to {“Pickup5 1”, “Pickup5 2”}
repeat with appletName in appNames
my IsThisAppletRunning(appletName)
end repeat

on IsThisAppletRunning(processName)
tell application “System Events”
try
get process processName
return true
on error errMsg number errNum
{errMsg, errNum}
if errNum = -1728 then
return false
else
return missing value
end if
end try
end tell
end IsThisAppletRunning
on idle
tell application “System Events”
tell application “Pickup5 2”
run
end tell
return 308
end tell
end idle

If you use FastScripts to run your scripts, rather than the shell, then you can monitor your running script tasks both via the menu and programmatically.

I’m using this to run my own ‘pomodoro’-style timer (to remind myself to take breaks, etc.) and it works well. Easy to cancel running scripts or prevent certain scripts from running concurrently.

I would try something like this:

on idle
   if not IsThisAppletRunning("Pickup5 1") then
      tell application "BBEdit"
         run
      end tell
      return 308
   else if not IsThisAppletRunning("Pickup5 2") then
      tell application "BBEdit"
         run
      end tell
      return 308
   end if
   return 308
end idle

Probably want to change that missing value in the handler to a boolean too:

on IsThisAppletRunning(processName)
   tell application "System Events"
      try
         get process processName
         return true
      on error errMsg number errNum
         {errMsg, errNum}
         if errNum = -1728 then
            return false
         else
            return false
         end if
      end try
   end tell
end IsThisAppletRunning

Since you’re making edits, I’d recommend changing the entire handler:

on appletIsRunniingWithName:A
		the application named A is running
end appletIsRunniingWithName:

Although it’s possibly preferable to formulate this with the id key reference forms:

on appletIsRunniingWithId:key
		the application id key is running
end appletIsRunniing

@Firehawk, a common way of keeping track of things like this is to write out to a file specific values that you can read back at any time to determine whether any of the applets are executing, which ones, or even whether they are in idle mode vs run mode. You just add lines at the start and end of each applet’s run handler that writes to disk with the updated state of the particular applet.

Another method would be to implement a fourth applet to act as a controller, which would negate the need for the slave applets to have their own idle handlers, instead yielding control over launching, quitting, executing, etc. to the controlling applet for all three slaves. Although there is a built-in controller that monitors and schedules process execution, which is launchd.

And, finally, one last solution might be to choose intervals for the three applets that aren’t ever going to coincide with one another. It’s probably not possible to prevent complete overlapping of an entire run loop, which will vary in length between executions—although, if you think 20 seconds is an upper bound on execution times for any single applet, you probably could actually. You can certainly prevent coincidental launches by scheduling the first launch for each applet to occur at intervals whose differences are pairwise coprime to the intervals themselves. For your applets that run at intervals of 5ʹ, 4h, and 8h, then launching them at t=0, 2ʹ, and 3ʹ respectively would avoid overlap provided they don’t run for more than 1 minute.

2 Likes

I like that handler!

1 Like

Thanks for the replies!
There are a couple things I need to clarify. I am a novice at Applescript. I often have 3,4, or 5 scripts running at the same time, so ending scripts when another is running would mean I must restart the ended script manually.
So with that in mind, would it be possible to monitor each scripts countdown timer and display it’s name and time left in the Mac’s Title bar? Then while a script is running, poll the other scripts remaining time to activation. If any scripts are within say 15 secs to activate, pause it by adding 10 secs to it, or something to that effect so the script would continue to run. Thanks for any help with this!!

Sorry for the late reply. Interested to know what progress you made with this.

What do these scripts do ? Precisely ? I’m asking because a) you stated you are a novice, and b) the way you describe the set up seems incredibly complicated and incredibly fragile. Therefore, it’s probably worth seeing if there’s another way to approach the task(s) you’re trying to accomplish.

It also makes me think these are scripts that rely heavily on things being in the right place at the right time, e.g. as when a script is issuing clicks and keystrokes to interact with the UI. hence being unable to execute simultaneously.

There’s a chance there may be another way that doesn’t involve relying on scripting the UI. And, even if not, there’s probably a number of ways to improve the scripts themselves. The nature of trying to interact with UI means that, as a technique, it’s inherently very fragile, and this will always be true, but there’s a lot of fragility that comes purely from how a script is written, and that will be a useful thing to review to ensure it’s not masochistically putting obstacles in its path just to try and overcome them.