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,66 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'concurrent/collection/map/non_concurrent_map_backend'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
module Collection
|
8
|
+
|
9
|
+
# @!visibility private
|
10
|
+
class MriMapBackend < NonConcurrentMapBackend
|
11
|
+
|
12
|
+
def initialize(options = nil)
|
13
|
+
super(options)
|
14
|
+
@write_lock = Mutex.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(key, value)
|
18
|
+
@write_lock.synchronize { super }
|
19
|
+
end
|
20
|
+
|
21
|
+
def compute_if_absent(key)
|
22
|
+
if NULL != (stored_value = @backend.fetch(key, NULL)) # fast non-blocking path for the most likely case
|
23
|
+
stored_value
|
24
|
+
else
|
25
|
+
@write_lock.synchronize { super }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def compute_if_present(key)
|
30
|
+
@write_lock.synchronize { super }
|
31
|
+
end
|
32
|
+
|
33
|
+
def compute(key)
|
34
|
+
@write_lock.synchronize { super }
|
35
|
+
end
|
36
|
+
|
37
|
+
def merge_pair(key, value)
|
38
|
+
@write_lock.synchronize { super }
|
39
|
+
end
|
40
|
+
|
41
|
+
def replace_pair(key, old_value, new_value)
|
42
|
+
@write_lock.synchronize { super }
|
43
|
+
end
|
44
|
+
|
45
|
+
def replace_if_exists(key, new_value)
|
46
|
+
@write_lock.synchronize { super }
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_and_set(key, value)
|
50
|
+
@write_lock.synchronize { super }
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete(key)
|
54
|
+
@write_lock.synchronize { super }
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete_pair(key, value)
|
58
|
+
@write_lock.synchronize { super }
|
59
|
+
end
|
60
|
+
|
61
|
+
def clear
|
62
|
+
@write_lock.synchronize { super }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'concurrent/constants'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
# @!visibility private
|
6
|
+
module Collection
|
7
|
+
|
8
|
+
# @!visibility private
|
9
|
+
class NonConcurrentMapBackend
|
10
|
+
|
11
|
+
# WARNING: all public methods of the class must operate on the @backend
|
12
|
+
# directly without calling each other. This is important because of the
|
13
|
+
# SynchronizedMapBackend which uses a non-reentrant mutex for performance
|
14
|
+
# reasons.
|
15
|
+
def initialize(options = nil)
|
16
|
+
@backend = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](key)
|
20
|
+
@backend[key]
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(key, value)
|
24
|
+
@backend[key] = value
|
25
|
+
end
|
26
|
+
|
27
|
+
def compute_if_absent(key)
|
28
|
+
if NULL != (stored_value = @backend.fetch(key, NULL))
|
29
|
+
stored_value
|
30
|
+
else
|
31
|
+
@backend[key] = yield
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def replace_pair(key, old_value, new_value)
|
36
|
+
if pair?(key, old_value)
|
37
|
+
@backend[key] = new_value
|
38
|
+
true
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def replace_if_exists(key, new_value)
|
45
|
+
if NULL != (stored_value = @backend.fetch(key, NULL))
|
46
|
+
@backend[key] = new_value
|
47
|
+
stored_value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def compute_if_present(key)
|
52
|
+
if NULL != (stored_value = @backend.fetch(key, NULL))
|
53
|
+
store_computed_value(key, yield(stored_value))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def compute(key)
|
58
|
+
store_computed_value(key, yield(@backend[key]))
|
59
|
+
end
|
60
|
+
|
61
|
+
def merge_pair(key, value)
|
62
|
+
if NULL == (stored_value = @backend.fetch(key, NULL))
|
63
|
+
@backend[key] = value
|
64
|
+
else
|
65
|
+
store_computed_value(key, yield(stored_value))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_and_set(key, value)
|
70
|
+
stored_value = @backend[key]
|
71
|
+
@backend[key] = value
|
72
|
+
stored_value
|
73
|
+
end
|
74
|
+
|
75
|
+
def key?(key)
|
76
|
+
@backend.key?(key)
|
77
|
+
end
|
78
|
+
|
79
|
+
def delete(key)
|
80
|
+
@backend.delete(key)
|
81
|
+
end
|
82
|
+
|
83
|
+
def delete_pair(key, value)
|
84
|
+
if pair?(key, value)
|
85
|
+
@backend.delete(key)
|
86
|
+
true
|
87
|
+
else
|
88
|
+
false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def clear
|
93
|
+
@backend.clear
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
def each_pair
|
98
|
+
dupped_backend.each_pair do |k, v|
|
99
|
+
yield k, v
|
100
|
+
end
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
104
|
+
def size
|
105
|
+
@backend.size
|
106
|
+
end
|
107
|
+
|
108
|
+
def get_or_default(key, default_value)
|
109
|
+
@backend.fetch(key, default_value)
|
110
|
+
end
|
111
|
+
|
112
|
+
alias_method :_get, :[]
|
113
|
+
alias_method :_set, :[]=
|
114
|
+
private :_get, :_set
|
115
|
+
private
|
116
|
+
def initialize_copy(other)
|
117
|
+
super
|
118
|
+
@backend = {}
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
def dupped_backend
|
123
|
+
@backend.dup
|
124
|
+
end
|
125
|
+
|
126
|
+
def pair?(key, expected_value)
|
127
|
+
NULL != (stored_value = @backend.fetch(key, NULL)) && expected_value.equal?(stored_value)
|
128
|
+
end
|
129
|
+
|
130
|
+
def store_computed_value(key, new_value)
|
131
|
+
if new_value.nil?
|
132
|
+
@backend.delete(key)
|
133
|
+
nil
|
134
|
+
else
|
135
|
+
@backend[key] = new_value
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'concurrent/collection/map/non_concurrent_map_backend'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
# @!visibility private
|
6
|
+
module Collection
|
7
|
+
|
8
|
+
# @!visibility private
|
9
|
+
class SynchronizedMapBackend < NonConcurrentMapBackend
|
10
|
+
|
11
|
+
require 'mutex_m'
|
12
|
+
include Mutex_m
|
13
|
+
# WARNING: Mutex_m is a non-reentrant lock, so the synchronized methods are
|
14
|
+
# not allowed to call each other.
|
15
|
+
|
16
|
+
def [](key)
|
17
|
+
synchronize { super }
|
18
|
+
end
|
19
|
+
|
20
|
+
def []=(key, value)
|
21
|
+
synchronize { super }
|
22
|
+
end
|
23
|
+
|
24
|
+
def compute_if_absent(key)
|
25
|
+
synchronize { super }
|
26
|
+
end
|
27
|
+
|
28
|
+
def compute_if_present(key)
|
29
|
+
synchronize { super }
|
30
|
+
end
|
31
|
+
|
32
|
+
def compute(key)
|
33
|
+
synchronize { super }
|
34
|
+
end
|
35
|
+
|
36
|
+
def merge_pair(key, value)
|
37
|
+
synchronize { super }
|
38
|
+
end
|
39
|
+
|
40
|
+
def replace_pair(key, old_value, new_value)
|
41
|
+
synchronize { super }
|
42
|
+
end
|
43
|
+
|
44
|
+
def replace_if_exists(key, new_value)
|
45
|
+
synchronize { super }
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_and_set(key, value)
|
49
|
+
synchronize { super }
|
50
|
+
end
|
51
|
+
|
52
|
+
def key?(key)
|
53
|
+
synchronize { super }
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete(key)
|
57
|
+
synchronize { super }
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete_pair(key, value)
|
61
|
+
synchronize { super }
|
62
|
+
end
|
63
|
+
|
64
|
+
def clear
|
65
|
+
synchronize { super }
|
66
|
+
end
|
67
|
+
|
68
|
+
def size
|
69
|
+
synchronize { super }
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_or_default(key, default_value)
|
73
|
+
synchronize { super }
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def dupped_backend
|
78
|
+
synchronize { super }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
module Collection
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
class TruffleRubyMapBackend < TruffleRuby::ConcurrentMap
|
8
|
+
def initialize(options = nil)
|
9
|
+
options ||= {}
|
10
|
+
super(initial_capacity: options[:initial_capacity], load_factor: options[:load_factor])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'concurrent/utility/engine'
|
2
|
+
require 'concurrent/collection/java_non_concurrent_priority_queue'
|
3
|
+
require 'concurrent/collection/ruby_non_concurrent_priority_queue'
|
4
|
+
|
5
|
+
module Concurrent
|
6
|
+
module Collection
|
7
|
+
|
8
|
+
# @!visibility private
|
9
|
+
# @!macro internal_implementation_note
|
10
|
+
NonConcurrentPriorityQueueImplementation = case
|
11
|
+
when Concurrent.on_jruby?
|
12
|
+
JavaNonConcurrentPriorityQueue
|
13
|
+
else
|
14
|
+
RubyNonConcurrentPriorityQueue
|
15
|
+
end
|
16
|
+
private_constant :NonConcurrentPriorityQueueImplementation
|
17
|
+
|
18
|
+
# @!macro priority_queue
|
19
|
+
#
|
20
|
+
# A queue collection in which the elements are sorted based on their
|
21
|
+
# comparison (spaceship) operator `<=>`. Items are added to the queue
|
22
|
+
# at a position relative to their priority. On removal the element
|
23
|
+
# with the "highest" priority is removed. By default the sort order is
|
24
|
+
# from highest to lowest, but a lowest-to-highest sort order can be
|
25
|
+
# set on construction.
|
26
|
+
#
|
27
|
+
# The API is based on the `Queue` class from the Ruby standard library.
|
28
|
+
#
|
29
|
+
# The pure Ruby implementation, `RubyNonConcurrentPriorityQueue` uses a heap algorithm
|
30
|
+
# stored in an array. The algorithm is based on the work of Robert Sedgewick
|
31
|
+
# and Kevin Wayne.
|
32
|
+
#
|
33
|
+
# The JRuby native implementation is a thin wrapper around the standard
|
34
|
+
# library `java.util.NonConcurrentPriorityQueue`.
|
35
|
+
#
|
36
|
+
# When running under JRuby the class `NonConcurrentPriorityQueue` extends `JavaNonConcurrentPriorityQueue`.
|
37
|
+
# When running under all other interpreters it extends `RubyNonConcurrentPriorityQueue`.
|
38
|
+
#
|
39
|
+
# @note This implementation is *not* thread safe.
|
40
|
+
#
|
41
|
+
# @see http://en.wikipedia.org/wiki/Priority_queue
|
42
|
+
# @see http://ruby-doc.org/stdlib-2.0.0/libdoc/thread/rdoc/Queue.html
|
43
|
+
#
|
44
|
+
# @see http://algs4.cs.princeton.edu/24pq/index.php#2.6
|
45
|
+
# @see http://algs4.cs.princeton.edu/24pq/MaxPQ.java.html
|
46
|
+
#
|
47
|
+
# @see http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html
|
48
|
+
#
|
49
|
+
# @!visibility private
|
50
|
+
class NonConcurrentPriorityQueue < NonConcurrentPriorityQueueImplementation
|
51
|
+
|
52
|
+
alias_method :has_priority?, :include?
|
53
|
+
|
54
|
+
alias_method :size, :length
|
55
|
+
|
56
|
+
alias_method :deq, :pop
|
57
|
+
alias_method :shift, :pop
|
58
|
+
|
59
|
+
alias_method :<<, :push
|
60
|
+
alias_method :enq, :push
|
61
|
+
|
62
|
+
# @!method initialize(opts = {})
|
63
|
+
# @!macro priority_queue_method_initialize
|
64
|
+
#
|
65
|
+
# Create a new priority queue with no items.
|
66
|
+
#
|
67
|
+
# @param [Hash] opts the options for creating the queue
|
68
|
+
# @option opts [Symbol] :order (:max) dictates the order in which items are
|
69
|
+
# stored: from highest to lowest when `:max` or `:high`; from lowest to
|
70
|
+
# highest when `:min` or `:low`
|
71
|
+
|
72
|
+
# @!method clear
|
73
|
+
# @!macro priority_queue_method_clear
|
74
|
+
#
|
75
|
+
# Removes all of the elements from this priority queue.
|
76
|
+
|
77
|
+
# @!method delete(item)
|
78
|
+
# @!macro priority_queue_method_delete
|
79
|
+
#
|
80
|
+
# Deletes all items from `self` that are equal to `item`.
|
81
|
+
#
|
82
|
+
# @param [Object] item the item to be removed from the queue
|
83
|
+
# @return [Object] true if the item is found else false
|
84
|
+
|
85
|
+
# @!method empty?
|
86
|
+
# @!macro priority_queue_method_empty
|
87
|
+
#
|
88
|
+
# Returns `true` if `self` contains no elements.
|
89
|
+
#
|
90
|
+
# @return [Boolean] true if there are no items in the queue else false
|
91
|
+
|
92
|
+
# @!method include?(item)
|
93
|
+
# @!macro priority_queue_method_include
|
94
|
+
#
|
95
|
+
# Returns `true` if the given item is present in `self` (that is, if any
|
96
|
+
# element == `item`), otherwise returns false.
|
97
|
+
#
|
98
|
+
# @param [Object] item the item to search for
|
99
|
+
#
|
100
|
+
# @return [Boolean] true if the item is found else false
|
101
|
+
|
102
|
+
# @!method length
|
103
|
+
# @!macro priority_queue_method_length
|
104
|
+
#
|
105
|
+
# The current length of the queue.
|
106
|
+
#
|
107
|
+
# @return [Fixnum] the number of items in the queue
|
108
|
+
|
109
|
+
# @!method peek
|
110
|
+
# @!macro priority_queue_method_peek
|
111
|
+
#
|
112
|
+
# Retrieves, but does not remove, the head of this queue, or returns `nil`
|
113
|
+
# if this queue is empty.
|
114
|
+
#
|
115
|
+
# @return [Object] the head of the queue or `nil` when empty
|
116
|
+
|
117
|
+
# @!method pop
|
118
|
+
# @!macro priority_queue_method_pop
|
119
|
+
#
|
120
|
+
# Retrieves and removes the head of this queue, or returns `nil` if this
|
121
|
+
# queue is empty.
|
122
|
+
#
|
123
|
+
# @return [Object] the head of the queue or `nil` when empty
|
124
|
+
|
125
|
+
# @!method push(item)
|
126
|
+
# @!macro priority_queue_method_push
|
127
|
+
#
|
128
|
+
# Inserts the specified element into this priority queue.
|
129
|
+
#
|
130
|
+
# @param [Object] item the item to insert onto the queue
|
131
|
+
|
132
|
+
# @!method self.from_list(list, opts = {})
|
133
|
+
# @!macro priority_queue_method_from_list
|
134
|
+
#
|
135
|
+
# Create a new priority queue from the given list.
|
136
|
+
#
|
137
|
+
# @param [Enumerable] list the list to build the queue from
|
138
|
+
# @param [Hash] opts the options for creating the queue
|
139
|
+
#
|
140
|
+
# @return [NonConcurrentPriorityQueue] the newly created and populated queue
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Collection
|
3
|
+
|
4
|
+
# @!macro priority_queue
|
5
|
+
#
|
6
|
+
# @!visibility private
|
7
|
+
# @!macro internal_implementation_note
|
8
|
+
class RubyNonConcurrentPriorityQueue
|
9
|
+
|
10
|
+
# @!macro priority_queue_method_initialize
|
11
|
+
def initialize(opts = {})
|
12
|
+
order = opts.fetch(:order, :max)
|
13
|
+
@comparator = [:min, :low].include?(order) ? -1 : 1
|
14
|
+
clear
|
15
|
+
end
|
16
|
+
|
17
|
+
# @!macro priority_queue_method_clear
|
18
|
+
def clear
|
19
|
+
@queue = [nil]
|
20
|
+
@length = 0
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
# @!macro priority_queue_method_delete
|
25
|
+
def delete(item)
|
26
|
+
return false if empty?
|
27
|
+
original_length = @length
|
28
|
+
k = 1
|
29
|
+
while k <= @length
|
30
|
+
if @queue[k] == item
|
31
|
+
swap(k, @length)
|
32
|
+
@length -= 1
|
33
|
+
sink(k) || swim(k)
|
34
|
+
@queue.pop
|
35
|
+
else
|
36
|
+
k += 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
@length != original_length
|
40
|
+
end
|
41
|
+
|
42
|
+
# @!macro priority_queue_method_empty
|
43
|
+
def empty?
|
44
|
+
size == 0
|
45
|
+
end
|
46
|
+
|
47
|
+
# @!macro priority_queue_method_include
|
48
|
+
def include?(item)
|
49
|
+
@queue.include?(item)
|
50
|
+
end
|
51
|
+
alias_method :has_priority?, :include?
|
52
|
+
|
53
|
+
# @!macro priority_queue_method_length
|
54
|
+
def length
|
55
|
+
@length
|
56
|
+
end
|
57
|
+
alias_method :size, :length
|
58
|
+
|
59
|
+
# @!macro priority_queue_method_peek
|
60
|
+
def peek
|
61
|
+
empty? ? nil : @queue[1]
|
62
|
+
end
|
63
|
+
|
64
|
+
# @!macro priority_queue_method_pop
|
65
|
+
def pop
|
66
|
+
return nil if empty?
|
67
|
+
max = @queue[1]
|
68
|
+
swap(1, @length)
|
69
|
+
@length -= 1
|
70
|
+
sink(1)
|
71
|
+
@queue.pop
|
72
|
+
max
|
73
|
+
end
|
74
|
+
alias_method :deq, :pop
|
75
|
+
alias_method :shift, :pop
|
76
|
+
|
77
|
+
# @!macro priority_queue_method_push
|
78
|
+
def push(item)
|
79
|
+
raise ArgumentError.new('cannot enqueue nil') if item.nil?
|
80
|
+
@length += 1
|
81
|
+
@queue << item
|
82
|
+
swim(@length)
|
83
|
+
true
|
84
|
+
end
|
85
|
+
alias_method :<<, :push
|
86
|
+
alias_method :enq, :push
|
87
|
+
|
88
|
+
# @!macro priority_queue_method_from_list
|
89
|
+
def self.from_list(list, opts = {})
|
90
|
+
queue = new(opts)
|
91
|
+
list.each{|item| queue << item }
|
92
|
+
queue
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Exchange the values at the given indexes within the internal array.
|
98
|
+
#
|
99
|
+
# @param [Integer] x the first index to swap
|
100
|
+
# @param [Integer] y the second index to swap
|
101
|
+
#
|
102
|
+
# @!visibility private
|
103
|
+
def swap(x, y)
|
104
|
+
temp = @queue[x]
|
105
|
+
@queue[x] = @queue[y]
|
106
|
+
@queue[y] = temp
|
107
|
+
end
|
108
|
+
|
109
|
+
# Are the items at the given indexes ordered based on the priority
|
110
|
+
# order specified at construction?
|
111
|
+
#
|
112
|
+
# @param [Integer] x the first index from which to retrieve a comparable value
|
113
|
+
# @param [Integer] y the second index from which to retrieve a comparable value
|
114
|
+
#
|
115
|
+
# @return [Boolean] true if the two elements are in the correct priority order
|
116
|
+
# else false
|
117
|
+
#
|
118
|
+
# @!visibility private
|
119
|
+
def ordered?(x, y)
|
120
|
+
(@queue[x] <=> @queue[y]) == @comparator
|
121
|
+
end
|
122
|
+
|
123
|
+
# Percolate down to maintain heap invariant.
|
124
|
+
#
|
125
|
+
# @param [Integer] k the index at which to start the percolation
|
126
|
+
#
|
127
|
+
# @!visibility private
|
128
|
+
def sink(k)
|
129
|
+
success = false
|
130
|
+
|
131
|
+
while (j = (2 * k)) <= @length do
|
132
|
+
j += 1 if j < @length && ! ordered?(j, j+1)
|
133
|
+
break if ordered?(k, j)
|
134
|
+
swap(k, j)
|
135
|
+
success = true
|
136
|
+
k = j
|
137
|
+
end
|
138
|
+
|
139
|
+
success
|
140
|
+
end
|
141
|
+
|
142
|
+
# Percolate up to maintain heap invariant.
|
143
|
+
#
|
144
|
+
# @param [Integer] k the index at which to start the percolation
|
145
|
+
#
|
146
|
+
# @!visibility private
|
147
|
+
def swim(k)
|
148
|
+
success = false
|
149
|
+
|
150
|
+
while k > 1 && ! ordered?(k/2, k) do
|
151
|
+
swap(k, k/2)
|
152
|
+
k = k/2
|
153
|
+
success = true
|
154
|
+
end
|
155
|
+
|
156
|
+
success
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'concurrent/concern/logging'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
module Concern
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
# @!macro internal_implementation_note
|
8
|
+
module Deprecation
|
9
|
+
# TODO require additional parameter: a version. Display when it'll be removed based on that. Error if not removed.
|
10
|
+
include Concern::Logging
|
11
|
+
|
12
|
+
def deprecated(message, strip = 2)
|
13
|
+
caller_line = caller(strip).first if strip > 0
|
14
|
+
klass = if Module === self
|
15
|
+
self
|
16
|
+
else
|
17
|
+
self.class
|
18
|
+
end
|
19
|
+
message = if strip > 0
|
20
|
+
format("[DEPRECATED] %s\ncalled on: %s", message, caller_line)
|
21
|
+
else
|
22
|
+
format('[DEPRECATED] %s', message)
|
23
|
+
end
|
24
|
+
log WARN, klass.to_s, message
|
25
|
+
end
|
26
|
+
|
27
|
+
def deprecated_method(old_name, new_name)
|
28
|
+
deprecated "`#{old_name}` is deprecated and it'll removed in next release, use `#{new_name}` instead", 3
|
29
|
+
end
|
30
|
+
|
31
|
+
extend self
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|