Skip to content

Commit 40080ae

Browse files
committed
Merge pull request #258 from ruby-concurrency/clock_time
Closes #256
2 parents e625ad9 + 518afaa commit 40080ae

File tree

15 files changed

+446
-298
lines changed

15 files changed

+446
-298
lines changed

CHANGELOG.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
### Next Release v0.8.1 (TBD)
1+
### Next Release v0.9.0 (Target Date: 5 April 2015)
22

33
* Pure Java implementations of
44
- `AtomicBoolean`
@@ -7,7 +7,14 @@
77
* Fixed bug when pruning Ruby thread pools
88
* Fixed bug in time calculations within `ScheduledTask`
99
* Default `count` in `CountDownLatch` to 1
10-
* Use monotonic clock for timeouts on all platforms where supported
10+
* Use monotonic clock for all timers via `Concurrent.monotonic_time`
11+
- Use `Process.clock_gettime(Process::CLOCK_MONOTONIC)` when available
12+
- Fallback to `java.lang.System.nanoTime()` on unsupported JRuby versions
13+
- Pure Ruby implementation for everything else
14+
- Effects `Concurrent.timer`, `Concurrent.timeout`, `TimerSet`, `TimerTask`, and `ScheduledTask`
15+
* Deprecated all clock-time based timer scheduling
16+
- Only support scheduling by delay
17+
- Effects `Concurrent.timer`, `TimerSet`, and `ScheduledTask`
1118

1219
## Current Release v0.8.0 (25 January 2015)
1320

doc/scheduled_task.md

Lines changed: 0 additions & 148 deletions
This file was deleted.

lib/concurrent.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@
2929
require 'concurrent/timer_task'
3030
require 'concurrent/tvar'
3131

32+
# @!macro [new] monotonic_clock_warning
33+
#
34+
# @note Time calculations one all platforms and languages are sensitive to
35+
# changes to the system clock. To alleviate the potential problems
36+
# associated with changing the system clock while an application is running,
37+
# most modern operating systems provide a monotonic clock that operates
38+
# independently of the system clock. A monotonic clock cannot be used to
39+
# determine human-friendly clock times. A monotonic clock is used exclusively
40+
# for calculating time intervals. Not all Ruby platforms provide access to an
41+
# operating system monotonic clock. On these platforms a pure-Ruby monotonic
42+
# clock will be used as a fallback. An operating system monotonic clock is both
43+
# faster and more reliable than the pure-Ruby implementation. The pure-Ruby
44+
# implementation should be fast and reliable enough for most non-realtime
45+
# operations. At this time the common Ruby platforms that provide access to an
46+
# operating system monotonic clock are MRI 2.1 and above and JRuby (all versions).
47+
#
48+
# @see http://linux.die.net/man/3/clock_gettime Linux clock_gettime(3)
49+
3250
# Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
3351
# F#, C#, Java, and classic concurrency patterns.
3452
#

lib/concurrent/atomic/condition.rb

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require 'concurrent/utility/monotonic_time'
2+
13
module Concurrent
24

35
# Condition is a better implementation of standard Ruby ConditionVariable. The
@@ -41,14 +43,16 @@ def initialize
4143
# @param [Mutex] mutex the locked mutex guarding the wait
4244
# @param [Object] timeout nil means no timeout
4345
# @return [Result]
46+
#
47+
# @!macro monotonic_clock_warning
4448
def wait(mutex, timeout = nil)
45-
start_time = clock_time
49+
start_time = Concurrent.monotonic_time
4650
@condition.wait(mutex, timeout)
4751

4852
if timeout.nil?
4953
Result.new(nil)
5054
else
51-
Result.new(start_time + timeout - clock_time)
55+
Result.new(start_time + timeout - Concurrent.monotonic_time)
5256
end
5357
end
5458

@@ -65,21 +69,5 @@ def broadcast
6569
@condition.broadcast
6670
true
6771
end
68-
69-
private
70-
71-
if defined?(Process::CLOCK_MONOTONIC)
72-
def clock_time
73-
Process.clock_gettime Process::CLOCK_MONOTONIC
74-
end
75-
elsif RUBY_PLATFORM == 'java'
76-
def clock_time
77-
java.lang.System.nanoTime() / 1_000_000_000.0
78-
end
79-
else
80-
def clock_time
81-
Time.now.to_f
82-
end
83-
end
8472
end
8573
end

lib/concurrent/executor/ruby_thread_pool_executor.rb

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
require 'thread'
22

3-
require_relative 'executor'
43
require 'concurrent/atomic/event'
4+
require 'concurrent/executor/executor'
55
require 'concurrent/executor/ruby_thread_pool_worker'
6+
require 'concurrent/utility/monotonic_time'
67

78
module Concurrent
89

@@ -91,7 +92,7 @@ def initialize(opts = {})
9192
@largest_length = 0
9293

9394
@gc_interval = opts.fetch(:gc_interval, 1).to_i # undocumented
94-
@last_gc_time = Time.now.to_f - [1.0, (@gc_interval * 2.0)].max
95+
@last_gc_time = Concurrent.monotonic_time - [1.0, (@gc_interval * 2.0)].max
9596
end
9697

9798
# @!macro executor_module_method_can_overflow_question
@@ -225,13 +226,13 @@ def ensure_capacity?
225226
#
226227
# @!visibility private
227228
def prune_pool
228-
if Time.now.to_f - @gc_interval >= @last_gc_time
229+
if Concurrent.monotonic_time - @gc_interval >= @last_gc_time
229230
@pool.delete_if { |worker| worker.dead? }
230231
# send :stop for each thread over idletime
231232
@pool.
232-
select { |worker| @idletime != 0 && Time.now.to_f - @idletime > worker.last_activity }.
233+
select { |worker| @idletime != 0 && Concurrent.monotonic_time - @idletime > worker.last_activity }.
233234
each { @queue << :stop }
234-
@last_gc_time = Time.now.to_f
235+
@last_gc_time = Concurrent.monotonic_time
235236
end
236237
end
237238

lib/concurrent/executor/ruby_thread_pool_worker.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'thread'
22
require 'concurrent/logging'
3+
require 'concurrent/utility/monotonic_time'
34

45
module Concurrent
56

@@ -12,7 +13,7 @@ def initialize(queue, parent)
1213
@queue = queue
1314
@parent = parent
1415
@mutex = Mutex.new
15-
@last_activity = Time.now.to_f
16+
@last_activity = Concurrent.monotonic_time
1617
@thread = nil
1718
end
1819

@@ -64,7 +65,7 @@ def run(thread = Thread.current)
6465
# let it fail
6566
log DEBUG, ex
6667
ensure
67-
@last_activity = Time.now.to_f
68+
@last_activity = Concurrent.monotonic_time
6869
@parent.on_end_task
6970
end
7071
end

0 commit comments

Comments
 (0)