Skip to content

Commit 421b58c

Browse files
Adjust monotonic clocks to include suspended time with precision (#16516)
Implements clock adjustments as proposed in [RFC 0015](crystal-lang/rfcs#15). We're using `CLOCK_MONOTONIC` everywhere except Linux (where it doesn't tick while suspended) and darwin (where resolution is only 1 microsecond). This is a breaking change on both Linux and darwin where we previously used clocks that don't count suspended or sleeping time.
1 parent ff1677d commit 421b58c

File tree

12 files changed

+142
-31
lines changed

12 files changed

+142
-31
lines changed

src/crystal/system/unix/time.cr

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,36 @@ module Crystal::System::Time
1616
{timespec.tv_sec.to_i64 + UNIX_EPOCH_IN_SECONDS, timespec.tv_nsec.to_i}
1717
end
1818

19-
def self.monotonic : {Int64, Int32}
19+
private def self.clock_gettime(&)
20+
# Chose the best monotonic steady clock with nanosecond precision that ticks
21+
# while the system is suspended. See https://github.com/crystal-lang/rfcs/pull/15
2022
clock = {% if flag?(:darwin) %}
21-
LibC::CLOCK_UPTIME_RAW
23+
# CLOCK_MONOTONIC on Darwin has 1 microsecond resolution, but
24+
# CLOCK_MONOTONIC_RAW has a higher resolution.
25+
LibC::CLOCK_MONOTONIC_RAW
26+
{% elsif flag?(:linux) %}
27+
# On Linux, `CLOCK_MONOTONIC` does not count suspended time, but
28+
# but `CLOCK_BOOTTIME` does.
29+
LibC::CLOCK_BOOTTIME
2230
{% else %}
31+
# On all other systems, `CLOCK_MONOTONIC` includes suspended time.
2332
LibC::CLOCK_MONOTONIC
2433
{% end %}
2534

2635
ret = LibC.clock_gettime(clock, out tp)
27-
raise RuntimeError.from_errno("clock_gettime()") unless ret == 0
36+
yield unless ret == 0
37+
tp
38+
end
39+
40+
def self.monotonic : {Int64, Int32}
41+
tp = clock_gettime do
42+
raise RuntimeError.from_errno("clock_gettime()")
43+
end
2844
{tp.tv_sec.to_i64, tp.tv_nsec.to_i32}
2945
end
3046

3147
def self.ticks : UInt64
32-
clock = {% if flag?(:darwin) %}
33-
LibC::CLOCK_UPTIME_RAW
34-
{% else %}
35-
LibC::CLOCK_MONOTONIC
36-
{% end %}
37-
38-
LibC.clock_gettime(clock, out tp)
48+
tp = clock_gettime { }
3949
tp.tv_sec.to_u64! &* NANOSECONDS_PER_SECOND &+ tp.tv_nsec.to_u64!
4050
end
4151

src/crystal/system/unix/timerfd.cr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
{% skip_file unless flag?(:linux) %}
12
require "c/sys/timerfd"
23

34
struct Crystal::System::TimerFD
45
getter fd : Int32
56

67
# Create a `timerfd` instance set to the monotonic clock.
78
def initialize
8-
@fd = LibC.timerfd_create(LibC::CLOCK_MONOTONIC, LibC::TFD_CLOEXEC)
9+
# We must use the same clock as in `Crystal::System::Time.clock_gettime` in
10+
# order to accept absolute timers with `Time::Instant` values.
11+
# Since `TimerFD` is only used on Linux, we do not have to differentiate
12+
# between different targets.
13+
@fd = LibC.timerfd_create(LibC::CLOCK_BOOTTIME, LibC::TFD_CLOEXEC)
914
raise RuntimeError.from_errno("timerfd_settime") if @fd == -1
1015
end
1116

src/lib_c/aarch64-linux-android/c/time.cr

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
require "./sys/types"
22

33
lib LibC
4-
CLOCK_MONOTONIC = 1
5-
CLOCK_REALTIME = 0
4+
CLOCK_REALTIME = 0
5+
CLOCK_MONOTONIC = 1
6+
CLOCK_PROCESS_CPUTIME_ID = 2
7+
CLOCK_THREAD_CPUTIME_ID = 3
8+
CLOCK_MONOTONIC_RAW = 4
9+
CLOCK_REALTIME_COARSE = 5
10+
CLOCK_MONOTONIC_COARSE = 6
11+
CLOCK_BOOTTIME = 7
12+
CLOCK_REALTIME_ALARM = 8
13+
CLOCK_BOOTTIME_ALARM = 9
14+
CLOCK_SGI_CYCLE = 10
15+
CLOCK_TAI = 11
616

717
struct Tm
818
tm_sec : Int

src/lib_c/aarch64-linux-gnu/c/time.cr

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
require "./sys/types"
22

33
lib LibC
4-
CLOCK_MONOTONIC = 1
5-
CLOCK_REALTIME = 0
4+
CLOCK_REALTIME = 0
5+
CLOCK_MONOTONIC = 1
6+
CLOCK_PROCESS_CPUTIME_ID = 2
7+
CLOCK_THREAD_CPUTIME_ID = 3
8+
CLOCK_MONOTONIC_RAW = 4
9+
CLOCK_REALTIME_COARSE = 5
10+
CLOCK_MONOTONIC_COARSE = 6
11+
CLOCK_BOOTTIME = 7
12+
CLOCK_REALTIME_ALARM = 8
13+
CLOCK_BOOTTIME_ALARM = 9
14+
CLOCK_TAI = 11
615

716
struct Tm
817
tm_sec : Int

src/lib_c/aarch64-linux-musl/c/time.cr

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
require "./sys/types"
22

33
lib LibC
4-
CLOCK_MONOTONIC = 1
5-
CLOCK_REALTIME = 0
4+
CLOCK_REALTIME = 0
5+
CLOCK_MONOTONIC = 1
6+
CLOCK_PROCESS_CPUTIME_ID = 2
7+
CLOCK_THREAD_CPUTIME_ID = 3
8+
CLOCK_MONOTONIC_RAW = 4
9+
CLOCK_REALTIME_COARSE = 5
10+
CLOCK_MONOTONIC_COARSE = 6
11+
CLOCK_BOOTTIME = 7
12+
CLOCK_REALTIME_ALARM = 8
13+
CLOCK_BOOTTIME_ALARM = 9
14+
CLOCK_SGI_CYCLE = 10
15+
CLOCK_TAI = 11
616

717
struct Tm
818
tm_sec : Int

src/lib_c/arm-linux-gnueabihf/c/time.cr

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
require "./sys/types"
22

33
lib LibC
4-
CLOCK_MONOTONIC = 1
5-
CLOCK_REALTIME = 0
4+
CLOCK_REALTIME = 0
5+
CLOCK_MONOTONIC = 1
6+
CLOCK_PROCESS_CPUTIME_ID = 2
7+
CLOCK_THREAD_CPUTIME_ID = 3
8+
CLOCK_MONOTONIC_RAW = 4
9+
CLOCK_REALTIME_COARSE = 5
10+
CLOCK_MONOTONIC_COARSE = 6
11+
CLOCK_BOOTTIME = 7
12+
CLOCK_REALTIME_ALARM = 8
13+
CLOCK_BOOTTIME_ALARM = 9
14+
CLOCK_TAI = 11
615

716
struct Tm
817
tm_sec : Int

src/lib_c/i386-linux-gnu/c/time.cr

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
require "./sys/types"
22

33
lib LibC
4-
CLOCK_MONOTONIC = 1
5-
CLOCK_REALTIME = 0
4+
CLOCK_REALTIME = 0
5+
CLOCK_MONOTONIC = 1
6+
CLOCK_PROCESS_CPUTIME_ID = 2
7+
CLOCK_THREAD_CPUTIME_ID = 3
8+
CLOCK_MONOTONIC_RAW = 4
9+
CLOCK_REALTIME_COARSE = 5
10+
CLOCK_MONOTONIC_COARSE = 6
11+
CLOCK_BOOTTIME = 7
12+
CLOCK_REALTIME_ALARM = 8
13+
CLOCK_BOOTTIME_ALARM = 9
14+
CLOCK_TAI = 11
615

716
struct Tm
817
tm_sec : Int

src/lib_c/i386-linux-musl/c/time.cr

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
require "./sys/types"
22

33
lib LibC
4-
CLOCK_MONOTONIC = 1
5-
CLOCK_REALTIME = 0
4+
CLOCK_REALTIME = 0
5+
CLOCK_MONOTONIC = 1
6+
CLOCK_PROCESS_CPUTIME_ID = 2
7+
CLOCK_THREAD_CPUTIME_ID = 3
8+
CLOCK_MONOTONIC_RAW = 4
9+
CLOCK_REALTIME_COARSE = 5
10+
CLOCK_MONOTONIC_COARSE = 6
11+
CLOCK_BOOTTIME = 7
12+
CLOCK_REALTIME_ALARM = 8
13+
CLOCK_BOOTTIME_ALARM = 9
14+
CLOCK_SGI_CYCLE = 10
15+
CLOCK_TAI = 11
616

717
struct Tm
818
tm_sec : Int

src/lib_c/x86_64-dragonfly/c/time.cr

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
require "./sys/types"
22

33
lib LibC
4-
CLOCK_MONOTONIC = 4
5-
CLOCK_REALTIME = 0
4+
CLOCK_REALTIME = 0
5+
CLOCK_MONOTONIC = 4
6+
CLOCK_UPTIME = 5
7+
CLOCK_UPTIME_PRECISE = 7
8+
CLOCK_UPTIME_FAST = 8
9+
CLOCK_REALTIME_PRECISE = 9
10+
CLOCK_REALTIME_FAST = 10
11+
CLOCK_MONOTONIC_PRECISE = 11
12+
CLOCK_MONOTONIC_FAST = 12
13+
CLOCK_SECOND = 13
14+
CLOCK_THREAD_CPUTIME_ID = 14
15+
CLOCK_PROCESS_CPUTIME_ID = 15
616

717
struct Tm
818
tm_sec : Int

src/lib_c/x86_64-freebsd/c/time.cr

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
require "./sys/types"
22

33
lib LibC
4-
CLOCK_MONOTONIC = 4
5-
CLOCK_REALTIME = 0
4+
CLOCK_REALTIME = 0
5+
CLOCK_MONOTONIC = 4
6+
CLOCK_UPTIME = 5
7+
CLOCK_UPTIME_PRECISE = 7
8+
CLOCK_UPTIME_FAST = 8
9+
CLOCK_REALTIME_PRECISE = 9
10+
CLOCK_REALTIME_FAST = 10
11+
CLOCK_MONOTONIC_PRECISE = 11
12+
CLOCK_MONOTONIC_FAST = 12
13+
CLOCK_SECOND = 13
14+
CLOCK_THREAD_CPUTIME_ID = 14
15+
CLOCK_PROCESS_CPUTIME_ID = 15
616

717
struct Tm
818
tm_sec : Int

0 commit comments

Comments
 (0)