Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: time: add function to get time.Time for a given monotonic time #23227

Closed
eriksw opened this issue Dec 23, 2017 · 6 comments
Closed

Comments

@eriksw
Copy link

eriksw commented Dec 23, 2017

What version of Go are you using (go version)?

go version go1.9.2 linux/amd64

What did you expect to see?

A way to construct a time.Time from a value in the system's monotonic time.

(My use case is an integration with systemd: I am given a deadline to respond to a request, and the deadline is given only in terms of CLOCK_MONOTONIC. I want to convert that value to something I can use as a Context's Deadline.)

What did you see instead?

There is no way I could find to accomplish this without resorting to reflect and unsafe.

Workaround (this should not be necessary):

import (
    "reflect"
    "time"
    _ "unsafe"
)

//go:linkname startNano runtime.startNano
var startNano int64

func TimeAtMonotonic(nanos int64) time.Time {
    now := time.Now()
    nowExt := reflect.ValueOf(now).FieldByName("ext").Int()
    return now.Add(time.Duration(nanos - startNano - nowExt))
}
@bradfitz bradfitz changed the title time: add function to get time.Time for a given monotonic time proposal: time: add function to get time.Time for a given monotonic time Dec 23, 2017
@gopherbot gopherbot added this to the Proposal milestone Dec 23, 2017
@bradfitz
Copy link
Contributor

I think we'd all consider this too disgusting to even consider, but you seem to have an actual use case, which is slightly horrifying, but makes this worth discussing. I marked this as a proposal for the proposal process, which is mostly on hold for the holidays until the new year.

@ianlancetaylor
Copy link
Contributor

You can use ClockGettime in the golang.org/x/sys/unix package to get the current monotonic time reading. You can use that to compute a duration from your given monotonic time, and add that duration to time.Now() to get time.Time.

@extemporalgenome
Copy link
Contributor

There seem to be few cases in which this could be properly used (the OP's case would be one of the few). If you provide an historical monotonic value after a clock jump, or even after a smear, should Go return a value calculated based on the current relationship between the monotonic and real time clocks or the relationship that existed (or would have existed) at the time of the supplied value? And how would Go know anything but the current, sampled relationship?

Anything exposed in the stdlib, especially a package as important as time itself, would and should be expected to have coherent, meaningful behavior around these use cases.

@ianlancetaylor
Copy link
Contributor

Since this is a rare use case, and since I believe there is a way to do this without making any changes to the time package, I'm going to close this proposal. Please comment if you disagree.

@eriksw
Copy link
Author

eriksw commented Jan 2, 2018

Assuming the monotonic time encoded in a time.Time from time.Now() and the monotonic time returned from a subsequent (or immediately prior) ClockGettime are the same introduces some amount of error, but I suspect it's negligible enough for my use case.

I hope that this use case can be regarded as an experience report regarding time in Go:

  • Monotonic times do matter beyond the scope of a single process.

    There isn't another reasonable way to asynchronously communicate about times and/or deadlines when the system may not have time sync yet, or when time sync is likely to happen during the course of the (async) exchange.

  • The hiding of monotonic times as a private feature of time.Time results in a poor experience in this kind of use case.

    Even though unix.ClockGettime provides a way to construct monotonic-originated time.Time (with negligible error), the offset nature of the ext field means that unsafe (not just reflect) is necessary to extract the monotonic time from a time.Time.

  • For things like a Context's Deadline, the "real time" aspect of its time might be irrelevant.

    A better experience might include some reasonable way to create a Timer and/or Context's Deadline based on absolute monotonic time instead of a time.Time—it would be my hope that Go2 might take the opportunity to not use something as complex as time.Time in places where a monotonic time (not bound to "real time") could be a better fit.

@ianlancetaylor
Copy link
Contributor

I want to clarify that the suggested workaround does not depend on any assumptions about the type of monotonic time used in time.Time. You have a value that is explicitly expressed as a ClockGettime value. You want to convert that into a a time.Time value. You can do that by computing the difference between your value and the current ClockGettime, and adding that to time.Now(). That has nothing to do with the internal representation of a time.Time.

I think your further comments are concerned with more accurate use of CLOCK_MONOTONIC values. That is entirely reasonable, but you don't have to use time.Time for that. You can use the kernel facilities. For example, instead of using time.Sleep, convert your monotonic time difference into a unix.TimeSpec and call unix.Pselect with nil and with your timeout value.

To put it another way, time.Time is what it is. If you are using values expressed in CLOCK_MONOTONIC and you need exact precision, use the kernel facilities, don't try to use time.Time. I don't think it makes sense to extend time.Time for this unusual use case.

@golang golang locked and limited conversation to collaborators Jan 2, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants