concurrent-ruby 1.1.5
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +478 -0
- data/Gemfile +41 -0
- data/LICENSE.md +23 -0
- data/README.md +381 -0
- data/Rakefile +327 -0
- data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
- data/lib/concurrent-ruby.rb +1 -0
- data/lib/concurrent.rb +134 -0
- data/lib/concurrent/agent.rb +587 -0
- data/lib/concurrent/array.rb +66 -0
- data/lib/concurrent/async.rb +459 -0
- data/lib/concurrent/atom.rb +222 -0
- data/lib/concurrent/atomic/abstract_thread_local_var.rb +66 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +126 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +143 -0
- data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent/atomic/atomic_reference.rb +204 -0
- data/lib/concurrent/atomic/count_down_latch.rb +100 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +128 -0
- data/lib/concurrent/atomic/event.rb +109 -0
- data/lib/concurrent/atomic/java_count_down_latch.rb +42 -0
- data/lib/concurrent/atomic/java_thread_local_var.rb +37 -0
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +44 -0
- data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
- data/lib/concurrent/atomic/read_write_lock.rb +254 -0
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +379 -0
- data/lib/concurrent/atomic/ruby_thread_local_var.rb +161 -0
- data/lib/concurrent/atomic/semaphore.rb +145 -0
- data/lib/concurrent/atomic/thread_local_var.rb +104 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +56 -0
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
- data/lib/concurrent/atomics.rb +10 -0
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +111 -0
- data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
- data/lib/concurrent/collection/lock_free_stack.rb +158 -0
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
- data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
- data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
- data/lib/concurrent/collection/map/synchronized_map_backend.rb +82 -0
- data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
- data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
- data/lib/concurrent/concern/deprecation.rb +34 -0
- data/lib/concurrent/concern/dereferenceable.rb +73 -0
- data/lib/concurrent/concern/logging.rb +32 -0
- data/lib/concurrent/concern/obligation.rb +220 -0
- data/lib/concurrent/concern/observable.rb +110 -0
- data/lib/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent/configuration.rb +184 -0
- data/lib/concurrent/constants.rb +8 -0
- data/lib/concurrent/dataflow.rb +81 -0
- data/lib/concurrent/delay.rb +199 -0
- data/lib/concurrent/errors.rb +69 -0
- data/lib/concurrent/exchanger.rb +352 -0
- data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
- data/lib/concurrent/executor/cached_thread_pool.rb +62 -0
- data/lib/concurrent/executor/executor_service.rb +185 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +206 -0
- data/lib/concurrent/executor/immediate_executor.rb +66 -0
- data/lib/concurrent/executor/indirect_immediate_executor.rb +44 -0
- data/lib/concurrent/executor/java_executor_service.rb +91 -0
- data/lib/concurrent/executor/java_single_thread_executor.rb +29 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +123 -0
- data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +22 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +362 -0
- data/lib/concurrent/executor/safe_task_executor.rb +35 -0
- data/lib/concurrent/executor/serial_executor_service.rb +34 -0
- data/lib/concurrent/executor/serialized_execution.rb +107 -0
- data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
- data/lib/concurrent/executor/simple_executor_service.rb +100 -0
- data/lib/concurrent/executor/single_thread_executor.rb +56 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +87 -0
- data/lib/concurrent/executor/timer_set.rb +173 -0
- data/lib/concurrent/executors.rb +20 -0
- data/lib/concurrent/future.rb +141 -0
- data/lib/concurrent/hash.rb +59 -0
- data/lib/concurrent/immutable_struct.rb +93 -0
- data/lib/concurrent/ivar.rb +207 -0
- data/lib/concurrent/map.rb +337 -0
- data/lib/concurrent/maybe.rb +229 -0
- data/lib/concurrent/mutable_struct.rb +229 -0
- data/lib/concurrent/mvar.rb +242 -0
- data/lib/concurrent/options.rb +42 -0
- data/lib/concurrent/promise.rb +579 -0
- data/lib/concurrent/promises.rb +2167 -0
- data/lib/concurrent/re_include.rb +58 -0
- data/lib/concurrent/scheduled_task.rb +318 -0
- data/lib/concurrent/set.rb +66 -0
- data/lib/concurrent/settable_struct.rb +129 -0
- data/lib/concurrent/synchronization.rb +30 -0
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
- data/lib/concurrent/synchronization/abstract_object.rb +24 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +160 -0
- data/lib/concurrent/synchronization/condition.rb +60 -0
- data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent/synchronization/jruby_object.rb +45 -0
- data/lib/concurrent/synchronization/lock.rb +36 -0
- data/lib/concurrent/synchronization/lockable_object.rb +74 -0
- data/lib/concurrent/synchronization/mri_object.rb +44 -0
- data/lib/concurrent/synchronization/mutex_lockable_object.rb +76 -0
- data/lib/concurrent/synchronization/object.rb +183 -0
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
- data/lib/concurrent/synchronization/rbx_object.rb +49 -0
- data/lib/concurrent/synchronization/truffleruby_object.rb +47 -0
- data/lib/concurrent/synchronization/volatile.rb +36 -0
- data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
- data/lib/concurrent/thread_safe/util.rb +16 -0
- data/lib/concurrent/thread_safe/util/adder.rb +74 -0
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
- data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +246 -0
- data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
- data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
- data/lib/concurrent/timer_task.rb +334 -0
- data/lib/concurrent/tuple.rb +86 -0
- data/lib/concurrent/tvar.rb +258 -0
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +56 -0
- data/lib/concurrent/utility/monotonic_time.rb +58 -0
- data/lib/concurrent/utility/native_extension_loader.rb +79 -0
- data/lib/concurrent/utility/native_integer.rb +53 -0
- data/lib/concurrent/utility/processor_counter.rb +158 -0
- data/lib/concurrent/version.rb +3 -0
- metadata +193 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'concurrent/atomic/atomic_reference'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
# A fixed size array with volatile (synchronized, thread safe) getters/setters.
|
6
|
+
# Mixes in Ruby's `Enumerable` module for enhanced search, sort, and traversal.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# tuple = Concurrent::Tuple.new(16)
|
10
|
+
#
|
11
|
+
# tuple.set(0, :foo) #=> :foo | volatile write
|
12
|
+
# tuple.get(0) #=> :foo | volatile read
|
13
|
+
# tuple.compare_and_set(0, :foo, :bar) #=> true | strong CAS
|
14
|
+
# tuple.cas(0, :foo, :baz) #=> false | strong CAS
|
15
|
+
# tuple.get(0) #=> :bar | volatile read
|
16
|
+
#
|
17
|
+
# @see https://en.wikipedia.org/wiki/Tuple Tuple entry at Wikipedia
|
18
|
+
# @see http://www.erlang.org/doc/reference_manual/data_types.html#id70396 Erlang Tuple
|
19
|
+
# @see http://ruby-doc.org/core-2.2.2/Enumerable.html Enumerable
|
20
|
+
class Tuple
|
21
|
+
include Enumerable
|
22
|
+
|
23
|
+
# The (fixed) size of the tuple.
|
24
|
+
attr_reader :size
|
25
|
+
|
26
|
+
# @!visibility private
|
27
|
+
Tuple = defined?(Rubinius::Tuple) ? Rubinius::Tuple : ::Array
|
28
|
+
private_constant :Tuple
|
29
|
+
|
30
|
+
# Create a new tuple of the given size.
|
31
|
+
#
|
32
|
+
# @param [Integer] size the number of elements in the tuple
|
33
|
+
def initialize(size)
|
34
|
+
@size = size
|
35
|
+
@tuple = tuple = Tuple.new(size)
|
36
|
+
i = 0
|
37
|
+
while i < size
|
38
|
+
tuple[i] = Concurrent::AtomicReference.new
|
39
|
+
i += 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get the value of the element at the given index.
|
44
|
+
#
|
45
|
+
# @param [Integer] i the index from which to retrieve the value
|
46
|
+
# @return [Object] the value at the given index or nil if the index is out of bounds
|
47
|
+
def get(i)
|
48
|
+
return nil if i >= @size || i < 0
|
49
|
+
@tuple[i].get
|
50
|
+
end
|
51
|
+
alias_method :volatile_get, :get
|
52
|
+
|
53
|
+
# Set the element at the given index to the given value
|
54
|
+
#
|
55
|
+
# @param [Integer] i the index for the element to set
|
56
|
+
# @param [Object] value the value to set at the given index
|
57
|
+
#
|
58
|
+
# @return [Object] the new value of the element at the given index or nil if the index is out of bounds
|
59
|
+
def set(i, value)
|
60
|
+
return nil if i >= @size || i < 0
|
61
|
+
@tuple[i].set(value)
|
62
|
+
end
|
63
|
+
alias_method :volatile_set, :set
|
64
|
+
|
65
|
+
# Set the value at the given index to the new value if and only if the current
|
66
|
+
# value matches the given old value.
|
67
|
+
#
|
68
|
+
# @param [Integer] i the index for the element to set
|
69
|
+
# @param [Object] old_value the value to compare against the current value
|
70
|
+
# @param [Object] new_value the value to set at the given index
|
71
|
+
#
|
72
|
+
# @return [Boolean] true if the value at the given element was set else false
|
73
|
+
def compare_and_set(i, old_value, new_value)
|
74
|
+
return false if i >= @size || i < 0
|
75
|
+
@tuple[i].compare_and_set(old_value, new_value)
|
76
|
+
end
|
77
|
+
alias_method :cas, :compare_and_set
|
78
|
+
|
79
|
+
# Calls the given block once for each element in self, passing that element as a parameter.
|
80
|
+
#
|
81
|
+
# @yieldparam [Object] ref the `Concurrent::AtomicReference` object at the current index
|
82
|
+
def each
|
83
|
+
@tuple.each {|ref| yield ref.get}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,258 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'concurrent/synchronization'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# A `TVar` is a transactional variable - a single-element container that
|
7
|
+
# is used as part of a transaction - see `Concurrent::atomically`.
|
8
|
+
#
|
9
|
+
# @!macro thread_safe_variable_comparison
|
10
|
+
#
|
11
|
+
# {include:file:docs-source/tvar.md}
|
12
|
+
class TVar < Synchronization::Object
|
13
|
+
safe_initialization!
|
14
|
+
|
15
|
+
# Create a new `TVar` with an initial value.
|
16
|
+
def initialize(value)
|
17
|
+
@value = value
|
18
|
+
@version = 0
|
19
|
+
@lock = Mutex.new
|
20
|
+
end
|
21
|
+
|
22
|
+
# Get the value of a `TVar`.
|
23
|
+
def value
|
24
|
+
Concurrent::atomically do
|
25
|
+
Transaction::current.read(self)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set the value of a `TVar`.
|
30
|
+
def value=(value)
|
31
|
+
Concurrent::atomically do
|
32
|
+
Transaction::current.write(self, value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @!visibility private
|
37
|
+
def unsafe_value # :nodoc:
|
38
|
+
@value
|
39
|
+
end
|
40
|
+
|
41
|
+
# @!visibility private
|
42
|
+
def unsafe_value=(value) # :nodoc:
|
43
|
+
@value = value
|
44
|
+
end
|
45
|
+
|
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
|
+
# @!visibility private
|
57
|
+
def unsafe_lock # :nodoc:
|
58
|
+
@lock
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
# Run a block that reads and writes `TVar`s as a single atomic transaction.
|
64
|
+
# With respect to the value of `TVar` objects, the transaction is atomic, in
|
65
|
+
# that it either happens or it does not, consistent, in that the `TVar`
|
66
|
+
# objects involved will never enter an illegal state, and isolated, in that
|
67
|
+
# transactions never interfere with each other. You may recognise these
|
68
|
+
# properties from database transactions.
|
69
|
+
#
|
70
|
+
# There are some very important and unusual semantics that you must be aware of:
|
71
|
+
#
|
72
|
+
# * Most importantly, the block that you pass to atomically may be executed
|
73
|
+
# more than once. In most cases your code should be free of
|
74
|
+
# side-effects, except for via TVar.
|
75
|
+
#
|
76
|
+
# * If an exception escapes an atomically block it will abort the transaction.
|
77
|
+
#
|
78
|
+
# * It is undefined behaviour to use callcc or Fiber with atomically.
|
79
|
+
#
|
80
|
+
# * If you create a new thread within an atomically, it will not be part of
|
81
|
+
# the transaction. Creating a thread counts as a side-effect.
|
82
|
+
#
|
83
|
+
# Transactions within transactions are flattened to a single transaction.
|
84
|
+
#
|
85
|
+
# @example
|
86
|
+
# a = new TVar(100_000)
|
87
|
+
# b = new TVar(100)
|
88
|
+
#
|
89
|
+
# Concurrent::atomically do
|
90
|
+
# a.value -= 10
|
91
|
+
# b.value += 10
|
92
|
+
# end
|
93
|
+
def atomically
|
94
|
+
raise ArgumentError.new('no block given') unless block_given?
|
95
|
+
|
96
|
+
# Get the current transaction
|
97
|
+
|
98
|
+
transaction = Transaction::current
|
99
|
+
|
100
|
+
# Are we not already in a transaction (not nested)?
|
101
|
+
|
102
|
+
if transaction.nil?
|
103
|
+
# New transaction
|
104
|
+
|
105
|
+
begin
|
106
|
+
# Retry loop
|
107
|
+
|
108
|
+
loop do
|
109
|
+
|
110
|
+
# Create a new transaction
|
111
|
+
|
112
|
+
transaction = Transaction.new
|
113
|
+
Transaction::current = transaction
|
114
|
+
|
115
|
+
# Run the block, aborting on exceptions
|
116
|
+
|
117
|
+
begin
|
118
|
+
result = yield
|
119
|
+
rescue Transaction::AbortError => e
|
120
|
+
transaction.abort
|
121
|
+
result = Transaction::ABORTED
|
122
|
+
rescue Transaction::LeaveError => e
|
123
|
+
transaction.abort
|
124
|
+
break result
|
125
|
+
rescue => e
|
126
|
+
transaction.abort
|
127
|
+
raise e
|
128
|
+
end
|
129
|
+
# If we can commit, break out of the loop
|
130
|
+
|
131
|
+
if result != Transaction::ABORTED
|
132
|
+
if transaction.commit
|
133
|
+
break result
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
ensure
|
138
|
+
# Clear the current transaction
|
139
|
+
|
140
|
+
Transaction::current = nil
|
141
|
+
end
|
142
|
+
else
|
143
|
+
# Nested transaction - flatten it and just run the block
|
144
|
+
|
145
|
+
yield
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Abort a currently running transaction - see `Concurrent::atomically`.
|
150
|
+
def abort_transaction
|
151
|
+
raise Transaction::AbortError.new
|
152
|
+
end
|
153
|
+
|
154
|
+
# Leave a transaction without committing or aborting - see `Concurrent::atomically`.
|
155
|
+
def leave_transaction
|
156
|
+
raise Transaction::LeaveError.new
|
157
|
+
end
|
158
|
+
|
159
|
+
module_function :atomically, :abort_transaction, :leave_transaction
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
class Transaction
|
164
|
+
|
165
|
+
ABORTED = ::Object.new
|
166
|
+
|
167
|
+
ReadLogEntry = Struct.new(:tvar, :version)
|
168
|
+
|
169
|
+
AbortError = Class.new(StandardError)
|
170
|
+
LeaveError = Class.new(StandardError)
|
171
|
+
|
172
|
+
def initialize
|
173
|
+
@read_log = []
|
174
|
+
@write_log = {}
|
175
|
+
end
|
176
|
+
|
177
|
+
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
|
186
|
+
end
|
187
|
+
|
188
|
+
def write(tvar, value)
|
189
|
+
# Have we already written to this TVar?
|
190
|
+
|
191
|
+
unless @write_log.has_key? tvar
|
192
|
+
# Try to lock the TVar
|
193
|
+
|
194
|
+
unless tvar.unsafe_lock.try_lock
|
195
|
+
# Someone else is writing to this TVar - abort
|
196
|
+
Concurrent::abort_transaction
|
197
|
+
end
|
198
|
+
|
199
|
+
# If we previously wrote to it, check the version hasn't changed
|
200
|
+
|
201
|
+
@read_log.each do |log_entry|
|
202
|
+
if log_entry.tvar == tvar and tvar.unsafe_version > log_entry.version
|
203
|
+
Concurrent::abort_transaction
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Record the value written
|
209
|
+
|
210
|
+
@write_log[tvar] = value
|
211
|
+
end
|
212
|
+
|
213
|
+
def abort
|
214
|
+
unlock
|
215
|
+
end
|
216
|
+
|
217
|
+
def commit
|
218
|
+
return false unless valid?
|
219
|
+
|
220
|
+
@write_log.each_pair do |tvar, value|
|
221
|
+
tvar.unsafe_value = value
|
222
|
+
tvar.unsafe_increment_version
|
223
|
+
end
|
224
|
+
|
225
|
+
unlock
|
226
|
+
|
227
|
+
true
|
228
|
+
end
|
229
|
+
|
230
|
+
def valid?
|
231
|
+
@read_log.each do |log_entry|
|
232
|
+
unless @write_log.has_key? log_entry.tvar
|
233
|
+
if log_entry.tvar.unsafe_version > log_entry.version
|
234
|
+
return false
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
true
|
240
|
+
end
|
241
|
+
|
242
|
+
def unlock
|
243
|
+
@write_log.each_key do |tvar|
|
244
|
+
tvar.unsafe_lock.unlock
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def self.current
|
249
|
+
Thread.current[:current_tvar_transaction]
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.current=(transaction)
|
253
|
+
Thread.current[:current_tvar_transaction] = transaction
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'concurrent/synchronization'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# Provides ability to add and remove handlers to be run at `Kernel#at_exit`, order is undefined.
|
7
|
+
# Each handler is executed at most once.
|
8
|
+
#
|
9
|
+
# @!visibility private
|
10
|
+
class AtExitImplementation < Synchronization::LockableObject
|
11
|
+
include Logger::Severity
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
super()
|
15
|
+
synchronize { ns_initialize(*args) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Add a handler to be run at `Kernel#at_exit`
|
19
|
+
# @param [Object] handler_id optionally provide an id, if already present, handler is replaced
|
20
|
+
# @yield the handler
|
21
|
+
# @return id of the handler
|
22
|
+
def add(handler_id = nil, &handler)
|
23
|
+
id = handler_id || handler.object_id
|
24
|
+
synchronize { @handlers[id] = handler }
|
25
|
+
id
|
26
|
+
end
|
27
|
+
|
28
|
+
# Delete a handler by handler_id
|
29
|
+
# @return [true, false]
|
30
|
+
def delete(handler_id)
|
31
|
+
!!synchronize { @handlers.delete handler_id }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Is handler with handler_id rpesent?
|
35
|
+
# @return [true, false]
|
36
|
+
def handler?(handler_id)
|
37
|
+
synchronize { @handlers.key? handler_id }
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return copy of the handlers
|
41
|
+
def handlers
|
42
|
+
synchronize { @handlers }.clone
|
43
|
+
end
|
44
|
+
|
45
|
+
# install `Kernel#at_exit` callback to execute added handlers
|
46
|
+
def install
|
47
|
+
synchronize do
|
48
|
+
@installed ||= begin
|
49
|
+
at_exit { runner }
|
50
|
+
true
|
51
|
+
end
|
52
|
+
self
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Will it run during `Kernel#at_exit`
|
57
|
+
def enabled?
|
58
|
+
synchronize { @enabled }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Configure if it runs during `Kernel#at_exit`
|
62
|
+
def enabled=(value)
|
63
|
+
synchronize { @enabled = value }
|
64
|
+
end
|
65
|
+
|
66
|
+
# run the handlers manually
|
67
|
+
# @return ids of the handlers
|
68
|
+
def run
|
69
|
+
handlers, _ = synchronize { handlers, @handlers = @handlers, {} }
|
70
|
+
handlers.each do |_, handler|
|
71
|
+
begin
|
72
|
+
handler.call
|
73
|
+
rescue => error
|
74
|
+
Concurrent.global_logger.call(ERROR, error)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
handlers.keys
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def ns_initialize(enabled = true)
|
83
|
+
@handlers = {}
|
84
|
+
@enabled = enabled
|
85
|
+
end
|
86
|
+
|
87
|
+
def runner
|
88
|
+
run if synchronize { @enabled }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private_constant :AtExitImplementation
|
93
|
+
|
94
|
+
# @see AtExitImplementation
|
95
|
+
# @!visibility private
|
96
|
+
AtExit = AtExitImplementation.new.install
|
97
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Utility
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
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
|
+
def on_cruby?
|
15
|
+
ruby_engine == 'ruby'
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_rbx?
|
19
|
+
ruby_engine == 'rbx'
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_truffleruby?
|
23
|
+
ruby_engine == 'truffleruby'
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_windows?
|
27
|
+
!(RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/).nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def on_osx?
|
31
|
+
!(RbConfig::CONFIG['host_os'] =~ /darwin|mac os/).nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_linux?
|
35
|
+
!(RbConfig::CONFIG['host_os'] =~ /linux/).nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
def ruby_engine
|
39
|
+
defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
|
40
|
+
end
|
41
|
+
|
42
|
+
def ruby_version(version = RUBY_VERSION, comparison, major, minor, patch)
|
43
|
+
result = (version.split('.').map(&:to_i) <=> [major, minor, patch])
|
44
|
+
comparisons = { :== => [0],
|
45
|
+
:>= => [1, 0],
|
46
|
+
:<= => [-1, 0],
|
47
|
+
:> => [1],
|
48
|
+
:< => [-1] }
|
49
|
+
comparisons.fetch(comparison).include? result
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @!visibility private
|
55
|
+
extend Utility::EngineDetector
|
56
|
+
end
|