concurrent-ruby 1.1.9 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/Gemfile +2 -8
  4. data/README.md +34 -37
  5. data/Rakefile +48 -69
  6. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  9. data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
  10. data/lib/concurrent-ruby/concurrent/array.rb +0 -10
  11. data/lib/concurrent-ruby/concurrent/async.rb +1 -0
  12. data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
  13. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
  14. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
  15. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
  16. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +82 -151
  17. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
  18. data/lib/concurrent-ruby/concurrent/atomic/event.rb +3 -3
  19. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  20. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
  21. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +188 -0
  22. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
  23. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
  24. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
  25. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  26. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +19 -3
  27. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
  28. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +9 -9
  29. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +32 -14
  30. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
  31. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  32. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +15 -4
  33. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
  34. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
  35. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
  36. data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
  37. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  38. data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
  39. data/lib/concurrent-ruby/concurrent/delay.rb +2 -2
  40. data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
  41. data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
  42. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +17 -14
  43. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
  44. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +3 -3
  45. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
  46. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
  47. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
  48. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +6 -6
  49. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
  50. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
  51. data/lib/concurrent-ruby/concurrent/hash.rb +0 -9
  52. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  53. data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
  54. data/lib/concurrent-ruby/concurrent/map.rb +9 -9
  55. data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
  56. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
  57. data/lib/concurrent-ruby/concurrent/mvar.rb +1 -1
  58. data/lib/concurrent-ruby/concurrent/promise.rb +1 -1
  59. data/lib/concurrent-ruby/concurrent/promises.rb +7 -6
  60. data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
  61. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +30 -17
  62. data/lib/concurrent-ruby/concurrent/set.rb +0 -10
  63. data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
  64. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
  65. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
  66. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
  67. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  68. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
  69. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
  70. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +6 -5
  71. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -5
  72. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
  73. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  74. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
  75. data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
  76. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
  77. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +2 -39
  78. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +1 -37
  79. data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
  80. data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
  81. data/lib/concurrent-ruby/concurrent/tvar.rb +22 -61
  82. data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
  83. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +7 -46
  84. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
  85. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
  86. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +36 -89
  87. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  88. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
  89. metadata +10 -12
  90. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  91. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
  92. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
  93. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
  94. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
  95. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -71
  96. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
  97. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
@@ -2,49 +2,46 @@ require 'delegate'
2
2
  require 'monitor'
3
3
 
4
4
  module Concurrent
5
- unless defined?(SynchronizedDelegator)
6
-
7
- # This class provides a trivial way to synchronize all calls to a given object
8
- # by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls
9
- # around the delegated `#send`. Example:
10
- #
11
- # array = [] # not thread-safe on many impls
12
- # array = SynchronizedDelegator.new([]) # thread-safe
13
- #
14
- # A simple `Monitor` provides a very coarse-grained way to synchronize a given
15
- # object, in that it will cause synchronization for methods that have no need
16
- # for it, but this is a trivial way to get thread-safety where none may exist
17
- # currently on some implementations.
18
- #
19
- # This class is currently being considered for inclusion into stdlib, via
20
- # https://bugs.ruby-lang.org/issues/8556
21
- #
22
- # @!visibility private
23
- class SynchronizedDelegator < SimpleDelegator
24
- def setup
25
- @old_abort = Thread.abort_on_exception
26
- Thread.abort_on_exception = true
27
- end
5
+ # This class provides a trivial way to synchronize all calls to a given object
6
+ # by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls
7
+ # around the delegated `#send`. Example:
8
+ #
9
+ # array = [] # not thread-safe on many impls
10
+ # array = SynchronizedDelegator.new([]) # thread-safe
11
+ #
12
+ # A simple `Monitor` provides a very coarse-grained way to synchronize a given
13
+ # object, in that it will cause synchronization for methods that have no need
14
+ # for it, but this is a trivial way to get thread-safety where none may exist
15
+ # currently on some implementations.
16
+ #
17
+ # This class is currently being considered for inclusion into stdlib, via
18
+ # https://bugs.ruby-lang.org/issues/8556
19
+ #
20
+ # @!visibility private
21
+ class SynchronizedDelegator < SimpleDelegator
22
+ def setup
23
+ @old_abort = Thread.abort_on_exception
24
+ Thread.abort_on_exception = true
25
+ end
28
26
 
29
- def teardown
30
- Thread.abort_on_exception = @old_abort
31
- end
27
+ def teardown
28
+ Thread.abort_on_exception = @old_abort
29
+ end
32
30
 
33
- def initialize(obj)
34
- __setobj__(obj)
35
- @monitor = Monitor.new
36
- end
31
+ def initialize(obj)
32
+ __setobj__(obj)
33
+ @monitor = Monitor.new
34
+ end
37
35
 
38
- def method_missing(method, *args, &block)
39
- monitor = @monitor
40
- begin
41
- monitor.enter
42
- super
43
- ensure
44
- monitor.exit
45
- end
36
+ def method_missing(method, *args, &block)
37
+ monitor = @monitor
38
+ begin
39
+ monitor.enter
40
+ super
41
+ ensure
42
+ monitor.exit
46
43
  end
47
-
48
44
  end
45
+
49
46
  end
50
47
  end
@@ -1,5 +1,6 @@
1
1
  require 'concurrent/thread_safe/util'
2
2
  require 'concurrent/thread_safe/util/volatile'
3
+ require 'concurrent/utility/engine'
3
4
 
4
5
  module Concurrent
5
6
 
@@ -32,45 +33,7 @@ module Concurrent
32
33
  # @!visibility private
33
34
  module CheapLockable
34
35
  private
35
- engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
36
- if engine == 'rbx'
37
- # Making use of the Rubinius' ability to lock via object headers to avoid the overhead of the extra Mutex objects.
38
- def cheap_synchronize
39
- Rubinius.lock(self)
40
- begin
41
- yield
42
- ensure
43
- Rubinius.unlock(self)
44
- end
45
- end
46
-
47
- def cheap_wait
48
- wchan = Rubinius::Channel.new
49
-
50
- begin
51
- waiters = @waiters ||= []
52
- waiters.push wchan
53
- Rubinius.unlock(self)
54
- signaled = wchan.receive_timeout nil
55
- ensure
56
- Rubinius.lock(self)
57
-
58
- unless signaled or waiters.delete(wchan)
59
- # we timed out, but got signaled afterwards (e.g. while waiting to
60
- # acquire @lock), so pass that signal on to the next waiter
61
- waiters.shift << true unless waiters.empty?
62
- end
63
- end
64
-
65
- self
66
- end
67
-
68
- def cheap_broadcast
69
- waiters = @waiters ||= []
70
- waiters.shift << true until waiters.empty?
71
- self
72
- end
73
- elsif engine == 'jruby'
36
+ if Concurrent.on_jruby?
74
37
  # Use Java's native synchronized (this) { wait(); notifyAll(); } to avoid the overhead of the extra Mutex objects
75
38
  require 'jruby'
76
39
 
@@ -1,4 +1,5 @@
1
1
  require 'concurrent/thread_safe/util'
2
+ require 'concurrent/utility/engine'
2
3
 
3
4
  # Shim for TruffleRuby.synchronized
4
5
  if Concurrent.on_truffleruby? && !TruffleRuby.respond_to?(:synchronized)
@@ -37,43 +38,6 @@ module Concurrent
37
38
  end
38
39
  end
39
40
 
40
- def self.make_synchronized_on_rbx(klass)
41
- klass.class_eval do
42
- private
43
-
44
- def _mon_initialize
45
- @_monitor ||= Monitor.new # avoid double initialisation
46
- end
47
-
48
- def self.new(*args)
49
- obj = super(*args)
50
- obj.send(:_mon_initialize)
51
- obj
52
- end
53
- end
54
-
55
- klass.superclass.instance_methods(false).each do |method|
56
- case method
57
- when :new_range, :new_reserved
58
- klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
59
- def #{method}(*args)
60
- obj = super
61
- obj.send(:_mon_initialize)
62
- obj
63
- end
64
- RUBY
65
- else
66
- klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
67
- def #{method}(*args)
68
- monitor = @_monitor
69
- monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.")
70
- monitor.synchronize { super }
71
- end
72
- RUBY
73
- end
74
- end
75
- end
76
-
77
41
  def self.make_synchronized_on_truffleruby(klass)
78
42
  klass.superclass.instance_methods(false).each do |method|
79
43
  klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -25,9 +25,7 @@ module Concurrent
25
25
  # Should the task experience an unrecoverable crash only the task thread will
26
26
  # crash. This makes the `TimerTask` very fault tolerant. Additionally, the
27
27
  # `TimerTask` thread can respond to the success or failure of the task,
28
- # performing logging or ancillary operations. `TimerTask` can also be
29
- # configured with a timeout value allowing it to kill a task that runs too
30
- # long.
28
+ # performing logging or ancillary operations.
31
29
  #
32
30
  # One other advantage of `TimerTask` is that it forces the business logic to
33
31
  # be completely decoupled from the concurrency logic. The business logic can
@@ -48,9 +46,7 @@ module Concurrent
48
46
  # {http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html
49
47
  # Observable} module. On execution the `TimerTask` will notify the observers
50
48
  # with three arguments: time of execution, the result of the block (or nil on
51
- # failure), and any raised exceptions (or nil on success). If the timeout
52
- # interval is exceeded the observer will receive a `Concurrent::TimeoutError`
53
- # object as the third argument.
49
+ # failure), and any raised exceptions (or nil on success).
54
50
  #
55
51
  # @!macro copy_options
56
52
  #
@@ -59,20 +55,18 @@ module Concurrent
59
55
  # task.execute
60
56
  #
61
57
  # task.execution_interval #=> 60 (default)
62
- # task.timeout_interval #=> 30 (default)
63
58
  #
64
59
  # # wait 60 seconds...
65
60
  # #=> 'Boom!'
66
61
  #
67
62
  # task.shutdown #=> true
68
63
  #
69
- # @example Configuring `:execution_interval` and `:timeout_interval`
70
- # task = Concurrent::TimerTask.new(execution_interval: 5, timeout_interval: 5) do
64
+ # @example Configuring `:execution_interval`
65
+ # task = Concurrent::TimerTask.new(execution_interval: 5) do
71
66
  # puts 'Boom!'
72
67
  # end
73
68
  #
74
69
  # task.execution_interval #=> 5
75
- # task.timeout_interval #=> 5
76
70
  #
77
71
  # @example Immediate execution with `:run_now`
78
72
  # task = Concurrent::TimerTask.new(run_now: true){ puts 'Boom!' }
@@ -115,15 +109,13 @@ module Concurrent
115
109
  # def update(time, result, ex)
116
110
  # if result
117
111
  # print "(#{time}) Execution successfully returned #{result}\n"
118
- # elsif ex.is_a?(Concurrent::TimeoutError)
119
- # print "(#{time}) Execution timed out\n"
120
112
  # else
121
113
  # print "(#{time}) Execution failed with error #{ex}\n"
122
114
  # end
123
115
  # end
124
116
  # end
125
117
  #
126
- # task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ 42 }
118
+ # task = Concurrent::TimerTask.new(execution_interval: 1){ 42 }
127
119
  # task.add_observer(TaskObserver.new)
128
120
  # task.execute
129
121
  # sleep 4
@@ -133,7 +125,7 @@ module Concurrent
133
125
  # #=> (2013-10-13 19:09:00 -0400) Execution successfully returned 42
134
126
  # task.shutdown
135
127
  #
136
- # task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ sleep }
128
+ # task = Concurrent::TimerTask.new(execution_interval: 1){ sleep }
137
129
  # task.add_observer(TaskObserver.new)
138
130
  # task.execute
139
131
  #
@@ -169,8 +161,6 @@ module Concurrent
169
161
  # @param [Hash] opts the options defining task execution.
170
162
  # @option opts [Integer] :execution_interval number of seconds between
171
163
  # task executions (default: EXECUTION_INTERVAL)
172
- # @option opts [Integer] :timeout_interval number of seconds a task can
173
- # run before it is considered to have failed (default: TIMEOUT_INTERVAL)
174
164
  # @option opts [Boolean] :run_now Whether to run the task immediately
175
165
  # upon instantiation or to wait until the first # execution_interval
176
166
  # has passed (default: false)
@@ -256,18 +246,14 @@ module Concurrent
256
246
  # @return [Fixnum] Number of seconds the task can run before it is
257
247
  # considered to have failed.
258
248
  def timeout_interval
259
- synchronize { @timeout_interval }
249
+ warn 'TimerTask timeouts are now ignored as these were not able to be implemented correctly'
260
250
  end
261
251
 
262
252
  # @!attribute [rw] timeout_interval
263
253
  # @return [Fixnum] Number of seconds the task can run before it is
264
254
  # considered to have failed.
265
255
  def timeout_interval=(value)
266
- if (value = value.to_f) <= 0.0
267
- raise ArgumentError.new('must be greater than zero')
268
- else
269
- synchronize { @timeout_interval = value }
270
- end
256
+ warn 'TimerTask timeouts are now ignored as these were not able to be implemented correctly'
271
257
  end
272
258
 
273
259
  private :post, :<<
@@ -278,7 +264,9 @@ module Concurrent
278
264
  set_deref_options(opts)
279
265
 
280
266
  self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
281
- self.timeout_interval = opts[:timeout] || opts[:timeout_interval] || TIMEOUT_INTERVAL
267
+ if opts[:timeout] || opts[:timeout_interval]
268
+ warn 'TimeTask timeouts are now ignored as these were not able to be implemented correctly'
269
+ end
282
270
  @run_now = opts[:now] || opts[:run_now]
283
271
  @executor = Concurrent::SafeTaskExecutor.new(task)
284
272
  @running = Concurrent::AtomicBoolean.new(false)
@@ -308,7 +296,6 @@ module Concurrent
308
296
  # @!visibility private
309
297
  def execute_task(completion)
310
298
  return nil unless @running.true?
311
- ScheduledTask.execute(timeout_interval, args: [completion], &method(:timeout_task))
312
299
  _success, value, reason = @executor.execute(self)
313
300
  if completion.try?
314
301
  self.value = value
@@ -320,14 +307,5 @@ module Concurrent
320
307
  end
321
308
  nil
322
309
  end
323
-
324
- # @!visibility private
325
- def timeout_task(completion)
326
- return unless @running.true?
327
- if completion.try?
328
- schedule_next_task
329
- observers.notify_observers(Time.now, nil, Concurrent::TimeoutError.new)
330
- end
331
- end
332
310
  end
333
311
  end
@@ -23,16 +23,12 @@ module Concurrent
23
23
  # The (fixed) size of the tuple.
24
24
  attr_reader :size
25
25
 
26
- # @!visibility private
27
- Tuple = defined?(Rubinius::Tuple) ? Rubinius::Tuple : ::Array
28
- private_constant :Tuple
29
-
30
26
  # Create a new tuple of the given size.
31
27
  #
32
28
  # @param [Integer] size the number of elements in the tuple
33
29
  def initialize(size)
34
30
  @size = size
35
- @tuple = tuple = Tuple.new(size)
31
+ @tuple = tuple = ::Array.new(size)
36
32
  i = 0
37
33
  while i < size
38
34
  tuple[i] = Concurrent::AtomicReference.new
@@ -1,5 +1,5 @@
1
1
  require 'set'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/object'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -15,7 +15,6 @@ module Concurrent
15
15
  # Create a new `TVar` with an initial value.
16
16
  def initialize(value)
17
17
  @value = value
18
- @version = 0
19
18
  @lock = Mutex.new
20
19
  end
21
20
 
@@ -43,16 +42,6 @@ module Concurrent
43
42
  @value = value
44
43
  end
45
44
 
46
- # @!visibility private
47
- def unsafe_version # :nodoc:
48
- @version
49
- end
50
-
51
- # @!visibility private
52
- def unsafe_increment_version # :nodoc:
53
- @version += 1
54
- end
55
-
56
45
  # @!visibility private
57
46
  def unsafe_lock # :nodoc:
58
47
  @lock
@@ -160,57 +149,44 @@ module Concurrent
160
149
 
161
150
  private
162
151
 
152
+ # @!visibility private
163
153
  class Transaction
164
154
 
165
155
  ABORTED = ::Object.new
166
156
 
167
- ReadLogEntry = Struct.new(:tvar, :version)
157
+ OpenEntry = Struct.new(:value, :modified)
168
158
 
169
159
  AbortError = Class.new(StandardError)
170
160
  LeaveError = Class.new(StandardError)
171
161
 
172
162
  def initialize
173
- @read_log = []
174
- @write_log = {}
163
+ @open_tvars = {}
175
164
  end
176
165
 
177
166
  def read(tvar)
178
- Concurrent::abort_transaction unless valid?
179
-
180
- if @write_log.has_key? tvar
181
- @write_log[tvar]
182
- else
183
- @read_log.push(ReadLogEntry.new(tvar, tvar.unsafe_version))
184
- tvar.unsafe_value
185
- end
167
+ entry = open(tvar)
168
+ entry.value
186
169
  end
187
170
 
188
171
  def write(tvar, value)
189
- # Have we already written to this TVar?
172
+ entry = open(tvar)
173
+ entry.modified = true
174
+ entry.value = value
175
+ end
190
176
 
191
- if @write_log.has_key? tvar
192
- # Record the value written
193
- @write_log[tvar] = value
194
- else
195
- # Try to lock the TVar
177
+ def open(tvar)
178
+ entry = @open_tvars[tvar]
196
179
 
180
+ unless entry
197
181
  unless tvar.unsafe_lock.try_lock
198
- # Someone else is writing to this TVar - abort
199
182
  Concurrent::abort_transaction
200
183
  end
201
184
 
202
- # Record the value written
203
-
204
- @write_log[tvar] = value
205
-
206
- # If we previously read from it, check the version hasn't changed
207
-
208
- @read_log.each do |log_entry|
209
- if log_entry.tvar == tvar and tvar.unsafe_version > log_entry.version
210
- Concurrent::abort_transaction
211
- end
212
- end
185
+ entry = OpenEntry.new(tvar.unsafe_value, false)
186
+ @open_tvars[tvar] = entry
213
187
  end
188
+
189
+ entry
214
190
  end
215
191
 
216
192
  def abort
@@ -218,32 +194,17 @@ module Concurrent
218
194
  end
219
195
 
220
196
  def commit
221
- return false unless valid?
222
-
223
- @write_log.each_pair do |tvar, value|
224
- tvar.unsafe_value = value
225
- tvar.unsafe_increment_version
226
- end
227
-
228
- unlock
229
-
230
- true
231
- end
232
-
233
- def valid?
234
- @read_log.each do |log_entry|
235
- unless @write_log.has_key? log_entry.tvar
236
- if log_entry.tvar.unsafe_version > log_entry.version
237
- return false
238
- end
197
+ @open_tvars.each do |tvar, entry|
198
+ if entry.modified
199
+ tvar.unsafe_value = entry.value
239
200
  end
240
201
  end
241
202
 
242
- true
203
+ unlock
243
204
  end
244
205
 
245
206
  def unlock
246
- @write_log.each_key do |tvar|
207
+ @open_tvars.each_key do |tvar|
247
208
  tvar.unsafe_lock.unlock
248
209
  end
249
210
  end
@@ -1,26 +1,19 @@
1
1
  module Concurrent
2
+ # @!visibility private
2
3
  module Utility
3
4
 
4
5
  # @!visibility private
5
6
  module EngineDetector
6
- def on_jruby?
7
- ruby_engine == 'jruby'
8
- end
9
-
10
- def on_jruby_9000?
11
- on_jruby? && ruby_version(JRUBY_VERSION, :>=, 9, 0, 0)
12
- end
13
-
14
7
  def on_cruby?
15
- ruby_engine == 'ruby'
8
+ RUBY_ENGINE == 'ruby'
16
9
  end
17
10
 
18
- def on_rbx?
19
- ruby_engine == 'rbx'
11
+ def on_jruby?
12
+ RUBY_ENGINE == 'jruby'
20
13
  end
21
14
 
22
15
  def on_truffleruby?
23
- ruby_engine == 'truffleruby'
16
+ RUBY_ENGINE == 'truffleruby'
24
17
  end
25
18
 
26
19
  def on_windows?
@@ -35,10 +28,6 @@ module Concurrent
35
28
  !(RbConfig::CONFIG['host_os'] =~ /linux/).nil?
36
29
  end
37
30
 
38
- def ruby_engine
39
- defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
40
- end
41
-
42
31
  def ruby_version(version = RUBY_VERSION, comparison, major, minor, patch)
43
32
  result = (version.split('.').map(&:to_i) <=> [major, minor, patch])
44
33
  comparisons = { :== => [0],
@@ -1,58 +1,19 @@
1
- require 'concurrent/synchronization'
2
-
3
1
  module Concurrent
4
2
 
5
- class_definition = Class.new(Synchronization::LockableObject) do
6
- def initialize
7
- @last_time = Time.now.to_f
8
- super()
9
- end
10
-
11
- if defined?(Process::CLOCK_MONOTONIC)
12
- # @!visibility private
13
- def get_time
14
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
15
- end
16
- elsif Concurrent.on_jruby?
17
- # @!visibility private
18
- def get_time
19
- java.lang.System.nanoTime() / 1_000_000_000.0
20
- end
21
- else
22
-
23
- # @!visibility private
24
- def get_time
25
- synchronize do
26
- now = Time.now.to_f
27
- if @last_time < now
28
- @last_time = now
29
- else # clock has moved back in time
30
- @last_time += 0.000_001
31
- end
32
- end
33
- end
34
-
35
- end
36
- end
37
-
38
- # Clock that cannot be set and represents monotonic time since
39
- # some unspecified starting point.
40
- #
41
- # @!visibility private
42
- GLOBAL_MONOTONIC_CLOCK = class_definition.new
43
- private_constant :GLOBAL_MONOTONIC_CLOCK
44
-
45
3
  # @!macro monotonic_get_time
46
4
  #
47
- # Returns the current time a tracked by the application monotonic clock.
5
+ # Returns the current time as tracked by the application monotonic clock.
6
+ #
7
+ # @param [Symbol] unit the time unit to be returned, can be either
8
+ # :float_second, :float_millisecond, :float_microsecond, :second,
9
+ # :millisecond, :microsecond, or :nanosecond default to :float_second.
48
10
  #
49
11
  # @return [Float] The current monotonic time since some unspecified
50
12
  # starting point
51
13
  #
52
14
  # @!macro monotonic_clock_warning
53
- def monotonic_time
54
- GLOBAL_MONOTONIC_CLOCK.get_time
15
+ def monotonic_time(unit = :float_second)
16
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, unit)
55
17
  end
56
-
57
18
  module_function :monotonic_time
58
19
  end
@@ -1,9 +1,10 @@
1
1
  require 'concurrent/utility/engine'
2
+ # Synchronization::AbstractObject must be defined before loading the extension
3
+ require 'concurrent/synchronization/abstract_object'
2
4
 
3
5
  module Concurrent
4
-
6
+ # @!visibility private
5
7
  module Utility
6
-
7
8
  # @!visibility private
8
9
  module NativeExtensionLoader
9
10
 
@@ -15,15 +16,7 @@ module Concurrent
15
16
  defined?(@c_extensions_loaded) && @c_extensions_loaded
16
17
  end
17
18
 
18
- def java_extensions_loaded?
19
- defined?(@java_extensions_loaded) && @java_extensions_loaded
20
- end
21
-
22
19
  def load_native_extensions
23
- unless defined? Synchronization::AbstractObject
24
- raise 'native_extension_loader loaded before Synchronization::AbstractObject'
25
- end
26
-
27
20
  if Concurrent.on_cruby? && !c_extensions_loaded?
28
21
  ['concurrent/concurrent_ruby_ext',
29
22
  "concurrent/#{RUBY_VERSION[0..2]}/concurrent_ruby_ext"
@@ -54,6 +47,10 @@ module Concurrent
54
47
  @c_extensions_loaded = true
55
48
  end
56
49
 
50
+ def java_extensions_loaded?
51
+ defined?(@java_extensions_loaded) && @java_extensions_loaded
52
+ end
53
+
57
54
  def set_java_extensions_loaded
58
55
  @java_extensions_loaded = true
59
56
  end
@@ -77,3 +74,4 @@ module Concurrent
77
74
  extend Utility::NativeExtensionLoader
78
75
  end
79
76
 
77
+ Concurrent.load_native_extensions
@@ -1,4 +1,5 @@
1
1
  module Concurrent
2
+ # @!visibility private
2
3
  module Utility
3
4
  # @private
4
5
  module NativeInteger