Multiple stay open "On Idle" scripts running at the same time.

I have two scripts that are stay open apps with on idle handlers. Basically they both keep looking at a folder to see if files exist there and then process them in InDesign if they do. Since they both use InDesign, I need the scripts to “take turns” running. I also would like then to remain separate scripts (the two projects need to remain separate - I can’t just lump all my code together for all on idle scripts I write).

So, how can I get the two scripts to “see” if the other one is running and wait to run itself? I can’t use “application exists” because it is a stay open app so it always exists. I feel like I need some external switch. Maybe a plist file? I haven’t scripted those before.

Anyone have any previous experience or ideas?

OS 10.4.8

Both scripts folllow this basic form:

on idle
	set ConversionFolder to "Server1:Conversion:" as alias
		tell application "Finder" to set these_items to (files of (entire contents of ConversionFolder) whose kind contains "Quark")
	if the number of items in these_items is greater than 0 then
		tell me to activate
		display dialog "Files located. Script starting!" buttons " " default button 1 giving up after 1
		my CheckAllServers()
		--Go through all files, one at a time
		repeat with this_item in these_items
			tell application "System Events" to set TheExtension to name extension of (this_item as alias)
			if TheExtension is not "" then
				tell (info for (this_item as alias)) to set FileName to name's text 1 thru ((my (offset of ("." & name extension) in name)) - 1)
			else
				tell (info for (this_item as alias)) to set FileName to name
			end if
			tell application "Finder" to set PathToOriginalFile to container of this_item as string
			set FinalPath to (PathToOriginalFile & FileName & ".indd")
				tell application "Adobe InDesign CS3"
					activate
					open file (this_item as string)
					delay 1
					save document 1 to FinalPath
					tell document 1 to close saving no
				end tell
			tell application "Finder"
				activate
				try
					delete this_item
				end try
			end tell
		end repeat
	end if
	return 10
end idle

Is there some reason you can’t run a single on-idle script that alternates between the two folder watchers? Remember that the return time can be a variable so as each runs it can set the idle time for the next run of the other.

Hi,

this would be a perfect job for a launchd agent, which is able to watch a folder and trigger a script,
if something has been changed. The launchd.plist file looks like this

[code]<?xml version=\"1.0\" encoding=\"UTF-8\"?>

<plist version="1.0">

Label
com.myagent.watchfolder
LowPriorityIO

Program
/usr/bin/osascript
ProgramArguments

osascript
/Path/To/Script.scpt

WatchPaths

/Path/To/Folder/To/Watch


[/code]
Take a look at Craig Smith’s launchd article

I would prefer not to lump different scripts into one script if at all possible. I know that is the easiest way to do it, but these are two projects under two project numbers, etc. so it will make file/project managemtn more complicated if I start mixing everything together.

I have not used launchd. Is it a built-in feature in applescript? If it a separate utility, I do not have control over what software is installed on the Macs in the department…

Thanks for the ideas. Any others??

Model: G5
Browser: Safari 419.3
Operating System: Mac OS X (10.4)

It might be more complicated to write one script, but it’s much more reliable for the workflow instead of let the single scripts controlling each other :wink:

launchd is a part of the operating system since Mac OS X 10.4 Tiger

This is just a shot in the dark, but why couldn’t you have each script write something like ‘Script 1 Finished’ to a text file, and have Script 2 poll the text file before starting its job… and vice verse.

I’m probably in over my head, but…

Peter B.


If there are no documents open while the scripts idle, another option might be for each script to check for the existence of ID windows and pause - this would give the active one time to do its thing. It seems like more of a kludge than some of the other suggestions to me, but it will keep them separate.

In short, and if I understand your requirements correctly, what you want is possible to do, but it I cannot think of an easy way to accomplish it. If Marc Anthony’s idea from post #7 works for you, then you might consider using it.

His check for open documents method will get you a 90% solution. The remaining problems (that I can think of at the moment) are 1) that the two scripts might simultaneously both check and find no open documents and thus both start their processing; 2) in the script you gave as an example, if there are multiple files to process, after one file is closed and before the next one has been opened, no documents will be open, so the other script might start doing its work before the first is finished with all its files; and 3) it is not necessarily “fair” or “starvation free” (see below). The second problem can be ameliorated by opening a “dummy” document of some sort and only closing it after all the other processing is finished (when coupled with the check for open documents, this dummy document functions as a weak kind of mutex (see below)).

As suggested several times in this thread, having all the code that access InDesign rolled into one script is a good way to make sure that only one scripts attempts to access InDesign at any one moment. Actually, you would not even necessarily have to put all the code into one script file to make this happen. You could keep the code for separate projects in separate scripts and create a single “driver” script that uses load script to access the separate pieces of code. This method would require modularizing some of the code, but it would be easy to control which (sub)script would run at what times.

If you cannot abide having everything in one script (or modularizing the code in the individual scripts and creating a single driver that controls their execution), then you are left with (at least) two independent programs that must coordinate their actions. This is the domain of multiprocess synchronization.

If you just want to make sure that neither of your scripts tries to control InDesign at the same moment, you can use a mutex (mutual exclusion lock, sometimes implemented as a semaphore). The idea of a mutex is to ensure that only one competitor (process, application, script, thread, et cetera) can access a shared resource at any one moment. In your case, the shared resource is InDesign and you have two scripts that might want access.

It is possible to build your own mutex (in addition to post #7, Peter Bunn’s idea in post #6 leans this way), but there are many edge cases and potential bugs when it comes to implementing synchronization primitives. For example, the “check for no open documents, open a dummy document, do the processing, close the dummy document” algorithm still probably has a race condition between the check for no open documents and open the dummy document steps (after opening the dummy document, your script would have to make sure it was the only script that asked for the dummy document to be opened, or if each script has its own dummy document, that the script’s dummy document is still the only document open after a short pause). So, it is usually best to use something provided by the OS, if possible. Mac OS X provides low-level, interprocess synchronization primitives, but I do not know of any OSAX or FBA that provides access to those synchronization APIs.

There are ways of synchronizing through the filesystem that have been in use on UNIXish systems long before the current crop of interprocess synchronization APIs were available. A program that makes some of these locking facilities available to shell scripts is lockfile. It is part of the procmail suite of programs, which is geared towards handling local mail delivery, but lockfile is fairly generic nonetheless. The BOM file of the Essentials package of my 10.3 and 10.4 installation media lists lockfile (along with stuff like, oh, Finder), so it should be already installed on your 10.4 system.

So, you could use lockfile to provide a mutex. But there are still further considerations. Using a simple mutex will only guarantee that the cooperating scripts both do not try to control InDesign at the same time. It will provide no guarantee of “fairness” (each competitor gets access the shared resource within a reasonable amount of time) nor will it prevent the more extreme situation of “starvation” (where one competitor never gets access to the shared resource even though it is ready to use it). Even if you were to implement and use a simple mutex system, it is impossible to find out if it is fair or starvation-free just by running it (such is the nature of many ad hoc multiprocess synchronization schemes). Practically, these issues are probably not a problem, but they may be depending on exactly what you want. If you needed to guarantee fairness (or lack of starvation), you could involve yet another script to act as a scheduler. I have a design in mind for a lockfile based mutex without a scheduler, but it is beyond what I would implement on a whim, not knowing whether it would actually be useful.

Hopefully, this information is useful (even if you can not copy-and-paste it into your code).

Hi,
I thought I might as well throw my idea into the melting pot, so here goes.
How about having two folders A and B and one file TriggerFile? Have Script A search folder A for the TriggerFile, if it finds the file then run the rest of your script, then the last thing the script should do is to move the TriggerFile into Folder B, allowing Script B to process if it finds the TriggerFile.
Thanks,
Nik

I am back to trying this again. I came up with an idea that I thought would work, but it does not.

set AppSwitchTextFile to ((path to application support from user domain as text) & "App_Switcher.txt")

set the OpenFile to open for access file AppSwitchTextFile
set idleOrActive to read OpenFile
close access file AppSwitchTextFile
repeat while idleOrActive is not "Idle"
	set the OpenFile to open for access file AppSwitchTextFile
	set idleOrActive to read OpenFile
	close access file AppSwitchTextFile
	delay 2
end repeat

set the OpenFile to open for access file AppSwitchTextFile with write permission
set eof of the OpenFile to 0
write "AppSwitchTest" to the OpenFile starting at eof
close access file AppSwitchTextFile

delay 10

set the OpenFile to open for access file AppSwitchTextFile with write permission
set eof of the OpenFile to 0
write "Idle" to the OpenFile starting at eof
close access file AppSwitchTextFile

When I open this in two different Script Editor windows and then run one, wait a second, and then run the other, I expected the first instance to finish running after about 10 seconds and then the second one would finish in about 10 seconds. What happens is the first one never finishes. I open the text file and it says “AppSwitchTest” so it completed that but never completes the final write out of “Idle” to the text file.

Any ideas? I feel like this would work pretty consistently for the original problem if it worked as I expected it to…

Edit: One additional note. This works if you just run one instance of it. It seems to be a problem when you run it in a couple of windows. Maybe there is a problem with multiple scripts accessing the same text file?? Seems hard to believe they would be accessing them at exactly the same time all the time.

Model: G5 OSX 10.4.8
Browser: Safari 419.3
Operating System: Mac OS X (10.4)

Have you considered having OnIdle#1 tell OnIdle#2 to quit, then do it’s thing, then launch OnIdle#2, and vice versa? Are the timings very different? – I ask because when first run, either of them will do the on idle tasks immediately.

Hmmm. I didn’t know you could do that. I will look into it if my current solution doesn’t work.

For the record, my current idea is to use the fact that a text file cannot be opened for access by more than one script at a time. I am still testing but so far it is working:

set AppSwitchTextFile to ((path to application support from user domain as text) & "App_Switcher.txt")

try
	set the OpenFile to open for access file AppSwitchTextFile with write permission
	set idleOrActive to "Idle"
on error
	set idleOrActive to "Active"
end try

repeat while idleOrActive is not "Idle"
	try
		set the OpenFile to open for access file AppSwitchTextFile with write permission
		set idleOrActive to "Idle"
	on error
		set idleOrActive to "Active"
	end try
	delay 5
end repeat

display dialog "Running" giving up after 1
delay 5

close access file AppSwitchTextFile