Skip to content

Commit

Permalink
Add Crystal::System::Time.ticks (#14620)
Browse files Browse the repository at this point in the history
  • Loading branch information
ysbaddaden authored May 25, 2024
1 parent 5ca7111 commit a30e3d9
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 15 deletions.
6 changes: 6 additions & 0 deletions src/crystal/system/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ module Crystal::System::Time
# since `0001-01-01 00:00:00`.
# def self.compute_utc_seconds_and_nanoseconds : {Int64, Int32}

# Returns the current time from the monotonic clock in `{seconds,
# nanoseconds}`.
# def self.monotonic : {Int64, Int32}

# Returns the current time from the monotonic clock in nanoseconds.
# Doesn't raise nor allocates GC HEAP memory.
# def self.ticks : UInt64

# Returns a list of paths where time zone data should be looked up.
# def self.zone_sources : Enumerable(String)

Expand Down
21 changes: 16 additions & 5 deletions src/crystal/system/unix/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,27 @@ require "c/time"
{% end %}

module Crystal::System::Time
UnixEpochInSeconds = 62135596800_i64
UNIX_EPOCH_IN_SECONDS = 62135596800_i64
NANOSECONDS_PER_SECOND = 1_000_000_000

def self.compute_utc_seconds_and_nanoseconds : {Int64, Int32}
{% if LibC.has_method?("clock_gettime") %}
ret = LibC.clock_gettime(LibC::CLOCK_REALTIME, out timespec)
raise RuntimeError.from_errno("clock_gettime") unless ret == 0
{timespec.tv_sec.to_i64 + UnixEpochInSeconds, timespec.tv_nsec.to_i}
{timespec.tv_sec.to_i64 + UNIX_EPOCH_IN_SECONDS, timespec.tv_nsec.to_i}
{% else %}
ret = LibC.gettimeofday(out timeval, nil)
raise RuntimeError.from_errno("gettimeofday") unless ret == 0
{timeval.tv_sec.to_i64 + UnixEpochInSeconds, timeval.tv_usec.to_i * 1_000}
{timeval.tv_sec.to_i64 + UNIX_EPOCH_IN_SECONDS, timeval.tv_usec.to_i * 1_000}
{% end %}
end

def self.monotonic : {Int64, Int32}
{% if flag?(:darwin) %}
info = mach_timebase_info
total_nanoseconds = LibC.mach_absolute_time * info.numer // info.denom
seconds = total_nanoseconds // 1_000_000_000
nanoseconds = total_nanoseconds.remainder(1_000_000_000)
seconds = total_nanoseconds // NANOSECONDS_PER_SECOND
nanoseconds = total_nanoseconds.remainder(NANOSECONDS_PER_SECOND)
{seconds.to_i64, nanoseconds.to_i32}
{% else %}
if LibC.clock_gettime(LibC::CLOCK_MONOTONIC, out tp) == 1
Expand All @@ -45,6 +46,16 @@ module Crystal::System::Time
{% end %}
end

def self.ticks : UInt64
{% if flag?(:darwin) %}
info = mach_timebase_info
LibC.mach_absolute_time &* info.numer // info.denom
{% else %}
LibC.clock_gettime(LibC::CLOCK_MONOTONIC, out tp)
tp.tv_sec.to_u64! &* NANOSECONDS_PER_SECOND &+ tp.tv_nsec.to_u64!
{% end %}
end

def self.to_timespec(time : ::Time)
t = uninitialized LibC::Timespec
t.tv_sec = typeof(t.tv_sec).new(time.to_unix)
Expand Down
19 changes: 9 additions & 10 deletions src/crystal/system/win32/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,20 @@ module Crystal::System::Time
((filetime.dwHighDateTime.to_u64 << 32) | filetime.dwLowDateTime.to_u64).to_f64 / FILETIME_TICKS_PER_SECOND.to_f64
end

@@performance_frequency : Int64 = begin
ret = LibC.QueryPerformanceFrequency(out frequency)
if ret == 0
raise RuntimeError.from_winerror("QueryPerformanceFrequency")
end

private class_getter performance_frequency : Int64 do
LibC.QueryPerformanceFrequency(out frequency)
frequency
end

def self.monotonic : {Int64, Int32}
if LibC.QueryPerformanceCounter(out ticks) == 0
raise RuntimeError.from_winerror("QueryPerformanceCounter")
end
LibC.QueryPerformanceCounter(out ticks)
frequency = performance_frequency
{ticks // frequency, (ticks.remainder(frequency) * NANOSECONDS_PER_SECOND / frequency).to_i32}
end

{ticks // @@performance_frequency, (ticks.remainder(@@performance_frequency) * NANOSECONDS_PER_SECOND / @@performance_frequency).to_i32}
def self.ticks : UInt64
LibC.QueryPerformanceCounter(out ticks)
ticks.to_u64! &* (NANOSECONDS_PER_SECOND // performance_frequency)
end

def self.load_localtime : ::Time::Location?
Expand Down

0 comments on commit a30e3d9

Please sign in to comment.