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.
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
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
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.
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