Monotonic clock

2,124 views
Skip to first unread message

beatgammit

unread,
Aug 19, 2013, 9:24:18 PM8/19/13
to golan...@googlegroups.com
I noticed there's no support in Go for a monotonic clock. If you're not familiar, this exists as a C call:


I'm running on a system with a volatile clock, so relative times are not currently feasible (e.g. process uptime, where the system clock could change during normal operation).

Does anyone know why Go doesn't have support for this? If not, does this seem like a useful feature to have?

BTW, all platforms seem to expose this:

Kyle Lemons

unread,
Aug 19, 2013, 9:49:48 PM8/19/13
to beatgammit, golang-nuts
For the record, CLOCK_MONOTONIC is not necessarily monotonic :).


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Dmitry Vyukov

unread,
Aug 20, 2013, 4:51:14 AM8/20/13
to Kyle Lemons, beatgammit, golang-nuts
On Tue, Aug 20, 2013 at 5:49 AM, Kyle Lemons <kev...@google.com> wrote:
For the record, CLOCK_MONOTONIC is not necessarily monotonic :).

What do you mean?

Dmitry Vyukov

unread,
Aug 20, 2013, 5:09:44 AM8/20/13
to beatgammit, golang-nuts
Looks like a useful feature to me.
I think that Go abuses real time. E.g. if you do common:

t0 := time.Now()
...
fmt.Pintf("... taken %v\n", time.Now().Sub(t0))

you actually want it to be not affected by manual time changes, automatic time adjustments, leap seconds, etc.

Or when you say time.Sleep(time.Second), Go first translates it to absolute real time, and then translates it back to relative time to calculate sleep duration. This assumes that real time never changes, which is not true on desktop systems. This means that if a user changes time, all his Go program can suddenly hang...

Arne Hormann

unread,
Aug 20, 2013, 5:20:03 AM8/20/13
to golan...@googlegroups.com, beatgammit
If you plan to add it, it may pay off to get inspired by Daniel J Bernstein: http://cr.yp.to/proto/utctai.html and http://cr.yp.to/libtai.html

Nick Craig-Wood

unread,
Aug 20, 2013, 6:14:53 AM8/20/13
to Dmitry Vyukov, golang-nuts
On 20/08/13 09:51, Dmitry Vyukov wrote:
> On Tue, Aug 20, 2013 at 5:49 AM, Kyle Lemons <kev...@google.com
> <mailto:kev...@google.com>> wrote:
>
> For the record, CLOCK_MONOTONIC is not necessarily monotonic :).
>
> What do you mean?

Look at some of these results and you'll see the problems and some
solutions!

https://www.google.co.uk/search?q=linux+monotonic+time+goes+backwards

In general a microsecond accurate monotonic clock with ntp active on a
multicore system is really hard to get right and there have been an
awful lot of problems in the linux kernel in the past with it.

Here is a useful stack overflow page about CLOCK_MONOTONIC and
CLOCK_MONOTONIC_RAW

http://stackoverflow.com/questions/14270300/what-is-the-difference-between-clock-monotonic-clock-monotonic-raw

--
Nick Craig-Wood <ni...@craig-wood.com> -- http://www.craig-wood.com/nick

Dmitry Vyukov

unread,
Aug 20, 2013, 7:02:37 AM8/20/13
to Nick Craig-Wood, golang-nuts
ouch
I am puzzled why somebody ever decided to do some adjustments to CLOCK_MONOTONIC...
does CLOCK_MONOTONIC_RAW have any such problems?

Kyle Lemons

unread,
Aug 20, 2013, 12:00:03 PM8/20/13
to Arne Hormann, golang-nuts, beatgammit
This stuff is really hard.  I'm not saying it's not worth doing at all, but I would say that it's not worth doing half-way.

TAI is frought with peril unless you are getting your time source from GPS.  You must rebuild your application with an updated version of libtai every six months if you want to have a chance of handling the leap seconds correctly.  Alternately, you could require an internet connection and download the NIST leap-seconds.list, which you could cache somewhere for a bit.  Note that the expiration date in that file should be treated with a grain of salt; you should probably start trying to get a new one well before it actually expires.

CLOCK_MONOTONIC isn't subject to leap seconds or daylight saving, but it does step when the system clock steps, and its frequency will change if the kernel frequency is being adjusted (like with ntpd).  CLOCK_MONOTONIC_RAW, where available, is reading a hardware clock, but you don't really know which one as far as I know.  If it's reading the HPET, then it will actually be monotonic but getting the frequency will be tricky; if it's reading the TSC, then it might still jump backward if the thread doing the measurement jumps between cores.

Dmitry Vyukov

unread,
Aug 20, 2013, 12:16:57 PM8/20/13
to Kyle Lemons, Arne Hormann, golang-nuts, beatgammit
Doesn't linux provide something like window's GetTickCount()? I.e.
strictly monotonic, no adjustments, time from unspecified point in
time, scheduler tick precision (i.e. not nanosecond).

Kyle Lemons

unread,
Aug 20, 2013, 12:41:44 PM8/20/13
to Dmitry Vyukov, Arne Hormann, golang-nuts, beatgammit
Are you thinking of clock?  I believe that is only useful for measuring "short" spans of time on a single thread, and I believe it only counts the amount of time your binary is actually executing, not wall-clock time.

Dmitry Vyukov

unread,
Aug 20, 2013, 12:49:10 PM8/20/13
to Kyle Lemons, Arne Hormann, golang-nuts, beatgammit
On Tue, Aug 20, 2013 at 8:41 PM, Kyle Lemons <kev...@google.com> wrote:
> Are you thinking of clock? I believe that is only useful for measuring
> "short" spans of time on a single thread, and I believe it only counts the
> amount of time your binary is actually executing, not wall-clock time.

No, I want wall-clock time. It's a same that linux does not have it.
One wants it in lots of cases -- everything related to measuring or
specifying relative periods.

Kyle Lemons

unread,
Aug 20, 2013, 12:57:56 PM8/20/13
to Dmitry Vyukov, Arne Hormann, golang-nuts, beatgammit
On Tue, Aug 20, 2013 at 9:49 AM, Dmitry Vyukov <dvy...@google.com> wrote:
On Tue, Aug 20, 2013 at 8:41 PM, Kyle Lemons <kev...@google.com> wrote:
> Are you thinking of clock?  I believe that is only useful for measuring
> "short" spans of time on a single thread, and I believe it only counts the
> amount of time your binary is actually executing, not wall-clock time.

No, I want wall-clock time. It's a same that linux does not have it.
One wants it in lots of cases -- everything related to measuring or
specifying relative periods.

It's not clear to me that windows has what you describe either.  As I said, it's a very difficult problem.  My day-job is Time, and I'm still learning gotchas.  It's not really clear to me that the windows version is actually any better either.

Dmitry Vyukov

unread,
Aug 20, 2013, 1:20:27 PM8/20/13
to Kyle Lemons, Arne Hormann, golang-nuts, beatgammit
On Tue, Aug 20, 2013 at 8:57 PM, Kyle Lemons <kev...@google.com> wrote:
> On Tue, Aug 20, 2013 at 9:49 AM, Dmitry Vyukov <dvy...@google.com> wrote:
>>
>> On Tue, Aug 20, 2013 at 8:41 PM, Kyle Lemons <kev...@google.com> wrote:
>> > Are you thinking of clock? I believe that is only useful for measuring
>> > "short" spans of time on a single thread, and I believe it only counts
>> > the
>> > amount of time your binary is actually executing, not wall-clock time.
>>
>> No, I want wall-clock time. It's a same that linux does not have it.
>> One wants it in lots of cases -- everything related to measuring or
>> specifying relative periods.
>
>
> It's not clear to me that windows has what you describe either. As I said,
> it's a very difficult problem. My day-job is Time, and I'm still learning
> gotchas. It's not really clear to me that the windows version is actually
> any better either.


GetTickCount64 never step back, never adjusted, global for all processors.
The potential gotcha is that includes time in hibernation,
QueryUnbiasedInterruptTime() returns time excluding hibernation.

Kyle Lemons

unread,
Aug 20, 2013, 1:50:33 PM8/20/13
to Dmitry Vyukov, Arne Hormann, golang-nuts, beatgammit
On Tue, Aug 20, 2013 at 10:20 AM, Dmitry Vyukov <dvy...@google.com> wrote:
On Tue, Aug 20, 2013 at 8:57 PM, Kyle Lemons <kev...@google.com> wrote:
> On Tue, Aug 20, 2013 at 9:49 AM, Dmitry Vyukov <dvy...@google.com> wrote:
>>
>> On Tue, Aug 20, 2013 at 8:41 PM, Kyle Lemons <kev...@google.com> wrote:
>> > Are you thinking of clock?  I believe that is only useful for measuring
>> > "short" spans of time on a single thread, and I believe it only counts
>> > the
>> > amount of time your binary is actually executing, not wall-clock time.
>>
>> No, I want wall-clock time. It's a same that linux does not have it.
>> One wants it in lots of cases -- everything related to measuring or
>> specifying relative periods.
>
>
> It's not clear to me that windows has what you describe either.  As I said,
> it's a very difficult problem.  My day-job is Time, and I'm still learning
> gotchas.  It's not really clear to me that the windows version is actually
> any better either.


GetTickCount64 never step back, never adjusted, global for all processors.
The potential gotcha is that includes time in hibernation,
QueryUnbiasedInterruptTime() returns time excluding hibernation.

Yeah, that looks like CLOCK_MONOTONIC_RAW.

Dmitry Vyukov

unread,
Aug 20, 2013, 1:57:21 PM8/20/13
to Kyle Lemons, Arne Hormann, golang-nuts, beatgammit
On Tue, Aug 20, 2013 at 9:50 PM, Kyle Lemons <kev...@google.com> wrote:
>> > wrote:
>> >>
>> >> On Tue, Aug 20, 2013 at 8:41 PM, Kyle Lemons <kev...@google.com> wrote:
>> >> > Are you thinking of clock? I believe that is only useful for
>> >> > measuring
>> >> > "short" spans of time on a single thread, and I believe it only
>> >> > counts
>> >> > the
>> >> > amount of time your binary is actually executing, not wall-clock
>> >> > time.
>> >>
>> >> No, I want wall-clock time. It's a same that linux does not have it.
>> >> One wants it in lots of cases -- everything related to measuring or
>> >> specifying relative periods.
>> >
>> >
>> > It's not clear to me that windows has what you describe either. As I
>> > said,
>> > it's a very difficult problem. My day-job is Time, and I'm still
>> > learning
>> > gotchas. It's not really clear to me that the windows version is
>> > actually
>> > any better either.
>>
>>
>> GetTickCount64 never step back, never adjusted, global for all processors.
>> The potential gotcha is that includes time in hibernation,
>> QueryUnbiasedInterruptTime() returns time excluding hibernation.
>
>
> Yeah, that looks like CLOCK_MONOTONIC_RAW.


Yeah, except that you said that it's not necessary available, and if
available its behavior is unspecified :)

Kyle Lemons

unread,
Aug 20, 2013, 2:11:12 PM8/20/13
to Dmitry Vyukov, Arne Hormann, golang-nuts, beatgammit
I was probably being a bit dramatic.  The kernel is free to choose whichever clock it wishes, but I think it would only be allowed to choose the TSC if the hardware TSCs are synchronized, that is crossing cores should be safe; whether it ever actually does choose anything but the HPET64 I can't really say.  CLOCK_MONOTONIC_RAW is going to be available in the same places GetTickCount64 is available in all likelihood.

Dmitry Vyukov

unread,
Aug 20, 2013, 2:16:58 PM8/20/13
to Kyle Lemons, Arne Hormann, golang-nuts, beatgammit
GetTickCount is a part of Windows API, so it's always available.
Could we use CLOCK_MONOTONIC_RAW in Go runtime on linux?

Kyle Lemons

unread,
Aug 20, 2013, 2:21:45 PM8/20/13
to Dmitry Vyukov, Arne Hormann, golang-nuts, beatgammit
Python's PEP is actually a really good summary of the availability on the different platforms:

Johan Bolmsjö

unread,
Aug 21, 2013, 4:09:35 AM8/21/13
to golan...@googlegroups.com, beatgammit
On this topic I think it was a mistake to change the socket timeout API from relative timeouts to absolute timeouts, the SetTimeout became SetDeadline with an abolute timeout.
Especially if the absolute time that is specified is subject to dramatic time changes, particularly backwards. For example I usually want to say, "let's give this operation 5 seconds to succeed until we give up". I have never wanted to say "let's give this operation until 12:00 to succeed", especially if time is now 11:59:59 and time is then set back 2 hours causing the timeout to not happen for 2 hours.

CLOCK_MONOTONIC is NTP adjusted in Linux wrt frequency, I think this is done to keep an exact time when measuring long intervals.
However I have myself observed that CLOCK_MONOTONIC can move backwards in some virtualized environments, more specifically Linux running under Virtualbox and usermode Linux. In these cases the host Linux version is unknown to me but the guest Linux version was 3.0:ish, which is quite recent.

Monotonic should per definition not be able to move backwards...

Dmitry Vyukov

unread,
Aug 21, 2013, 5:09:31 AM8/21/13
to Johan Bolmsjö, golang-nuts, beatgammit
On Wed, Aug 21, 2013 at 12:09 PM, Johan Bolmsjö <johan....@gmail.com> wrote:
> On this topic I think it was a mistake to change the socket timeout API from
> relative timeouts to absolute timeouts, the SetTimeout became SetDeadline
> with an abolute timeout.
> Especially if the absolute time that is specified is subject to dramatic
> time changes, particularly backwards. For example I usually want to say,
> "let's give this operation 5 seconds to succeed until we give up". I have
> never wanted to say "let's give this operation until 12:00 to succeed",
> especially if time is now 11:59:59 and time is then set back 2 hours causing
> the timeout to not happen for 2 hours.


I think it's pretty equivalent whether to use relative or absolute
time (in some scenarios when extending deadline, you need to convert
between rel and abs anyway). But the mistake is to use real time. Lots
and lots of use cases want "abstract monotonic" time.



> CLOCK_MONOTONIC is NTP adjusted in Linux wrt frequency, I think this is done
> to keep an exact time when measuring long intervals.
> However I have myself observed that CLOCK_MONOTONIC can move backwards in
> some virtualized environments, more specifically Linux running under
> Virtualbox and usermode Linux. In these cases the host Linux version is
> unknown to me but the guest Linux version was 3.0:ish, which is quite
> recent.

Looks like a plain bug in VM. I think we saw something similar with openvz.

beatgammit

unread,
Aug 21, 2013, 11:57:41 AM8/21/13
to golan...@googlegroups.com, beatgammit
SetDeadline() has always bugged me. Is there an explanation why this was done instead of SetTimeout()? Nearly every other language I use has relative time for socket timeouts...

If monotonic time were to be added, could SetTimeout() be added and make SetDeadline() convert to relative time from Now()? Or would this break the backwards-compatible guarantee? It seems in most cases this would have the same effect, while reducing bugs in the case that the system time changes. (this may already be the case, I didn't go past the call to runtime_poll* in the sources).

Dmitry Vyukov

unread,
Aug 21, 2013, 12:19:49 PM8/21/13
to beatgammit, golang-nuts
On Wed, Aug 21, 2013 at 7:57 PM, beatgammit <beatg...@gmail.com> wrote:
> SetDeadline() has always bugged me. Is there an explanation why this was
> done instead of SetTimeout()? Nearly every other language I use has relative
> time for socket timeouts...
>
> If monotonic time were to be added, could SetTimeout() be added and make
> SetDeadline() convert to relative time from Now()? Or would this break the
> backwards-compatible guarantee? It seems in most cases this would have the
> same effect, while reducing bugs in the case that the system time changes.
> (this may already be the case, I didn't go past the call to runtime_poll* in
> the sources).


The problem is not with SetDeadline/SetTimeout, the problem is with
absence of monotonic time. If you have monotonic time, then you can
easily implement SetTimeout yourself as:
SetDeadline(time.MonotonicNow().Add(d))
Well, you do it now with time.Now(), but that will break if user changes time.

Ian Lance Taylor

unread,
Aug 21, 2013, 12:32:10 PM8/21/13
to beatgammit, golang-nuts
On Wed, Aug 21, 2013 at 8:57 AM, beatgammit <beatg...@gmail.com> wrote:
> SetDeadline() has always bugged me. Is there an explanation why this was
> done instead of SetTimeout()? Nearly every other language I use has relative
> time for socket timeouts...

Seting a relative timeout is confusing when, for example, one Write
call leads to several write system calls. Does the relative timeout
apply separately to each system call? Should we accumulate the total
time per Write call? In general, what is the granularity of the
timeout with regard to the call?

An absolute time has no such confusion. And, of course, it's easy to
switch back and forth depending on what the program wants.

Ian

Johan Bolmsjö

unread,
Aug 21, 2013, 1:53:02 PM8/21/13
to golan...@googlegroups.com, Johan Bolmsjö, beatgammit
I agree if the absolute time is a monotonic time. My example about the socket timeout was this, where the absolute time is real time. Adjusting the clock on the host will affect the timeout. If one does not use NTP synced machines this may be a time bomb waiting to go off.

I hope I have not misunderstood anything about how to use this API, I believe that time.Time is a real time clock (basically unix epoque time).
I'm a big proponent of monotonic time, want to use it as much as possible.

//
// * Start netcat:
//   nc -l -p 2222 127.0.0.1
// * Start this program:
//   ./test
// * Set time 1 minute backwards, example:
//   date +%T -s "19:15:00"
// * The timeout will be longer than 10 seconds because absolute real time is used in the timeout API.
//

package main

import (
    "fmt"
    "net"
    "time"
)

func main() {
    conn, err := net.Dial("tcp", "127.0.0.1:2222")
    if err != nil {
        fmt.Printf("error: %s\n", err)
        return
    }
   
    now := time.Now()
    fmt.Printf("now: %s\n", now);
    if err = conn.SetReadDeadline(now.Add(10e9)); err != nil {
        fmt.Printf("error: %s\n", err)
        return
    }
   
    var buf [10]byte
    n, err := conn.Read(buf[:])
    if err != nil {
        fmt.Printf("now: %s, error: %s\n", time.Now(), err)
        return
    } else {
        fmt.Printf("read %d bytes\n", n)
    }
}

Kyle Lemons

unread,
Aug 21, 2013, 2:30:17 PM8/21/13
to Johan Bolmsjö, golang-nuts, beatgammit
On Wed, Aug 21, 2013 at 10:53 AM, Johan Bolmsjö <johan....@gmail.com> wrote:
I agree if the absolute time is a monotonic time. My example about the socket timeout was this, where the absolute time is real time. Adjusting the clock on the host will affect the timeout. If one does not use NTP synced machines this may be a time bomb waiting to go off.

Depending on time, the duration of sleeps, or the duration of a timeout will always be a bomb waiting to go off.  That said, we may want to think about doing clocks right in the stdlib so that folks don't have to do it right themselves, because it is exceedingly difficult.
 
I hope I have not misunderstood anything about how to use this API, I believe that time.Time is a real time clock (basically unix epoque time).
I'm a big proponent of monotonic time, want to use it as much as possible.

Technically the code doesn't have a timeout of ... uh, use 10*time.Second, not 10e9 please ... 10 seconds, it has a deadline that will be reached when the system clock has advanced 10 seconds past where it was when the binary started.  That's what you're asking it to do, and that's what it does.
 

Юрий Соколов

unread,
Aug 22, 2013, 1:00:52 AM8/22/13
to golan...@googlegroups.com
Hi.

I'd be glad if time package have ’type MonoTime int64’, ’type MonoTimer’, ’func NewMonoTimer(Duration) *MonoTimer’, ’type MonoTicker’, ’func NewMinoTicker(Duration) *MonoTicker’.

And, of cause, ’type net.Connected interface { SetMonoDeadline(time.MonoTime) }’.


Юрий Соколов

unread,
Aug 22, 2013, 5:10:00 AM8/22/13
to golan...@googlegroups.com
Or, even better, package `time/mono` with 

    package mono
    import "time"

    type Time int64
    func Now() Time
    func (t Time) Add(d time.Duration) Time

    type Timer time.Timer
    func AfterFunc(d time.Duration, f func()) *Timer
    func NewTimer(d time.Duration) *Timer
    func (t *Timer) Reset(d time.Duration) bool
    func (t *Timer) Stop() bool

    type Ticker time.Ticker
    func NewTicker(d time.Duration) *Ticker
    func (t *Ticker) Stop()

    func After(d time.Duration) <-chan Time
    func Sleep(d time.Duration)
    func Tick(d time.Duration) <-chan Time

So that, interface mostly the same as with `time` package,
no separate Duration type (time.Duration is enough),
no need for arithmetic methods (cause mono.Time is int64),
mono.Ticker and mono.Timer share structure with time.Ticker and time.Timer, but uses separate timers priority queue in runtime package.

четверг, 22 августа 2013 г., 9:00:52 UTC+4 пользователь Юрий Соколов написал:

beatgammit

unread,
Aug 22, 2013, 11:33:48 AM8/22/13
to golan...@googlegroups.com, beatgammit
Ah, ok. I guess that makes sense.

Related, how likely is this to get into Go in the near future? I didn't see an issue in the tracker when I wrote this, so would that be the first step?

I have it working now using a syscall, but that isn't very portable.

Ian Lance Taylor

unread,
Aug 23, 2013, 12:38:43 AM8/23/13
to beatgammit, golang-nuts
On Thu, Aug 22, 2013 at 8:33 AM, beatgammit <beatg...@gmail.com> wrote:
>
> Related, how likely is this to get into Go in the near future? I didn't see
> an issue in the tracker when I wrote this, so would that be the first step?
>
> I have it working now using a syscall, but that isn't very portable.

I may be missing something but it seems to me that it should be
possible to implement a monotonic clock in an external package. I
would start that way, and see if enough people find it useful to
include in the standard library.

Ian

Kyle Lemons

unread,
Sep 18, 2013, 4:59:49 PM9/18/13
to Ian Lance Taylor, beatgammit, golang-nuts
The problem is that you can't make net and such use a monotonic clock.  In the recent example on a GCE instance where there was a giant clock step 5min into the lifetime of the VM, this caused some deadlines to be hours away instead of seconds away.


Dmitry Vyukov

unread,
Sep 18, 2013, 5:04:14 PM9/18/13
to Kyle Lemons, Ian Lance Taylor, beatgammit, golang-nuts
On Wed, Sep 18, 2013 at 1:59 PM, Kyle Lemons <kev...@google.com> wrote:
> The problem is that you can't make net and such use a monotonic clock.

Why?

Kyle Lemons

unread,
Sep 18, 2013, 5:07:18 PM9/18/13
to Dmitry Vyukov, Ian Lance Taylor, beatgammit, golang-nuts
On Wed, Sep 18, 2013 at 2:04 PM, Dmitry Vyukov <dvy...@google.com> wrote:
On Wed, Sep 18, 2013 at 1:59 PM, Kyle Lemons <kev...@google.com> wrote:
> The problem is that you can't make net and such use a monotonic clock.

Why?

As a third party package (the context of my question)?  How would you do that?

Dmitry Vyukov

unread,
Sep 18, 2013, 5:13:23 PM9/18/13
to Kyle Lemons, Ian Lance Taylor, beatgammit, golang-nuts
On Wed, Sep 18, 2013 at 2:07 PM, Kyle Lemons <kev...@google.com> wrote:
> On Wed, Sep 18, 2013 at 2:04 PM, Dmitry Vyukov <dvy...@google.com> wrote:
>>
>> On Wed, Sep 18, 2013 at 1:59 PM, Kyle Lemons <kev...@google.com> wrote:
>> > The problem is that you can't make net and such use a monotonic clock.
>>
>> Why?
>
>
> As a third party package (the context of my question)? How would you do
> that?


ah, I missed the context, sorry
yeah, it's basically impossible to make net use it, and it's difficult
to make an efficient MonotonicTicker in third-party library
Reply all
Reply to author
Forward
Message has been deleted
0 new messages