Skip to content

Adjust monotonic clocks to include suspended time with precision#16516

Merged
straight-shoota merged 5 commits intocrystal-lang:masterfrom
straight-shoota:fix/time.monotonic-clock
Dec 21, 2025
Merged

Adjust monotonic clocks to include suspended time with precision#16516
straight-shoota merged 5 commits intocrystal-lang:masterfrom
straight-shoota:fix/time.monotonic-clock

Conversation

@straight-shoota
Copy link
Member

@straight-shoota straight-shoota commented Dec 17, 2025

Implements clock adjustments as proposed in https://github.com/crystal-lang/rfcs/blob/rfc/time-monotonic/text/0015-time-monotonic.md#clock-implementation

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.

@straight-shoota straight-shoota self-assigned this Dec 17, 2025
@straight-shoota straight-shoota added kind:feature breaking-change May have breaking effect in some edge cases. Mostly negligible, but still noteworthy. topic:stdlib:time labels Dec 17, 2025
Copy link
Collaborator

@ysbaddaden ysbaddaden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks both the epoll event loop (and the io_uring branch) that both use absolute times as per CLOCK_MONOTONIC.

It may break something else that implicitly expects the monotonic clock (but probably not).

@straight-shoota
Copy link
Member Author

I guess for the event loop we either have to switch to CLOCK_BOOTTIME (timerfd supports that), or decouple absolute timestamps from Crystal::System::Time.instant. That would mean using only relative timers.
It might be best to encapsulate the clock entirely in the event loop, i.e. have no direct integration with Time::Instant.

@ysbaddaden
Copy link
Collaborator

We need absolute timers in the event loop: while the event operation can be retried multiple times, the timeout is a fixed point in time.

We could rework the epoll and io_uring evloops to recalculate a relative timeout every time but... to include suspend time we'd still have to tell timerfd and io_uring to use CLOCK_BOOTTIME 🤷

So, let's just use CLOCK_BOOTTIME to avoid the issue for starters, and add a note to keep the clocks synced.

@ysbaddaden
Copy link
Collaborator

Neither NetBSD, OpenBSD or Solaris detail or guarantee whether their clock count suspended time. Shouldn't we use CLOCK_BOOTIME when it's defined, and fallback to CLOCK_MONOTONIC otherwise?

@straight-shoota
Copy link
Member Author

CLOCK_MONOTONIC is the standardized clock in POSIX. We need to use different clocks on Darwin and Linux because they are deviating from the standard.
It seems OpenBSD is the only environment where we have a choice to use one or the other. But it's moot because their implementation is identical: They both delegate to nanouptime, so suspended time is included (in contrast to CLOCK_UPTIME and nanoruntime).
As I understand it, the idiomatic clock on BSD systems is the standard CLOCK_MONOTONIC. OpenBSD has added CLOCK_BOOTTIME only for source compatibility with Linux.

@straight-shoota straight-shoota added this to the 1.19.0 milestone Dec 19, 2025
@straight-shoota straight-shoota merged commit 421b58c into crystal-lang:master Dec 21, 2025
50 checks passed
@straight-shoota straight-shoota deleted the fix/time.monotonic-clock branch December 21, 2025 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change May have breaking effect in some edge cases. Mostly negligible, but still noteworthy. kind:feature topic:stdlib:time

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants