concurrent-ruby 1.1.6 → 1.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/Gemfile +3 -8
  4. data/{LICENSE.md → LICENSE.txt} +18 -20
  5. data/README.md +43 -23
  6. data/Rakefile +32 -35
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
  9. data/lib/concurrent-ruby/concurrent/array.rb +1 -1
  10. data/lib/concurrent-ruby/concurrent/async.rb +10 -20
  11. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +1 -0
  12. data/lib/concurrent-ruby/concurrent/atomic/event.rb +2 -2
  13. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +18 -2
  14. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +4 -6
  15. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +52 -42
  16. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +26 -5
  17. data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +1 -1
  18. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  19. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  20. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  21. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +16 -13
  22. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +20 -3
  23. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +1 -1
  24. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +17 -1
  25. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
  26. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +37 -38
  27. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +5 -5
  28. data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +2 -1
  29. data/lib/concurrent-ruby/concurrent/hash.rb +1 -1
  30. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  31. data/lib/concurrent-ruby/concurrent/map.rb +13 -4
  32. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +2 -2
  33. data/lib/concurrent-ruby/concurrent/promise.rb +1 -0
  34. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +29 -16
  35. data/lib/concurrent-ruby/concurrent/set.rb +14 -6
  36. data/lib/concurrent-ruby/concurrent/settable_struct.rb +1 -1
  37. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +3 -5
  38. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +12 -0
  39. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +6 -0
  40. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +26 -1
  41. data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +1 -1
  42. data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -34
  43. data/lib/concurrent-ruby/concurrent/tvar.rb +19 -56
  44. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +67 -35
  45. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +2 -35
  46. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  47. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
  48. metadata +10 -10
@@ -23,6 +23,9 @@ module Concurrent
23
23
  # @!macro thread_pool_executor_constant_default_thread_timeout
24
24
  DEFAULT_THREAD_IDLETIMEOUT = 60
25
25
 
26
+ # @!macro thread_pool_executor_constant_default_synchronous
27
+ DEFAULT_SYNCHRONOUS = false
28
+
26
29
  # @!macro thread_pool_executor_attr_reader_max_length
27
30
  attr_reader :max_length
28
31
 
@@ -35,6 +38,9 @@ module Concurrent
35
38
  # @!macro thread_pool_executor_attr_reader_max_queue
36
39
  attr_reader :max_queue
37
40
 
41
+ # @!macro thread_pool_executor_attr_reader_synchronous
42
+ attr_reader :synchronous
43
+
38
44
  # @!macro thread_pool_executor_method_initialize
39
45
  def initialize(opts = {})
40
46
  super(opts)
@@ -87,13 +93,8 @@ module Concurrent
87
93
  end
88
94
 
89
95
  # @!visibility private
90
- def ready_worker(worker)
91
- synchronize { ns_ready_worker worker }
92
- end
93
-
94
- # @!visibility private
95
- def worker_not_old_enough(worker)
96
- synchronize { ns_worker_not_old_enough worker }
96
+ def ready_worker(worker, last_message)
97
+ synchronize { ns_ready_worker worker, last_message }
97
98
  end
98
99
 
99
100
  # @!visibility private
@@ -106,6 +107,11 @@ module Concurrent
106
107
  synchronize { @completed_task_count += 1 }
107
108
  end
108
109
 
110
+ # @!macro thread_pool_executor_method_prune_pool
111
+ def prune_pool
112
+ synchronize { ns_prune_pool }
113
+ end
114
+
109
115
  private
110
116
 
111
117
  # @!visibility private
@@ -114,9 +120,11 @@ module Concurrent
114
120
  @max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
115
121
  @idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
116
122
  @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
123
+ @synchronous = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS)
117
124
  @fallback_policy = opts.fetch(:fallback_policy, :abort)
118
- raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
119
125
 
126
+ raise ArgumentError.new("`synchronous` cannot be set unless `max_queue` is 0") if @synchronous && @max_queue > 0
127
+ raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
120
128
  raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @max_length < DEFAULT_MIN_POOL_SIZE
121
129
  raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if @max_length > DEFAULT_MAX_POOL_SIZE
122
130
  raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @min_length < DEFAULT_MIN_POOL_SIZE
@@ -148,10 +156,11 @@ module Concurrent
148
156
  if ns_assign_worker(*args, &task) || ns_enqueue(*args, &task)
149
157
  @scheduled_task_count += 1
150
158
  else
151
- handle_fallback(*args, &task)
159
+ return fallback_action(*args, &task)
152
160
  end
153
161
 
154
162
  ns_prune_pool if @next_gc_time < Concurrent.monotonic_time
163
+ nil
155
164
  end
156
165
 
157
166
  # @!visibility private
@@ -184,7 +193,7 @@ module Concurrent
184
193
  # @!visibility private
185
194
  def ns_assign_worker(*args, &task)
186
195
  # keep growing if the pool is not at the minimum yet
187
- worker = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker
196
+ worker, _ = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker
188
197
  if worker
189
198
  worker << [task, args]
190
199
  true
@@ -201,6 +210,8 @@ module Concurrent
201
210
  #
202
211
  # @!visibility private
203
212
  def ns_enqueue(*args, &task)
213
+ return false if @synchronous
214
+
204
215
  if !ns_limited_queue? || @queue.size < @max_queue
205
216
  @queue << [task, args]
206
217
  true
@@ -213,7 +224,7 @@ module Concurrent
213
224
  def ns_worker_died(worker)
214
225
  ns_remove_busy_worker worker
215
226
  replacement_worker = ns_add_busy_worker
216
- ns_ready_worker replacement_worker, false if replacement_worker
227
+ ns_ready_worker replacement_worker, Concurrent.monotonic_time, false if replacement_worker
217
228
  end
218
229
 
219
230
  # creates new worker which has to receive work to do after it's added
@@ -232,29 +243,21 @@ module Concurrent
232
243
  # handle ready worker, giving it new job or assigning back to @ready
233
244
  #
234
245
  # @!visibility private
235
- def ns_ready_worker(worker, success = true)
246
+ def ns_ready_worker(worker, last_message, success = true)
236
247
  task_and_args = @queue.shift
237
248
  if task_and_args
238
249
  worker << task_and_args
239
250
  else
240
251
  # stop workers when !running?, do not return them to @ready
241
252
  if running?
242
- @ready.push(worker)
253
+ raise unless last_message
254
+ @ready.push([worker, last_message])
243
255
  else
244
256
  worker.stop
245
257
  end
246
258
  end
247
259
  end
248
260
 
249
- # returns back worker to @ready which was not idle for enough time
250
- #
251
- # @!visibility private
252
- def ns_worker_not_old_enough(worker)
253
- # let's put workers coming from idle_test back to the start (as the oldest worker)
254
- @ready.unshift(worker)
255
- true
256
- end
257
-
258
261
  # removes a worker which is not in not tracked in @ready
259
262
  #
260
263
  # @!visibility private
@@ -268,10 +271,17 @@ module Concurrent
268
271
  #
269
272
  # @!visibility private
270
273
  def ns_prune_pool
271
- return if @pool.size <= @min_length
272
-
273
- last_used = @ready.shift
274
- last_used << :idle_test if last_used
274
+ now = Concurrent.monotonic_time
275
+ stopped_workers = 0
276
+ while !@ready.empty? && (@pool.size - stopped_workers > @min_length)
277
+ worker, last_message = @ready.first
278
+ if now - last_message > self.idletime
279
+ stopped_workers += 1
280
+ @ready.shift
281
+ worker << :stop
282
+ else break
283
+ end
284
+ end
275
285
 
276
286
  @next_gc_time = Concurrent.monotonic_time + @gc_interval
277
287
  end
@@ -320,19 +330,10 @@ module Concurrent
320
330
 
321
331
  def create_worker(queue, pool, idletime)
322
332
  Thread.new(queue, pool, idletime) do |my_queue, my_pool, my_idletime|
323
- last_message = Concurrent.monotonic_time
324
333
  catch(:stop) do
325
334
  loop do
326
335
 
327
336
  case message = my_queue.pop
328
- when :idle_test
329
- if (Concurrent.monotonic_time - last_message) > my_idletime
330
- my_pool.remove_busy_worker(self)
331
- throw :stop
332
- else
333
- my_pool.worker_not_old_enough(self)
334
- end
335
-
336
337
  when :stop
337
338
  my_pool.remove_busy_worker(self)
338
339
  throw :stop
@@ -340,9 +341,7 @@ module Concurrent
340
341
  else
341
342
  task, args = message
342
343
  run_task my_pool, task, args
343
- last_message = Concurrent.monotonic_time
344
-
345
- my_pool.ready_worker(self)
344
+ my_pool.ready_worker(self, Concurrent.monotonic_time)
346
345
  end
347
346
  end
348
347
  end
@@ -16,10 +16,10 @@ module Concurrent
16
16
 
17
17
  # @return [Array]
18
18
  def execute(*args)
19
- synchronize do
20
- success = false
21
- value = reason = nil
19
+ success = true
20
+ value = reason = nil
22
21
 
22
+ synchronize do
23
23
  begin
24
24
  value = @task.call(*args)
25
25
  success = true
@@ -27,9 +27,9 @@ module Concurrent
27
27
  reason = ex
28
28
  success = false
29
29
  end
30
-
31
- [success, value, reason]
32
30
  end
31
+
32
+ [success, value, reason]
33
33
  end
34
34
  end
35
35
  end
@@ -73,7 +73,8 @@ module Concurrent
73
73
  # @option opts [Symbol] :fallback_policy (:abort) the policy for handling new
74
74
  # tasks that are received when the queue size has reached
75
75
  # `max_queue` or the executor has shut down
76
- #
76
+ # @option opts [Boolean] :synchronous (DEFAULT_SYNCHRONOUS) whether or not a value of 0
77
+ # for :max_queue means the queue must perform direct hand-off rather than unbounded.
77
78
  # @raise [ArgumentError] if `:max_threads` is less than one
78
79
  # @raise [ArgumentError] if `:min_threads` is less than zero
79
80
  # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified
@@ -10,7 +10,7 @@ module Concurrent
10
10
  # or writing at a time. This includes iteration methods like `#each`,
11
11
  # which takes the lock repeatedly when reading an item.
12
12
  #
13
- # @see http://ruby-doc.org/core-2.2.0/Hash.html Ruby standard library `Hash`
13
+ # @see http://ruby-doc.org/core/Hash.html Ruby standard library `Hash`
14
14
 
15
15
  # @!macro internal_implementation_note
16
16
  HashImplementation = case
@@ -5,7 +5,7 @@ module Concurrent
5
5
 
6
6
  # A thread-safe, immutable variation of Ruby's standard `Struct`.
7
7
  #
8
- # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
8
+ # @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
9
9
  module ImmutableStruct
10
10
  include Synchronization::AbstractStruct
11
11
 
@@ -15,7 +15,10 @@ module Concurrent
15
15
  when Concurrent.on_cruby?
16
16
  require 'concurrent/collection/map/mri_map_backend'
17
17
  MriMapBackend
18
- when Concurrent.on_rbx? || Concurrent.on_truffleruby?
18
+ when Concurrent.on_truffleruby? && defined?(::TruffleRuby::ConcurrentMap)
19
+ require 'concurrent/collection/map/truffleruby_map_backend'
20
+ TruffleRubyMapBackend
21
+ when Concurrent.on_truffleruby? || Concurrent.on_rbx?
19
22
  require 'concurrent/collection/map/atomic_reference_map_backend'
20
23
  AtomicReferenceMapBackend
21
24
  else
@@ -114,7 +117,7 @@ module Concurrent
114
117
  # @return [true, false] true if deleted
115
118
  # @!macro map.atomic_method
116
119
 
117
-
120
+ #
118
121
  def initialize(options = nil, &block)
119
122
  if options.kind_of?(::Hash)
120
123
  validate_options_hash!(options)
@@ -143,8 +146,15 @@ module Concurrent
143
146
  end
144
147
  end
145
148
 
149
+ # Set a value with key
150
+ # @param [Object] key
151
+ # @param [Object] value
152
+ # @return [Object] the new value
153
+ def []=(key, value)
154
+ super
155
+ end
156
+
146
157
  alias_method :get, :[]
147
- # TODO (pitr-ch 30-Oct-2018): doc
148
158
  alias_method :put, :[]=
149
159
 
150
160
  # Get a value with key, or default_value when key is absent,
@@ -271,7 +281,6 @@ module Concurrent
271
281
  each_pair { |k, v| return k if v == value }
272
282
  nil
273
283
  end unless method_defined?(:key)
274
- alias_method :index, :key if RUBY_VERSION < '1.9'
275
284
 
276
285
  # Is map empty?
277
286
  # @return [true, false]
@@ -6,7 +6,7 @@ module Concurrent
6
6
  # An thread-safe variation of Ruby's standard `Struct`. Values can be set at
7
7
  # construction or safely changed at any time during the object's lifecycle.
8
8
  #
9
- # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
9
+ # @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
10
10
  module MutableStruct
11
11
  include Synchronization::AbstractStruct
12
12
 
@@ -40,7 +40,7 @@ module Concurrent
40
40
  # struct. Unset parameters default to nil. Passing more parameters than number of attributes
41
41
  # will raise an `ArgumentError`.
42
42
  #
43
- # @see http://ruby-doc.org/core-2.2.0/Struct.html#method-c-new Ruby standard library `Struct#new`
43
+ # @see http://ruby-doc.org/core/Struct.html#method-c-new Ruby standard library `Struct#new`
44
44
 
45
45
  # @!macro struct_values
46
46
  #
@@ -250,6 +250,7 @@ module Concurrent
250
250
  realize(@promise_body)
251
251
  end
252
252
  else
253
+ compare_and_set_state(:pending, :unscheduled)
253
254
  @parent.execute
254
255
  end
255
256
  self
@@ -58,29 +58,42 @@ module Concurrent
58
58
  # @example Basic usage
59
59
  #
60
60
  # require 'concurrent'
61
- # require 'thread' # for Queue
62
- # require 'open-uri' # for open(uri)
61
+ # require 'csv'
62
+ # require 'open-uri'
63
63
  #
64
64
  # class Ticker
65
- # def get_year_end_closing(symbol, year)
66
- # uri = "http://ichart.finance.yahoo.com/table.csv?s=#{symbol}&a=11&b=01&c=#{year}&d=11&e=31&f=#{year}&g=m"
67
- # data = open(uri) {|f| f.collect{|line| line.strip } }
68
- # data[1].split(',')[4].to_f
69
- # end
65
+ # def get_year_end_closing(symbol, year, api_key)
66
+ # uri = "https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=#{symbol}&apikey=#{api_key}&datatype=csv"
67
+ # data = []
68
+ # csv = URI.parse(uri).read
69
+ # if csv.include?('call frequency')
70
+ # return :rate_limit_exceeded
71
+ # end
72
+ # CSV.parse(csv, headers: true) do |row|
73
+ # data << row['close'].to_f if row['timestamp'].include?(year.to_s)
74
+ # end
75
+ # year_end = data.first
76
+ # year_end
77
+ # rescue => e
78
+ # p e
79
+ # end
70
80
  # end
71
81
  #
82
+ # api_key = ENV['ALPHAVANTAGE_KEY']
83
+ # abort(error_message) unless api_key
84
+ #
72
85
  # # Future
73
- # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013) }
86
+ # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013, api_key) }
74
87
  # price.state #=> :pending
75
- # sleep(1) # do other stuff
76
- # price.value #=> 63.65
77
- # price.state #=> :fulfilled
88
+ # price.pending? #=> true
89
+ # price.value(0) #=> nil (does not block)
78
90
  #
79
- # # ScheduledTask
80
- # task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) }
81
- # task.state #=> :pending
82
- # sleep(3) # do other stuff
83
- # task.value #=> 25.96
91
+ # sleep(1) # do other stuff
92
+ #
93
+ # price.value #=> 63.65 (after blocking if necessary)
94
+ # price.state #=> :fulfilled
95
+ # price.fulfilled? #=> true
96
+ # price.value #=> 63.65
84
97
  #
85
98
  # @example Successful task execution
86
99
  #
@@ -19,13 +19,19 @@ module Concurrent
19
19
  #
20
20
  # @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set`
21
21
 
22
-
23
22
  # @!macro internal_implementation_note
24
23
  SetImplementation = case
25
24
  when Concurrent.on_cruby?
26
- # Because MRI never runs code in parallel, the existing
27
- # non-thread-safe structures should usually work fine.
28
- ::Set
25
+ # The CRuby implementation of Set is written in Ruby itself and is
26
+ # not thread safe for certain methods.
27
+ require 'monitor'
28
+ require 'concurrent/thread_safe/util/data_structures'
29
+
30
+ class CRubySet < ::Set
31
+ end
32
+
33
+ ThreadSafe::Util.make_synchronized_on_cruby CRubySet
34
+ CRubySet
29
35
 
30
36
  when Concurrent.on_jruby?
31
37
  require 'jruby/synchronized'
@@ -33,6 +39,7 @@ module Concurrent
33
39
  class JRubySet < ::Set
34
40
  include JRuby::Synchronized
35
41
  end
42
+
36
43
  JRubySet
37
44
 
38
45
  when Concurrent.on_rbx?
@@ -41,7 +48,8 @@ module Concurrent
41
48
 
42
49
  class RbxSet < ::Set
43
50
  end
44
- ThreadSafe::Util.make_synchronized_on_rbx Concurrent::RbxSet
51
+
52
+ ThreadSafe::Util.make_synchronized_on_rbx RbxSet
45
53
  RbxSet
46
54
 
47
55
  when Concurrent.on_truffleruby?
@@ -50,7 +58,7 @@ module Concurrent
50
58
  class TruffleRubySet < ::Set
51
59
  end
52
60
 
53
- ThreadSafe::Util.make_synchronized_on_truffleruby Concurrent::TruffleRubySet
61
+ ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubySet
54
62
  TruffleRubySet
55
63
 
56
64
  else
@@ -9,7 +9,7 @@ module Concurrent
9
9
  # or any time thereafter. Attempting to assign a value to a member
10
10
  # that has already been set will result in a `Concurrent::ImmutabilityError`.
11
11
  #
12
- # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
12
+ # @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
13
13
  # @see http://en.wikipedia.org/wiki/Final_(Java) Java `final` keyword
14
14
  module SettableStruct
15
15
  include Synchronization::AbstractStruct
@@ -4,9 +4,7 @@ module Concurrent
4
4
  # @!visibility private
5
5
  # @!macro internal_implementation_note
6
6
  LockableObjectImplementation = case
7
- when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3)
8
- MonitorLockableObject
9
- when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
7
+ when Concurrent.on_cruby?
10
8
  MutexLockableObject
11
9
  when Concurrent.on_jruby?
12
10
  JRubyLockableObject
@@ -26,8 +24,8 @@ module Concurrent
26
24
  # the classes using it. Use {Synchronization::Object} not this abstract class.
27
25
  #
28
26
  # @note this object does not support usage together with
29
- # [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
30
- # and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
27
+ # [`Thread#wakeup`](http://ruby-doc.org/core/Thread.html#method-i-wakeup)
28
+ # and [`Thread#raise`](http://ruby-doc.org/core/Thread.html#method-i-raise).
31
29
  # `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
32
30
  # `Thread#wakeup` will not work on all platforms.
33
31
  #
@@ -32,6 +32,12 @@ module Concurrent
32
32
  @__Condition__ = ::ConditionVariable.new
33
33
  end
34
34
 
35
+ def initialize_copy(other)
36
+ super
37
+ @__Lock__ = ::Mutex.new
38
+ @__Condition__ = ::ConditionVariable.new
39
+ end
40
+
35
41
  protected
36
42
 
37
43
  def synchronize
@@ -61,6 +67,12 @@ module Concurrent
61
67
  @__Condition__ = @__Lock__.new_cond
62
68
  end
63
69
 
70
+ def initialize_copy(other)
71
+ super
72
+ @__Lock__ = ::Monitor.new
73
+ @__Condition__ = @__Lock__.new_cond
74
+ end
75
+
64
76
  protected
65
77
 
66
78
  def synchronize # TODO may be a problem with lock.synchronize { lock.wait }
@@ -12,6 +12,12 @@ module Concurrent
12
12
  @__owner__ = nil
13
13
  end
14
14
 
15
+ def initialize_copy(other)
16
+ super
17
+ @__Waiters__ = []
18
+ @__owner__ = nil
19
+ end
20
+
15
21
  protected
16
22
 
17
23
  def synchronize(&block)
@@ -12,12 +12,37 @@ end
12
12
  module Concurrent
13
13
  module ThreadSafe
14
14
  module Util
15
+ def self.make_synchronized_on_cruby(klass)
16
+ klass.class_eval do
17
+ def initialize(*args, &block)
18
+ @_monitor = Monitor.new
19
+ super
20
+ end
21
+
22
+ def initialize_copy(other)
23
+ # make sure a copy is not sharing a monitor with the original object!
24
+ @_monitor = Monitor.new
25
+ super
26
+ end
27
+ end
28
+
29
+ klass.superclass.instance_methods(false).each do |method|
30
+ klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
31
+ def #{method}(*args)
32
+ monitor = @_monitor
33
+ monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.")
34
+ monitor.synchronize { super }
35
+ end
36
+ RUBY
37
+ end
38
+ end
39
+
15
40
  def self.make_synchronized_on_rbx(klass)
16
41
  klass.class_eval do
17
42
  private
18
43
 
19
44
  def _mon_initialize
20
- @_monitor = Monitor.new unless @_monitor # avoid double initialisation
45
+ @_monitor ||= Monitor.new # avoid double initialisation
21
46
  end
22
47
 
23
48
  def self.new(*args)
@@ -97,7 +97,7 @@ module Concurrent
97
97
  # TODO: this only adds padding after the :value slot, need to find a way to add padding before the slot
98
98
  # TODO (pitr-ch 28-Jul-2018): the padding instance vars may not be created
99
99
  # hide from yardoc in a method
100
- attr_reader *(12.times.collect{ |i| "padding_#{i}".to_sym })
100
+ attr_reader :padding_0, :padding_1, :padding_2, :padding_3, :padding_4, :padding_5, :padding_6, :padding_7, :padding_8, :padding_9, :padding_10, :padding_11
101
101
  end
102
102
  padding
103
103
  end