Unix, the Alarm System Call, and Power Management
Published at 20:37 on 25 April 2023
And by “Unix” I include Unix-like operating systems like Linux and MacOS. In fact, my experience is limited to Linux and MacOS in this regard, but I would be surprised if the various BSD and System V Unix systems out there with automatic power management differ much.
I have a simple alarm clock/reminder script I wrote in Python. The heart of it was the following logic:
def sleep_until(then):
delta = then - time.time()
if delta > 0.0:
time.sleep(delta)
return True
return False
Now, the time.sleep call in Python is implemented as a call to sleep in the C standard library, which in turn is implemented as via the alarm system call. All of these accept an offset in seconds, which in the former case specifies the amount of time to sleep, and in the latter the amount of time before an alarm signal is delivered to the process.
The logic above is simplicity itself, yet from time to time my reminders would come in late! Eventually, I linked it to those times when the system suspended itself due to lack of activity for a while; and my alerts were late by an amount that corresponded with the time the system was suspended. Apparently, when Unix and Unix-like systems suspend themselves, time as specified to alarm ceases to pass; that system call only counts seconds that transpire when the system is awake.
The cure is to break up the sleeping into chunks, and to repeatedly check the system clock:
MAX_SLEEP = 60.0
def sleep_until(then):
delta = then - time.time()
if delta <= 0.0:
return False
while delta > 0.0:
time.sleep(min(MAX_SLEEP, delta))
delta = then - time.time()
return True
At least, this seems to work. I implemented the change yesterday and alerts that spanned times when my computer was asleep got raised at the correct time. It’s a little ugly to replace a blocking with busy-waiting like this, but although the above logic busy-waits, it still spends most of its time blocked.
Note that this seems to affect other programs as well. In fact, one of my motives for writing this script was the frequent failure of the Gnome clock app to issue alarms at the proper time.
Note also that this assumes the computer will be in an awake state at the time the alert is scheduled. If the computer goes to sleep and stays asleep, it will issue no alerts during the time it is asleep. Remedying this state of affairs requires privileged system calls that one must be careful making. I decided that the safety of having a nonprivileged program was worth the slight chance of a missed alert; in my case, the problem almost always happens as a result of a system suspending itself on lunch break, with the alert time being while I am at my desk in the afternoon.