o-concurrent-ruby 1.1.11
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 +542 -0
- data/Gemfile +37 -0
- data/LICENSE.txt +21 -0
- data/README.md +404 -0
- data/Rakefile +307 -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 +189 -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/concurrent/agent.rb +587 -0
- data/lib/concurrent-ruby/concurrent/array.rb +66 -0
- data/lib/concurrent-ruby/concurrent/async.rb +449 -0
- data/lib/concurrent-ruby/concurrent/atom.rb +222 -0
- data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +66 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +126 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +143 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
- data/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb +100 -0
- data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +128 -0
- data/lib/concurrent-ruby/concurrent/atomic/event.rb +109 -0
- data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +42 -0
- data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +37 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +44 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +131 -0
- data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +254 -0
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +377 -0
- data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +181 -0
- data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +166 -0
- data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +104 -0
- data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +56 -0
- data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
- data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
- data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
- data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +111 -0
- data/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
- data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
- data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
- data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +66 -0
- data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
- data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +82 -0
- data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
- data/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
- data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +160 -0
- data/lib/concurrent-ruby/concurrent/concern/deprecation.rb +34 -0
- data/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb +73 -0
- data/lib/concurrent-ruby/concurrent/concern/logging.rb +32 -0
- data/lib/concurrent-ruby/concurrent/concern/obligation.rb +220 -0
- data/lib/concurrent-ruby/concurrent/concern/observable.rb +110 -0
- data/lib/concurrent-ruby/concurrent/configuration.rb +188 -0
- data/lib/concurrent-ruby/concurrent/constants.rb +8 -0
- data/lib/concurrent-ruby/concurrent/dataflow.rb +81 -0
- data/lib/concurrent-ruby/concurrent/delay.rb +199 -0
- data/lib/concurrent-ruby/concurrent/errors.rb +69 -0
- data/lib/concurrent-ruby/concurrent/exchanger.rb +352 -0
- data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +131 -0
- data/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb +62 -0
- data/lib/concurrent-ruby/concurrent/executor/executor_service.rb +185 -0
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +220 -0
- data/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb +66 -0
- data/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb +44 -0
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +103 -0
- data/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb +30 -0
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +140 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +82 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +21 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +368 -0
- data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +35 -0
- data/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb +34 -0
- data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +107 -0
- data/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb +28 -0
- data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +100 -0
- data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +57 -0
- data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +88 -0
- data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +172 -0
- data/lib/concurrent-ruby/concurrent/executors.rb +20 -0
- data/lib/concurrent-ruby/concurrent/future.rb +141 -0
- data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
- data/lib/concurrent-ruby/concurrent/immutable_struct.rb +101 -0
- data/lib/concurrent-ruby/concurrent/ivar.rb +207 -0
- data/lib/concurrent-ruby/concurrent/map.rb +346 -0
- data/lib/concurrent-ruby/concurrent/maybe.rb +229 -0
- data/lib/concurrent-ruby/concurrent/mutable_struct.rb +239 -0
- data/lib/concurrent-ruby/concurrent/mvar.rb +242 -0
- data/lib/concurrent-ruby/concurrent/options.rb +42 -0
- data/lib/concurrent-ruby/concurrent/promise.rb +580 -0
- data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
- data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +331 -0
- data/lib/concurrent-ruby/concurrent/set.rb +74 -0
- data/lib/concurrent-ruby/concurrent/settable_struct.rb +139 -0
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +98 -0
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +24 -0
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +171 -0
- data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +60 -0
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +45 -0
- data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +36 -0
- data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +72 -0
- data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +44 -0
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
- data/lib/concurrent-ruby/concurrent/synchronization/object.rb +183 -0
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +71 -0
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +49 -0
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
- data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +36 -0
- data/lib/concurrent-ruby/concurrent/synchronization.rb +30 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +50 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +74 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +246 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb +75 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util.rb +16 -0
- data/lib/concurrent-ruby/concurrent/timer_task.rb +311 -0
- data/lib/concurrent-ruby/concurrent/tuple.rb +86 -0
- data/lib/concurrent-ruby/concurrent/tvar.rb +221 -0
- data/lib/concurrent-ruby/concurrent/utility/engine.rb +56 -0
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
- data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
- data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +53 -0
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +130 -0
- data/lib/concurrent-ruby/concurrent/version.rb +3 -0
- data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
- data/lib/concurrent-ruby/concurrent.rb +134 -0
- metadata +192 -0
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'concurrent/atomic/mutex_semaphore'
|
2
|
+
require 'concurrent/synchronization'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
###################################################################
|
7
|
+
|
8
|
+
# @!macro semaphore_method_initialize
|
9
|
+
#
|
10
|
+
# Create a new `Semaphore` with the initial `count`.
|
11
|
+
#
|
12
|
+
# @param [Fixnum] count the initial count
|
13
|
+
#
|
14
|
+
# @raise [ArgumentError] if `count` is not an integer or is less than zero
|
15
|
+
|
16
|
+
# @!macro semaphore_method_acquire
|
17
|
+
#
|
18
|
+
# Acquires the given number of permits from this semaphore,
|
19
|
+
# blocking until all are available. If a block is given,
|
20
|
+
# yields to it and releases the permits afterwards.
|
21
|
+
#
|
22
|
+
# @param [Fixnum] permits Number of permits to acquire
|
23
|
+
#
|
24
|
+
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
25
|
+
# one
|
26
|
+
#
|
27
|
+
# @return [nil, BasicObject] Without a block, `nil` is returned. If a block
|
28
|
+
# is given, its return value is returned.
|
29
|
+
|
30
|
+
# @!macro semaphore_method_available_permits
|
31
|
+
#
|
32
|
+
# Returns the current number of permits available in this semaphore.
|
33
|
+
#
|
34
|
+
# @return [Integer]
|
35
|
+
|
36
|
+
# @!macro semaphore_method_drain_permits
|
37
|
+
#
|
38
|
+
# Acquires and returns all permits that are immediately available.
|
39
|
+
#
|
40
|
+
# @return [Integer]
|
41
|
+
|
42
|
+
# @!macro semaphore_method_try_acquire
|
43
|
+
#
|
44
|
+
# Acquires the given number of permits from this semaphore,
|
45
|
+
# only if all are available at the time of invocation or within
|
46
|
+
# `timeout` interval. If a block is given, yields to it if the permits
|
47
|
+
# were successfully acquired, and releases them afterward, returning the
|
48
|
+
# block's return value.
|
49
|
+
#
|
50
|
+
# @param [Fixnum] permits the number of permits to acquire
|
51
|
+
#
|
52
|
+
# @param [Fixnum] timeout the number of seconds to wait for the counter
|
53
|
+
# or `nil` to return immediately
|
54
|
+
#
|
55
|
+
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
56
|
+
# one
|
57
|
+
#
|
58
|
+
# @return [true, false, nil, BasicObject] `false` if no permits are
|
59
|
+
# available, `true` when acquired a permit. If a block is given, the
|
60
|
+
# block's return value is returned if the permits were acquired; if not,
|
61
|
+
# `nil` is returned.
|
62
|
+
|
63
|
+
# @!macro semaphore_method_release
|
64
|
+
#
|
65
|
+
# Releases the given number of permits, returning them to the semaphore.
|
66
|
+
#
|
67
|
+
# @param [Fixnum] permits Number of permits to return to the semaphore.
|
68
|
+
#
|
69
|
+
# @raise [ArgumentError] if `permits` is not a number or is less than one
|
70
|
+
#
|
71
|
+
# @return [nil]
|
72
|
+
|
73
|
+
###################################################################
|
74
|
+
|
75
|
+
# @!macro semaphore_public_api
|
76
|
+
#
|
77
|
+
# @!method initialize(count)
|
78
|
+
# @!macro semaphore_method_initialize
|
79
|
+
#
|
80
|
+
# @!method acquire(permits = 1)
|
81
|
+
# @!macro semaphore_method_acquire
|
82
|
+
#
|
83
|
+
# @!method available_permits
|
84
|
+
# @!macro semaphore_method_available_permits
|
85
|
+
#
|
86
|
+
# @!method drain_permits
|
87
|
+
# @!macro semaphore_method_drain_permits
|
88
|
+
#
|
89
|
+
# @!method try_acquire(permits = 1, timeout = nil)
|
90
|
+
# @!macro semaphore_method_try_acquire
|
91
|
+
#
|
92
|
+
# @!method release(permits = 1)
|
93
|
+
# @!macro semaphore_method_release
|
94
|
+
|
95
|
+
###################################################################
|
96
|
+
|
97
|
+
# @!visibility private
|
98
|
+
# @!macro internal_implementation_note
|
99
|
+
SemaphoreImplementation = case
|
100
|
+
when defined?(JavaSemaphore)
|
101
|
+
JavaSemaphore
|
102
|
+
else
|
103
|
+
MutexSemaphore
|
104
|
+
end
|
105
|
+
private_constant :SemaphoreImplementation
|
106
|
+
|
107
|
+
# @!macro semaphore
|
108
|
+
#
|
109
|
+
# A counting semaphore. Conceptually, a semaphore maintains a set of
|
110
|
+
# permits. Each {#acquire} blocks if necessary until a permit is
|
111
|
+
# available, and then takes it. Each {#release} adds a permit, potentially
|
112
|
+
# releasing a blocking acquirer.
|
113
|
+
# However, no actual permit objects are used; the Semaphore just keeps a
|
114
|
+
# count of the number available and acts accordingly.
|
115
|
+
# Alternatively, permits may be acquired within a block, and automatically
|
116
|
+
# released after the block finishes executing.
|
117
|
+
#
|
118
|
+
# @!macro semaphore_public_api
|
119
|
+
# @example
|
120
|
+
# semaphore = Concurrent::Semaphore.new(2)
|
121
|
+
#
|
122
|
+
# t1 = Thread.new do
|
123
|
+
# semaphore.acquire
|
124
|
+
# puts "Thread 1 acquired semaphore"
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
# t2 = Thread.new do
|
128
|
+
# semaphore.acquire
|
129
|
+
# puts "Thread 2 acquired semaphore"
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# t3 = Thread.new do
|
133
|
+
# semaphore.acquire
|
134
|
+
# puts "Thread 3 acquired semaphore"
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# t4 = Thread.new do
|
138
|
+
# sleep(2)
|
139
|
+
# puts "Thread 4 releasing semaphore"
|
140
|
+
# semaphore.release
|
141
|
+
# end
|
142
|
+
#
|
143
|
+
# [t1, t2, t3, t4].each(&:join)
|
144
|
+
#
|
145
|
+
# # prints:
|
146
|
+
# # Thread 3 acquired semaphore
|
147
|
+
# # Thread 2 acquired semaphore
|
148
|
+
# # Thread 4 releasing semaphore
|
149
|
+
# # Thread 1 acquired semaphore
|
150
|
+
#
|
151
|
+
# @example
|
152
|
+
# semaphore = Concurrent::Semaphore.new(1)
|
153
|
+
#
|
154
|
+
# puts semaphore.available_permits
|
155
|
+
# semaphore.acquire do
|
156
|
+
# puts semaphore.available_permits
|
157
|
+
# end
|
158
|
+
# puts semaphore.available_permits
|
159
|
+
#
|
160
|
+
# # prints:
|
161
|
+
# # 1
|
162
|
+
# # 0
|
163
|
+
# # 1
|
164
|
+
class Semaphore < SemaphoreImplementation
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'concurrent/utility/engine'
|
2
|
+
require 'concurrent/atomic/ruby_thread_local_var'
|
3
|
+
require 'concurrent/atomic/java_thread_local_var'
|
4
|
+
|
5
|
+
module Concurrent
|
6
|
+
|
7
|
+
###################################################################
|
8
|
+
|
9
|
+
# @!macro thread_local_var_method_initialize
|
10
|
+
#
|
11
|
+
# Creates a thread local variable.
|
12
|
+
#
|
13
|
+
# @param [Object] default the default value when otherwise unset
|
14
|
+
# @param [Proc] default_block Optional block that gets called to obtain the
|
15
|
+
# default value for each thread
|
16
|
+
|
17
|
+
# @!macro thread_local_var_method_get
|
18
|
+
#
|
19
|
+
# Returns the value in the current thread's copy of this thread-local variable.
|
20
|
+
#
|
21
|
+
# @return [Object] the current value
|
22
|
+
|
23
|
+
# @!macro thread_local_var_method_set
|
24
|
+
#
|
25
|
+
# Sets the current thread's copy of this thread-local variable to the specified value.
|
26
|
+
#
|
27
|
+
# @param [Object] value the value to set
|
28
|
+
# @return [Object] the new value
|
29
|
+
|
30
|
+
# @!macro thread_local_var_method_bind
|
31
|
+
#
|
32
|
+
# Bind the given value to thread local storage during
|
33
|
+
# execution of the given block.
|
34
|
+
#
|
35
|
+
# @param [Object] value the value to bind
|
36
|
+
# @yield the operation to be performed with the bound variable
|
37
|
+
# @return [Object] the value
|
38
|
+
|
39
|
+
|
40
|
+
###################################################################
|
41
|
+
|
42
|
+
# @!macro thread_local_var_public_api
|
43
|
+
#
|
44
|
+
# @!method initialize(default = nil, &default_block)
|
45
|
+
# @!macro thread_local_var_method_initialize
|
46
|
+
#
|
47
|
+
# @!method value
|
48
|
+
# @!macro thread_local_var_method_get
|
49
|
+
#
|
50
|
+
# @!method value=(value)
|
51
|
+
# @!macro thread_local_var_method_set
|
52
|
+
#
|
53
|
+
# @!method bind(value, &block)
|
54
|
+
# @!macro thread_local_var_method_bind
|
55
|
+
|
56
|
+
###################################################################
|
57
|
+
|
58
|
+
# @!visibility private
|
59
|
+
# @!macro internal_implementation_note
|
60
|
+
ThreadLocalVarImplementation = case
|
61
|
+
when Concurrent.on_jruby?
|
62
|
+
JavaThreadLocalVar
|
63
|
+
else
|
64
|
+
RubyThreadLocalVar
|
65
|
+
end
|
66
|
+
private_constant :ThreadLocalVarImplementation
|
67
|
+
|
68
|
+
# @!macro thread_local_var
|
69
|
+
#
|
70
|
+
# A `ThreadLocalVar` is a variable where the value is different for each thread.
|
71
|
+
# Each variable may have a default value, but when you modify the variable only
|
72
|
+
# the current thread will ever see that change.
|
73
|
+
#
|
74
|
+
# @!macro thread_safe_variable_comparison
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# v = ThreadLocalVar.new(14)
|
78
|
+
# v.value #=> 14
|
79
|
+
# v.value = 2
|
80
|
+
# v.value #=> 2
|
81
|
+
#
|
82
|
+
# @example
|
83
|
+
# v = ThreadLocalVar.new(14)
|
84
|
+
#
|
85
|
+
# t1 = Thread.new do
|
86
|
+
# v.value #=> 14
|
87
|
+
# v.value = 1
|
88
|
+
# v.value #=> 1
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# t2 = Thread.new do
|
92
|
+
# v.value #=> 14
|
93
|
+
# v.value = 2
|
94
|
+
# v.value #=> 2
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# v.value #=> 14
|
98
|
+
#
|
99
|
+
# @see https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html Java ThreadLocal
|
100
|
+
#
|
101
|
+
# @!macro thread_local_var_public_api
|
102
|
+
class ThreadLocalVar < ThreadLocalVarImplementation
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
# @!macro internal_implementation_note
|
5
|
+
class MutexAtomicReference < Synchronization::LockableObject
|
6
|
+
include AtomicDirectUpdate
|
7
|
+
include AtomicNumericCompareAndSetWrapper
|
8
|
+
alias_method :compare_and_swap, :compare_and_set
|
9
|
+
|
10
|
+
# @!macro atomic_reference_method_initialize
|
11
|
+
def initialize(value = nil)
|
12
|
+
super()
|
13
|
+
synchronize { ns_initialize(value) }
|
14
|
+
end
|
15
|
+
|
16
|
+
# @!macro atomic_reference_method_get
|
17
|
+
def get
|
18
|
+
synchronize { @value }
|
19
|
+
end
|
20
|
+
alias_method :value, :get
|
21
|
+
|
22
|
+
# @!macro atomic_reference_method_set
|
23
|
+
def set(new_value)
|
24
|
+
synchronize { @value = new_value }
|
25
|
+
end
|
26
|
+
alias_method :value=, :set
|
27
|
+
|
28
|
+
# @!macro atomic_reference_method_get_and_set
|
29
|
+
def get_and_set(new_value)
|
30
|
+
synchronize do
|
31
|
+
old_value = @value
|
32
|
+
@value = new_value
|
33
|
+
old_value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
alias_method :swap, :get_and_set
|
37
|
+
|
38
|
+
# @!macro atomic_reference_method_compare_and_set
|
39
|
+
def _compare_and_set(old_value, new_value)
|
40
|
+
synchronize do
|
41
|
+
if @value.equal? old_value
|
42
|
+
@value = new_value
|
43
|
+
true
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def ns_initialize(value)
|
53
|
+
@value = value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# Special "compare and set" handling of numeric values.
|
4
|
+
#
|
5
|
+
# @!visibility private
|
6
|
+
# @!macro internal_implementation_note
|
7
|
+
module AtomicNumericCompareAndSetWrapper
|
8
|
+
|
9
|
+
# @!macro atomic_reference_method_compare_and_set
|
10
|
+
def compare_and_set(old_value, new_value)
|
11
|
+
if old_value.kind_of? Numeric
|
12
|
+
while true
|
13
|
+
old = get
|
14
|
+
|
15
|
+
return false unless old.kind_of? Numeric
|
16
|
+
|
17
|
+
return false unless old == old_value
|
18
|
+
|
19
|
+
result = _compare_and_set(old, new_value)
|
20
|
+
return result if result
|
21
|
+
end
|
22
|
+
else
|
23
|
+
_compare_and_set(old_value, new_value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'concurrent/atomic/atomic_reference'
|
2
|
+
require 'concurrent/atomic/atomic_boolean'
|
3
|
+
require 'concurrent/atomic/atomic_fixnum'
|
4
|
+
require 'concurrent/atomic/cyclic_barrier'
|
5
|
+
require 'concurrent/atomic/count_down_latch'
|
6
|
+
require 'concurrent/atomic/event'
|
7
|
+
require 'concurrent/atomic/read_write_lock'
|
8
|
+
require 'concurrent/atomic/reentrant_read_write_lock'
|
9
|
+
require 'concurrent/atomic/semaphore'
|
10
|
+
require 'concurrent/atomic/thread_local_var'
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'concurrent/synchronization'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
module Collection
|
5
|
+
|
6
|
+
# A thread safe observer set implemented using copy-on-read approach:
|
7
|
+
# observers are added and removed from a thread safe collection; every time
|
8
|
+
# a notification is required the internal data structure is copied to
|
9
|
+
# prevent concurrency issues
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class CopyOnNotifyObserverSet < Synchronization::LockableObject
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
super()
|
16
|
+
synchronize { ns_initialize }
|
17
|
+
end
|
18
|
+
|
19
|
+
# @!macro observable_add_observer
|
20
|
+
def add_observer(observer = nil, func = :update, &block)
|
21
|
+
if observer.nil? && block.nil?
|
22
|
+
raise ArgumentError, 'should pass observer as a first argument or block'
|
23
|
+
elsif observer && block
|
24
|
+
raise ArgumentError.new('cannot provide both an observer and a block')
|
25
|
+
end
|
26
|
+
|
27
|
+
if block
|
28
|
+
observer = block
|
29
|
+
func = :call
|
30
|
+
end
|
31
|
+
|
32
|
+
synchronize do
|
33
|
+
@observers[observer] = func
|
34
|
+
observer
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @!macro observable_delete_observer
|
39
|
+
def delete_observer(observer)
|
40
|
+
synchronize do
|
41
|
+
@observers.delete(observer)
|
42
|
+
observer
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @!macro observable_delete_observers
|
47
|
+
def delete_observers
|
48
|
+
synchronize do
|
49
|
+
@observers.clear
|
50
|
+
self
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @!macro observable_count_observers
|
55
|
+
def count_observers
|
56
|
+
synchronize { @observers.count }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Notifies all registered observers with optional args
|
60
|
+
# @param [Object] args arguments to be passed to each observer
|
61
|
+
# @return [CopyOnWriteObserverSet] self
|
62
|
+
def notify_observers(*args, &block)
|
63
|
+
observers = duplicate_observers
|
64
|
+
notify_to(observers, *args, &block)
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Notifies all registered observers with optional args and deletes them.
|
69
|
+
#
|
70
|
+
# @param [Object] args arguments to be passed to each observer
|
71
|
+
# @return [CopyOnWriteObserverSet] self
|
72
|
+
def notify_and_delete_observers(*args, &block)
|
73
|
+
observers = duplicate_and_clear_observers
|
74
|
+
notify_to(observers, *args, &block)
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
def ns_initialize
|
81
|
+
@observers = {}
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def duplicate_and_clear_observers
|
87
|
+
synchronize do
|
88
|
+
observers = @observers.dup
|
89
|
+
@observers.clear
|
90
|
+
observers
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def duplicate_observers
|
95
|
+
synchronize { @observers.dup }
|
96
|
+
end
|
97
|
+
|
98
|
+
def notify_to(observers, *args)
|
99
|
+
raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
|
100
|
+
observers.each do |observer, function|
|
101
|
+
args = yield if block_given?
|
102
|
+
observer.send(function, *args)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'concurrent/synchronization'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
module Collection
|
5
|
+
|
6
|
+
# A thread safe observer set implemented using copy-on-write approach:
|
7
|
+
# every time an observer is added or removed the whole internal data structure is
|
8
|
+
# duplicated and replaced with a new one.
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
class CopyOnWriteObserverSet < Synchronization::LockableObject
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
super()
|
15
|
+
synchronize { ns_initialize }
|
16
|
+
end
|
17
|
+
|
18
|
+
# @!macro observable_add_observer
|
19
|
+
def add_observer(observer = nil, func = :update, &block)
|
20
|
+
if observer.nil? && block.nil?
|
21
|
+
raise ArgumentError, 'should pass observer as a first argument or block'
|
22
|
+
elsif observer && block
|
23
|
+
raise ArgumentError.new('cannot provide both an observer and a block')
|
24
|
+
end
|
25
|
+
|
26
|
+
if block
|
27
|
+
observer = block
|
28
|
+
func = :call
|
29
|
+
end
|
30
|
+
|
31
|
+
synchronize do
|
32
|
+
new_observers = @observers.dup
|
33
|
+
new_observers[observer] = func
|
34
|
+
@observers = new_observers
|
35
|
+
observer
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# @!macro observable_delete_observer
|
40
|
+
def delete_observer(observer)
|
41
|
+
synchronize do
|
42
|
+
new_observers = @observers.dup
|
43
|
+
new_observers.delete(observer)
|
44
|
+
@observers = new_observers
|
45
|
+
observer
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @!macro observable_delete_observers
|
50
|
+
def delete_observers
|
51
|
+
self.observers = {}
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
# @!macro observable_count_observers
|
56
|
+
def count_observers
|
57
|
+
observers.count
|
58
|
+
end
|
59
|
+
|
60
|
+
# Notifies all registered observers with optional args
|
61
|
+
# @param [Object] args arguments to be passed to each observer
|
62
|
+
# @return [CopyOnWriteObserverSet] self
|
63
|
+
def notify_observers(*args, &block)
|
64
|
+
notify_to(observers, *args, &block)
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Notifies all registered observers with optional args and deletes them.
|
69
|
+
#
|
70
|
+
# @param [Object] args arguments to be passed to each observer
|
71
|
+
# @return [CopyOnWriteObserverSet] self
|
72
|
+
def notify_and_delete_observers(*args, &block)
|
73
|
+
old = clear_observers_and_return_old
|
74
|
+
notify_to(old, *args, &block)
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
def ns_initialize
|
81
|
+
@observers = {}
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def notify_to(observers, *args)
|
87
|
+
raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
|
88
|
+
observers.each do |observer, function|
|
89
|
+
args = yield if block_given?
|
90
|
+
observer.send(function, *args)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def observers
|
95
|
+
synchronize { @observers }
|
96
|
+
end
|
97
|
+
|
98
|
+
def observers=(new_set)
|
99
|
+
synchronize { @observers = new_set }
|
100
|
+
end
|
101
|
+
|
102
|
+
def clear_observers_and_return_old
|
103
|
+
synchronize do
|
104
|
+
old_observers = @observers
|
105
|
+
@observers = {}
|
106
|
+
old_observers
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
if Concurrent.on_jruby?
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
module Collection
|
5
|
+
|
6
|
+
|
7
|
+
# @!macro priority_queue
|
8
|
+
#
|
9
|
+
# @!visibility private
|
10
|
+
# @!macro internal_implementation_note
|
11
|
+
class JavaNonConcurrentPriorityQueue
|
12
|
+
|
13
|
+
# @!macro priority_queue_method_initialize
|
14
|
+
def initialize(opts = {})
|
15
|
+
order = opts.fetch(:order, :max)
|
16
|
+
if [:min, :low].include?(order)
|
17
|
+
@queue = java.util.PriorityQueue.new(11) # 11 is the default initial capacity
|
18
|
+
else
|
19
|
+
@queue = java.util.PriorityQueue.new(11, java.util.Collections.reverseOrder())
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# @!macro priority_queue_method_clear
|
24
|
+
def clear
|
25
|
+
@queue.clear
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
# @!macro priority_queue_method_delete
|
30
|
+
def delete(item)
|
31
|
+
found = false
|
32
|
+
while @queue.remove(item) do
|
33
|
+
found = true
|
34
|
+
end
|
35
|
+
found
|
36
|
+
end
|
37
|
+
|
38
|
+
# @!macro priority_queue_method_empty
|
39
|
+
def empty?
|
40
|
+
@queue.size == 0
|
41
|
+
end
|
42
|
+
|
43
|
+
# @!macro priority_queue_method_include
|
44
|
+
def include?(item)
|
45
|
+
@queue.contains(item)
|
46
|
+
end
|
47
|
+
alias_method :has_priority?, :include?
|
48
|
+
|
49
|
+
# @!macro priority_queue_method_length
|
50
|
+
def length
|
51
|
+
@queue.size
|
52
|
+
end
|
53
|
+
alias_method :size, :length
|
54
|
+
|
55
|
+
# @!macro priority_queue_method_peek
|
56
|
+
def peek
|
57
|
+
@queue.peek
|
58
|
+
end
|
59
|
+
|
60
|
+
# @!macro priority_queue_method_pop
|
61
|
+
def pop
|
62
|
+
@queue.poll
|
63
|
+
end
|
64
|
+
alias_method :deq, :pop
|
65
|
+
alias_method :shift, :pop
|
66
|
+
|
67
|
+
# @!macro priority_queue_method_push
|
68
|
+
def push(item)
|
69
|
+
raise ArgumentError.new('cannot enqueue nil') if item.nil?
|
70
|
+
@queue.add(item)
|
71
|
+
end
|
72
|
+
alias_method :<<, :push
|
73
|
+
alias_method :enq, :push
|
74
|
+
|
75
|
+
# @!macro priority_queue_method_from_list
|
76
|
+
def self.from_list(list, opts = {})
|
77
|
+
queue = new(opts)
|
78
|
+
list.each{|item| queue << item }
|
79
|
+
queue
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|