I want to automate the execution of a long-running job. It should be launched once per day but only if the prior job isn't still running. Can I accomplish this with launchd and how?
launchd ensures you only have one instance of the "agent" running and it does this for you automatically. Set your job up normally using StartInterval to schedule the execution of your background job and the system won't launch a new instance if the previous instance hasn't completed by the time it gets to that scheduled start interval.
If multiple schedule intervals pass,
launchd will run just one new instance at the next interval. It's "smart" about it.
Here's a sample plist:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd "> <plist version="1.0"> <dict> <key>Label</key> <string>com.mycompany.test</string> <key>ProgramArguments</key> <array> <string>/some/path/to/a/script.sh</string> </array> <key>StartInterval</key> <integer>60</integer> </dict> </plist>
In the above example, if
/some/path/to/a/script.sh takes 361 seconds to run,
launchd won't start 5 new instances during that time frame, and it will only start one new instance at the 420'th second mark in time.
The wording on the launchd.plist(5) man page says as much, but the way it's written kind of makes it non-obvious:
This optional key causes the job to be started every N seconds. If the system is asleep, the job will be started the next time the computer wakes up. If multiple intervals transpire before the computer is woken, those events will be coalesced into one event upon wake from sleep.
That last sentence essentially says "
launchd makes your jobs singleton for you as long as you follow the rules for good job behaviour".