Can Launchd Run Programs More Frequently Than Every 10 Seconds?

launchdmacos

I have some services like this that I'd like to run almost immediately after files are modified.

<?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>test</string>
    <key>ProgramArguments</key>
    <array>     
        <string>say</string>
        <string>a</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>/Users/username/Desktop/</string>
    </array>
</dict>
</plist>

Even if ThrottleInterval was set to 1 or 0, they are only run at most every 10 seconds.

9/9/12 4:57:05.457 PM com.apple.launchd.peruser.501[138]: (test) Throttling respawn: Will start in 7 seconds
9/9/12 4:57:09.541 PM com.apple.launchd.peruser.501[138]: (test) Throttling respawn: Will start in 3 seconds

man launchd.plist only says that programs aren't run more than every 10 seconds by default, but doesn't mention that ThrottleInterval couldn't be set below that.

ThrottleInterval <integer>
This key lets one override the default throttling policy imposed on jobs by launchd.
The value is in seconds, and by default, jobs will not be spawned more than once
every 10 seconds.  The principle behind this is that jobs should linger around just
in case they are needed again in the near future. This not only reduces the latency
of responses, but it encourages developers to amortize the cost of program invoca-
tion.

You could keep the program or script running for 10 seconds and watch for changes every second:

#!/bin/bash

start=$(date +%s)
prev=

until (( $(date +%s) >= $start + 10 )); do
    new=$(stat -f %m ~/Desktop/)
    [[ $prev != $new ]] && say a
    prev=$new
    sleep 1
done

Or the same in Ruby:

#!/usr/bin/env ruby

start = Time.now
prev = nil

until Time.now >= start + 10
  current = File.mtime("#{ENV['HOME']}/Desktop/")
  `say a` if current != prev
  prev = current
  sleep 1
end

But is there some way to bypass or decrease the time limit? It also applies to folder actions.

Best Answer

There is no way to bypass or decrease the time limit.

Apple's documentation regarding Creating Launchd Jobs states the following:

Important If your daemon shuts down too quickly after being launched, launchd may think it has crashed. Daemons that continue this behavior may be suspended and not launched again when future requests arrive. To avoid this behavior, do not shut down for at least 10 seconds after launch.

Your program or script needs to keep running for at least 10 seconds. Consider implementing a loop to check for file modification dates in the last ten seconds, sleeping for ten seconds, and repeating.

Alternatively, you can watch specific files using the kqueue or FSEvents APIs. This StackOverflow question may be helpful, File-level filesystem change notification in Mac OS X.