On Fri, May 28, 2021 at 08:15:20AM +0200, Mark Kettenis wrote:
> > Date: Thu, 27 May 2021 18:29:04 -0500
> > From: Scott Cheloha <scottchel...@gmail.com>
> 
> Sorry, but does is one of those areas where I'm not very aware how the
> interfaces are used by applications.  So my default position is:
> "don't change it".  Especially since these are "legacy" interfaces.

This is a reasonable default stance.

The typical usage pattern is to asynchronously do something
periodically.  Usually the SIGALRM handler sets a flag and then the
application clears the flag and runs a subroutine in response.

I have never seen an application in the wild, anywhere, use SIGVTALRM
or SIGPROF.

> > On Wed, May 19, 2021 at 10:32:55AM -0500, Scott Cheloha wrote:
> > > On Wed, May 12, 2021 at 01:15:05PM -0500, Scott Cheloha wrote:
> > > > 
> > > > [...]
> > > > 
> > > > Paul de Weerd mentioned off-list that the initial expiration for an
> > > > ITIMER_REAL timer is always at least one tick.  I looked into it and
> > > > yes, this is the case, because the kernel rounds it_value up to one
> > > > tick if it is non-zero.
> > > > 
> > > > After thinking about it a bit I don't think we should do this
> > > > rounding.  At least, not for the initial expiration.
> 
> The manual page explicity says:
> 
>   "Time values smaller than the resolution of the system clock are
>    rounded up to this reolution (typically 10 milliseconds)".
> 
> which has been there from revision 1.

Before that, even.  This is documented this way because CSRG BSD on
the DEC VAX could only drive a 100hz system clock:

https://svnweb.freebsd.org/csrg/lib/libc/sys/getitimer.2?revision=20230&view=markup#l81

My impression is that we actually inherited the HZ=100 default from
the CSRG BSD DEC VAX port because it was good enough for most new
platforms and so eventually it became a defacto standard for all new
BSD platforms.

> Note that POSIX defines timer_gettime() and timer_settime(), which we
> don't implement.

I want these, but it would require implementing POSIX realtime
signals.  That implies:

- Changing all of our kernel signal stuff.

- Some of our signal stuff is MD, so changes on every architecture.

- So now we have a pretty big kernel ABI break.

- Then we need new stuff in libc.  More than just syscall stubs.
  Probably a major revision bump and an ABI break.

- And of course, new stuff in libpthread for the necessary
  multithreaded signal handling changes.  Another major revision.

I don't think guenther@ would do all of that work for such rarely used
interfaces even if I threatened his life.

Maybe for ports, though...

> We don't implement these, but the POSIX standard
> says in the rationale:
> 
>   "Practical clocks tick at a finite rate, with rates of 100 hertz and
>    1000 hertz being common.  The inverse of this tick rate is the
>    clock resolution, also called the clock granularity, which in
>    either case is expressed as a time duration, being 10 milliseconds
>    and 1 millisecond respectively for these common rates. The
>    granularity of practical clocks implies that if one reads a given
>    clock twice in rapid succession, one may get the same time value
>    twice; and that timers must wait for the next clock tick after the
>    theoretical expiration time, to ensure that a timer never returns
>    too soon.  Note also that the granularity of the clock may be
>    significantly coarser than the resolution of the data format used
>    to set and get time and interval values. Also note that some
>    implementations may choose to adjust time and/or interval values to
>    exactly match the ticks of the underlying clock."
> 
> which seems to imply that rounding up is what is desired here as well,
> although I presume here the actual resolution of the clock is supposed
> to be used.

For kclock timeouts the "theoretical resolution" is 1 nanosecond.
setitimer(2) uses a timeval, though, so in this case you get 1
microsecond.

Regardless, rounding in this case is still wrong.

Let me show you with a practical example:

1. Suppose for sake of discussion you have a 100hz kernel with no error.
   That is, hardclock(9) runs *exactly* every 10ms on the primary CPU.

   Let's denote each hardclock tick on the kernel timeline with "tN".
   So The first tick happens at t0.  And the second tick happens at
   t1 = t0 + 10ms.  And so on.

   To start we then have a kernel timeline like this:

t0 - - - - - - - - - t1 - - - - - - - - - t2 - - - - - - - - - t3

2. In my program I set a one-shot timer with setitimer(2) with a timeout
   of 1 microsecond.  Signal handling code excluded for brevity:

        struct itimerval itv;

        itv.it_value.tv_sec = 0;
        itv.it_value.tv_usec = 1;
        timerclear(&itv.it_interval);
        setitimer(ITIMER_REAL, &itv, NULL);

3. Let's say the above setitimer(2) call "S" happens between t0 and t1:

t0 - - - - S - - - - t1 - - - - - - - - - t2 - - - - - - - - - t3

   The timeout is 1 microsecond.  So the theoretical Expected expiration
   "E" is *probably* between t0 and t1:

t0 - - - - S E - - - t1 - - - - - - - - - t2 - - - - - - - - - t3

4. BUT, the current code rounds my 1 microsecond timeout up to 10
   milliseconds.  So the Actual expiration "A" set in the kernel is
   between t1 and t2:

t0 - - - - S E - - - t1 - - - - - A - - - t2 - - - - - - - - - t3

   Because we only check for timeout expirations during the hardclock
   tick, my timeout will not expire until t2.

   Of course, there is no reason to round my timeout from E up to A.
   If we didn't do the rounding we could service the timeout at t1, a
   full tick earlier, which is far closer to the ideal expiration E.

Does that make sense?

The rounding for it_value is pointless and adds potentially increases
latency.  We should reduce latency where possible, and in this case it
is *trivial*.  All we have to do is not round up.  It doesn't cost us
anything.

> But for timers associated with the
> CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID that would be
> realstathz, which is still tick-like...

This is tangential.

Also, this is actually an error on my part in clock_getres(2).
CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID are based on the
active timecounter so they always have a higher resolution than stathz
or hz.  I keep forgetting to send out a patch to fix it because the
interface is pretty useless.

Reply via email to