I have a daemon that launchd runs at system boot (OS X). I need to delay startup of my daemon by 3-5 seconds, yet the following code executes instantly at boot, but delays properly well after boot:
#include <unistd.h>
...
printf("Before delay\n");
unsigned int delay = 3000000;
while( (delay=usleep(delay)) > 0)
{
;
}
printf("After delay\n");
If I run it by hand after the system has started, it delays correctly. If I let launchd start it at boot the console log shows that there is no delay between Before delay and After delay – they are executed in the same second.
If I could get launchd to execute my daemon after a delay after boot that would be fine as well, but my reading suggests that this isn’t possible (perhaps I’m wrong?).
Otherwise, I need to understand why usleep isn’t working, and what I can do to fix it, or what delay I might be able to use instead that works that early in the boot process.
First things first. Put in some extra code to also print out the current time, rather than relying on
launchdto do it.It’s possible that the different flushing behaviour for standard output may be coming into play.
If standard output can be determined to be an interactive device (such as running it from the command line), it is line buffered – you’ll get the "before" line flushed before the delay.
Otherwise, it’s fully buffered so the flush may not happen until the program exits (or you reach the buffer size of (for example) 4K. That means that
launchdmay see the lines come out together, both after the delay.Getting the C code to timestamp the lines will tell you if ths is the problems, something like:
To see why the buffering may be a problem, consider running that program above as follows:
You can see that, because the buffering causes both lines to appear to the
whileloop when the program exits, they get the same timestamp of12:59:24despite the fact they were generated three seconds apart within the program.In fact, if you change it as follows:
you can see the time seen by the "surrounding" program (the
whileloop or, in your case,launchd) is totally disconnected from the program itself).Secondly,
usleepis a function that can fail! And it can fail by returning -1, which is very much not greater than zero.That means, if it fails, your delay will be effectively nothing.
The Single UNIX Specification states, for
usleep:That’s certainly the case with your code although it would be hard to explain why it works after boot and not before.
Interestingly, the Mac OSX docs don’t list EINVAL but they do allow for EINTR if the sleep is interrupted externally. So again, something you should check.
You can check those possibilities with something like:
One other thing I’ve just noticed, from your code you seem to be assuming that
usleepreturns the number of microseconds unslept (remaining) and you loop until it’s all done, but that behaviour is not borne out by the man pages.I know that
nanosleepdoes this (by updating the passed structure to contain the remaining time rather than returning it) butusleeponly returns 0 or -1.The
sleepfunction acts in that manner, returning the number of seconds yet to go. Perhaps you might look into using that function instead, if possible.In any case, I would still run that (last) code segment above just so you can ascertain what the actual problem is.