diff --git a/src/crystal/system/time.cr b/src/crystal/system/time.cr index 0e127c5f7879..c2579760ef79 100644 --- a/src/crystal/system/time.cr +++ b/src/crystal/system/time.cr @@ -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) diff --git a/src/crystal/system/unix/time.cr b/src/crystal/system/unix/time.cr index f7963f32f83f..58ed5b60e7e1 100644 --- a/src/crystal/system/unix/time.cr +++ b/src/crystal/system/unix/time.cr @@ -16,17 +16,18 @@ 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 @@ -34,8 +35,8 @@ module Crystal::System::Time {% 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 @@ -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) diff --git a/src/crystal/system/win32/time.cr b/src/crystal/system/win32/time.cr index 358cc79a4926..8ab47c8aff7c 100644 --- a/src/crystal/system/win32/time.cr +++ b/src/crystal/system/win32/time.cr @@ -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?