concurrent-ruby 1.1.6 → 1.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/Gemfile +3 -8
- data/{LICENSE.md → LICENSE.txt} +18 -20
- data/README.md +43 -23
- data/Rakefile +32 -35
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
- data/lib/concurrent-ruby/concurrent/array.rb +1 -1
- data/lib/concurrent-ruby/concurrent/async.rb +10 -20
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +1 -0
- data/lib/concurrent-ruby/concurrent/atomic/event.rb +2 -2
- data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +18 -2
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +4 -6
- data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +52 -42
- data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +26 -5
- data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
- data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +16 -13
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +20 -3
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +17 -1
- data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +37 -38
- data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +5 -5
- data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +2 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +1 -1
- data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/map.rb +13 -4
- data/lib/concurrent-ruby/concurrent/mutable_struct.rb +2 -2
- data/lib/concurrent-ruby/concurrent/promise.rb +1 -0
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +29 -16
- data/lib/concurrent-ruby/concurrent/set.rb +14 -6
- data/lib/concurrent-ruby/concurrent/settable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +3 -5
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +12 -0
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +6 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +26 -1
- data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +1 -1
- data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -34
- data/lib/concurrent-ruby/concurrent/tvar.rb +19 -56
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +67 -35
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +2 -35
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
- 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
|
-
|
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
|
-
|
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
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
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
|
-
|
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
|
-
|
20
|
-
|
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
|
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
|
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.
|
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
|
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
|
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
|
#
|
@@ -58,29 +58,42 @@ module Concurrent
|
|
58
58
|
# @example Basic usage
|
59
59
|
#
|
60
60
|
# require 'concurrent'
|
61
|
-
# require '
|
62
|
-
# require 'open-uri'
|
61
|
+
# require 'csv'
|
62
|
+
# require 'open-uri'
|
63
63
|
#
|
64
64
|
# class Ticker
|
65
|
-
# def get_year_end_closing(symbol, year)
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
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
|
-
#
|
76
|
-
# price.value #=>
|
77
|
-
# price.state #=> :fulfilled
|
88
|
+
# price.pending? #=> true
|
89
|
+
# price.value(0) #=> nil (does not block)
|
78
90
|
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
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
|
-
#
|
27
|
-
#
|
28
|
-
|
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
|
-
|
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
|
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
|
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?
|
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
|
30
|
-
# and [`Thread#raise`](http://ruby-doc.org/core
|
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,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
|
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
|
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
|