Hacker News new | ask | show | jobs
by hkwerf 493 days ago
This article fails to separate the concerns one has with timers. There's three cases I see:

The simple case is just putting a thread to sleep for a given time. It's somewhat odd that there's no portable API for that, granted. At least POSIX has sleep, nanosleep and clock_nanosleep (list edited, thanks oguz-ismail).

The second case is that a thread wants to continue processing until a timer has passed. This has to happen cooperatively one way or another, so setting a timer is equivalent to regularly querying the system time and comparing to a limit. There is simply no need for a "timer" operating system facility. (Even if you were to plug POSIX timers in here, you'd in all likelihood still need to check some flag set in the signal handler in the processing thread. And then you'd still need to check the system time as signals can originate from anywhere, just as with sleep/etc. above.)

In the third case, a thread waits for I/O. Then, of course, the call to that I/O facility defines how any timer is handled. As these facilities are not portable, timers aren't portable. The author refers to this realization as the need to implement "Userspace timers". These are just an artifact of the I/O facility and calls querying the system time, though.

So, ultimately, not having portable timers is the least of our problems. So long as we don't unify select, poll, kqueue, io_uring and what not, we don't have a need for them. And, as the author realized, libraries like libuv, which do an acceptable job at unifying those facilities, tend to provide a portable timer API.

5 comments

Another consideration: often you want to wait for several types of object (timer, semaphore, file, etc.), so you need one interface where you can do wait([A, B, C, ...]) where A can be a timer and B a semaphore, C a file, etc. Basically, the OS should present a unified interface.
Often such apis (select/poll/epoll) have an option to wait with a timeout, so if you have a bunch of different timers, you can manage them yourself (e.g. on a heap) and set the timeout to the nearest timer

Of course if you have epoll you probably have timerfd, wich does give you a unified interface, just not as portable

And there's the questions people usually don't think of.

If you clock moves forward or backward, how would you like that the effect your sleep?

If the system suspends during your sleep, do you want that to count towards the sleep? Should expiration wake the system?

>usleep

Not since 2008. POSIX has sleep(), nanosleep() and clock_nanosleep() now

Thanks, I've edited the list. :)
> so setting a timer is equivalent to regularly querying the system time and comparing to a limit

Setting an OS timer would make the thread use 0% CPU. “Regularly querying the system time and comparing to a limit” would not.

Also, a good OS API would allow callers to specify how important it is to be woken at that exact moment, and would use that information to coordinate wake-up times across processes, and thus maximize idle periods.

> Setting an OS timer would make the thread use 0% CPU. “Regularly querying the system time and comparing to a limit” would not.

As mentioned, this is the first case and can be achieved by calls to sleep/Sleep/etc. You'd only regularly query if there is actual useful work to be done. That's the premise of the second case you quoted from.

The article is, I reckon, about a rather common case when a thread that does multiplexed I/O also needs to perform some timer-related tasks as well. As the very first paragraph states, "A blocking sleep won't cut it!"

Unless you do it in a separate thread and then signal other threads but that's insane.

Which concerns were not separated?