-
Notifications
You must be signed in to change notification settings - Fork 134
encode monotonic time in wall clock #1439
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@livekit/protocol": patch | ||
| --- | ||
|
|
||
| encode monotonic time in wall clock |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,14 +12,9 @@ | |
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| // Package mono enforces use of monotonic time when creating/parsing time.Time from external sources. | ||
| // | ||
| // Using time.Now produces monotonic time values that correctly measure time difference in the presence of clock resets. | ||
| // | ||
| // On the other hand, time produce by time.Unix or time.Parse doesn't have this property. Clock reset may lead to incorrect | ||
| // durations computed from these timestamps. To fix this, prefer using Unix and Parse provided by this package. | ||
| // | ||
| // Monotonic time could also be erased when using functions like Truncate, Round, In, UTC. Be careful when using these. | ||
| // These APIs encode monotonic time into time.Time wall-clock fields. Returned | ||
| // values intentionally do not carry Go's internal monotonic payload and are | ||
| // meant to be compared with other mono timestamps. | ||
| // | ||
| // More details: https://go.googlesource.com/proposal/+/master/design/12914-monotonic.md | ||
| package mono | ||
|
|
@@ -38,37 +33,31 @@ func resetClock() { | |
| epochNano = epoch.UnixNano() | ||
| } | ||
|
|
||
| // jumpClock adjusts reference timestamp by a given duration emulating a clock reset/jump. | ||
| // Used in tests only. | ||
| // jumpClock adjusts reference timestamp by a given duration emulating a clock | ||
| // reset/jump. Used in tests only. | ||
| func jumpClock(dt time.Duration) { | ||
| epoch = epoch.Add(-dt) // we pretend time.Now() jumps, not the reference | ||
| epochNano = epoch.UnixNano() | ||
| } | ||
|
|
||
| // FromTime ensures that time.Time value uses monotonic clock. | ||
| // | ||
| // Deprecated: You should probably use Unix or Parse instead. | ||
| // FromTime creates a Time from the monotonic part of t. Note that the monotonic | ||
| // part of t could have been erased when using functions like Truncate, Round, | ||
| // In, UTC, etc... Be careful when using this | ||
| func FromTime(t time.Time) time.Time { | ||
| return fromTime(t) | ||
| } | ||
|
|
||
| func fromTime(t time.Time) time.Time { | ||
| if t.IsZero() { | ||
| return time.Time{} | ||
| } | ||
| return epoch.Add(t.Sub(epoch)) | ||
| return time.Unix(0, epochNano+int64(t.Sub(epoch))) | ||
| } | ||
|
|
||
| // Now is a wrapper for time.Time. | ||
| // | ||
| // Deprecated: time.Now always uses monotonic clock. | ||
| // Now creates a monotonic time without reading the system wall clock | ||
| func Now() time.Time { | ||
| return time.Now() | ||
| return time.Unix(0, epochNano+int64(time.Since(epoch))) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Awesome 💯
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL! Nice optimisation!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only concern is that it requires the use of Previously, any value produced by The nice thing about the previous approach was that it's idiomatic, one may still use stdlib for everything, except getting timestamps from external sources.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| // Unix is an analog of time.Unix that produces monotonic time. | ||
| func Unix(sec, nsec int64) time.Time { | ||
| return fromTime(time.Unix(sec, nsec)) | ||
| return FromTime(time.Unix(sec, nsec)) | ||
| } | ||
|
|
||
| // Parse is an analog of time.Parse that produces monotonic time. | ||
|
|
@@ -77,17 +66,17 @@ func Parse(layout, value string) (time.Time, error) { | |
| if err != nil { | ||
| return time.Time{}, err | ||
| } | ||
| return fromTime(t), nil | ||
| return FromTime(t), nil | ||
| } | ||
|
|
||
| // UnixNano returns the number of nanoseconds elapsed, based on the application start time. | ||
| // This value may be different from time.Now().UnixNano() in the presence of time resets. | ||
| // UnixNano returns the number of nanoseconds elapsed, based on the application | ||
| // start time. | ||
| func UnixNano() int64 { | ||
| return epochNano + int64(time.Since(epoch)) | ||
| } | ||
|
|
||
| // UnixMicro returns the number of microseconds elapsed, based on the application start time. | ||
| // This value may be different from time.Now().UnixMicro() in the presence of time resets. | ||
| // UnixMicro returns the number of microseconds elapsed, based on the | ||
| // application start time. | ||
| func UnixMicro() int64 { | ||
| return UnixNano() / 1000 | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to avoid any confusion we store the epoch relative monotonic time in the wall clock field of a time without the
hasMonotonicflag so normal time mutation apis work and the time remains comparable to other mono produced times