Class: Monotonic::Time

Inherits:
Object
  • Object
show all
Defined in:
lib/Monotonic/Time.rb

Constant Summary collapse

CLOCK =

An instant, which #to_time maps back onto the wall clock by way of the boot time, so the clock wanted here is the one which tracks time since boot as the wall clock understands it. That is CLOCK_MONOTONIC, sleep and all. Monotonic::Timer measures intervals rather than instants and chooses a finer clock of its own.

Unlike Timer's, this one is chosen rather than found, there being no alternative which would still answer to #to_time. .clock_name is a method all the same, so that the two classes answer the question alike.

Process::CLOCK_MONOTONIC

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(nanoseconds_since_boot = Process.clock_gettime(CLOCK, :nanosecond)) ⇒ Time

Returns a new instance of Time.



47
48
49
# File 'lib/Monotonic/Time.rb', line 47

def initialize(nanoseconds_since_boot = Process.clock_gettime(CLOCK, :nanosecond))
  @nanoseconds_since_boot = nanoseconds_since_boot
end

Instance Attribute Details

#nanoseconds_since_bootObject (readonly)

class << self



37
38
39
# File 'lib/Monotonic/Time.rb', line 37

def nanoseconds_since_boot
  @nanoseconds_since_boot
end

Class Method Details

.clock_nameObject



21
22
23
# File 'lib/Monotonic/Time.rb', line 21

def clock_name
  :CLOCK_MONOTONIC
end

.nowObject



25
26
27
# File 'lib/Monotonic/Time.rb', line 25

def now
  self.new
end

.resolutionObject

How finely this clock advances, in nanoseconds. It is asked rather than tabulated, being a property of the processor and the operating system and not of this library: upon macOS CLOCK_MONOTONIC answers 1000 here.



32
33
34
# File 'lib/Monotonic/Time.rb', line 32

def resolution
  Process.clock_getres(CLOCK, :nanosecond)
end

Instance Method Details

#+(addend) ⇒ Object

One question tells the two cases apart, and it is asked of the object rather than of its class: does this know where it sits since boot? If it does it is an instant, and if it converts to nanoseconds it is a duration. Nothing here names a class, so anything answering the same messages will do.

An instant plus a duration is an instant. An instant plus an instant is nothing at all: it depends upon where the epoch was arbitrarily put, and a quantity which moves when you move the origin is not a quantity. Ruby's own Time refuses it, with "time + time?", and so does this. The refusal is asked of the duck too, since leaving it to Monotonic::Time happening not to answer #to_nanoseconds would be an accident rather than a rule.



63
64
65
66
67
68
69
70
71
# File 'lib/Monotonic/Time.rb', line 63

def +(addend)
  if addend.respond_to?(:nanoseconds_since_boot)
    raise TypeError, "can't add #{addend.class} to #{self.class}: an instant plus an instant is not an instant"
  end
  unless addend.respond_to?(:to_nanoseconds)
    raise TypeError, "can't add #{addend.class} to #{self.class}: expected something answering to #to_nanoseconds"
  end
  self.class.new(@nanoseconds_since_boot + addend.to_nanoseconds.to_i)
end

#-(subtrahend) ⇒ Object

Minus an instant it is the duration between them; minus a duration it is the earlier instant. The first is why a monotonic clock is read at all.



75
76
77
78
79
80
81
82
83
# File 'lib/Monotonic/Time.rb', line 75

def -(subtrahend)
  if subtrahend.respond_to?(:nanoseconds_since_boot)
    Duration::Nanoseconds.new(@nanoseconds_since_boot - subtrahend.nanoseconds_since_boot)
  elsif subtrahend.respond_to?(:to_nanoseconds)
    self.class.new(@nanoseconds_since_boot - subtrahend.to_nanoseconds.to_i)
  else
    raise TypeError, "can't subtract #{subtrahend.class} from #{self.class}: expected an instant, or something answering to #to_nanoseconds"
  end
end

#seconds_since_bootObject

The clock is read in nanoseconds because it answers there with an Integer, which is exact and stays exact however long the machine has been up. Seconds are derived rather than read, so that the reading loses nothing and the rounding happens where it is asked for.



43
44
45
# File 'lib/Monotonic/Time.rb', line 43

def seconds_since_boot
  Duration::Nanoseconds.new(@nanoseconds_since_boot).to_seconds.to_f
end

#to_sObject



85
86
87
# File 'lib/Monotonic/Time.rb', line 85

def to_s
  "#{seconds_since_boot} seconds since boot."
end

#to_timeObject

The boot time is asked for here rather than kept upon every instant. It cost 1740ns of the 1947ns an instant took to make, and only this method wants it — so an instant is now cheap to take, and the mapping still uses the boot time as it stands rather than as it stood, Darwin moving kern.boottime as the wall clock is disciplined.



94
95
96
# File 'lib/Monotonic/Time.rb', line 94

def to_time
  Sys::Uptime.boot_time + seconds_since_boot
end