Skip to content

Commit

Permalink
Concurrent.monotonic_time accept an optional unit parameter
Browse files Browse the repository at this point in the history
This is a direct transposition of MRI's `clock_gettime` unit option.
  • Loading branch information
byroot committed Jun 25, 2021
1 parent 0cca522 commit c607220
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 7 deletions.
47 changes: 40 additions & 7 deletions lib/concurrent-ruby/concurrent/utility/monotonic_time.rb
Expand Up @@ -10,25 +10,54 @@ def initialize

if defined?(Process::CLOCK_MONOTONIC)
# @!visibility private
def get_time
Process.clock_gettime(Process::CLOCK_MONOTONIC)
def get_time(unit)
Process.clock_gettime(Process::CLOCK_MONOTONIC, unit)
end
elsif Concurrent.on_jruby?
# @!visibility private
def get_time
java.lang.System.nanoTime() / 1_000_000_000.0
TIME_UNITS = Hash.new { |_hash, key| raise ArgumentError, "unexpected unit: #{key}" }.compare_by_identity
private_constant :TIME_UNITS
TIME_UNITS.merge!(
second: 1_000_000_000,
millisecond: 1_000_000,
microsecond: 1_000,
nanosecond: 1,
float_second: 1_000_000_000.0,
float_millisecond: 1_000_000.0,
float_microsecond: 1_000.0,
)

# @!visibility private
def get_time(unit)
java.lang.System.nanoTime() / TIME_UNITS[unit]
end
else
# @!visibility private
TIME_UNITS = Hash.new { |_hash, key| raise ArgumentError, "unexpected unit: #{key}" }.compare_by_identity
private_constant :TIME_UNITS
TIME_UNITS.merge!(
second: [nil, true],
millisecond: [1_000, true],
microsecond: [1_000_000, true],
nanosecond: [1_000_000_000, true],
float_second: [nil, false],
float_millisecond: [1_000.0, false],
float_microsecond: [1_000_000.0, false],
)

# @!visibility private
def get_time
def get_time(unit)
synchronize do
now = Time.now.to_f
if @last_time < now
@last_time = now
else # clock has moved back in time
@last_time += 0.000_001
end
scale, to_int = TIME_UNITS[unit]
now *= scale if scale
now = now.to_i if to_int
now
end
end

Expand All @@ -46,12 +75,16 @@ def get_time
#
# Returns the current time a tracked by the application monotonic clock.
#
# @param [Symbol] unit the time unit to be returned, can be either
# :float_second, :float_millisecond, :float_microsecond, :second,
# :millisecond, :microsecond, or :nanosecond default to :float_second.
#
# @return [Float] The current monotonic time since some unspecified
# starting point
#
# @!macro monotonic_clock_warning
def monotonic_time
GLOBAL_MONOTONIC_CLOCK.get_time
def monotonic_time(unit = :float_second)
GLOBAL_MONOTONIC_CLOCK.get_time(unit)
end

module_function :monotonic_time
Expand Down
30 changes: 30 additions & 0 deletions spec/concurrent/monotonic_time_spec.rb
@@ -0,0 +1,30 @@
module Concurrent

RSpec.describe :monotonic_time do
context 'behavior' do

it 'returns seconds as float' do
expect(Concurrent.monotonic_time).to be_a(Float)
end

%i(float_second float_millisecond float_microsecond).each do |unit|
it "returns a Float when unit = #{unit.inspect}" do
expect(Concurrent.monotonic_time(unit)).to be_a(Float)
end
end

%i(second millisecond microsecond nanosecond).each do |unit|
it "returns an Integer when unit = #{unit.inspect}" do
expect(Concurrent.monotonic_time(unit)).to be_an(Integer)
end
end

it 'raises ArgumentError on unknown units' do
expect {
Concurrent.monotonic_time(:foo)
}.to raise_error(ArgumentError)
end

end
end
end

0 comments on commit c607220

Please sign in to comment.