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,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
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# @!macro warn.edge
|
4
|
+
class LockFreeStack < Synchronization::Object
|
5
|
+
|
6
|
+
safe_initialization!
|
7
|
+
|
8
|
+
class Node
|
9
|
+
# TODO (pitr-ch 20-Dec-2016): Could be unified with Stack class?
|
10
|
+
|
11
|
+
# @return [Node]
|
12
|
+
attr_reader :next_node
|
13
|
+
|
14
|
+
# @return [Object]
|
15
|
+
attr_reader :value
|
16
|
+
|
17
|
+
# @!visibility private
|
18
|
+
# allow to nil-ify to free GC when the entry is no longer relevant, not synchronised
|
19
|
+
attr_writer :value
|
20
|
+
|
21
|
+
def initialize(value, next_node)
|
22
|
+
@value = value
|
23
|
+
@next_node = next_node
|
24
|
+
end
|
25
|
+
|
26
|
+
singleton_class.send :alias_method, :[], :new
|
27
|
+
end
|
28
|
+
|
29
|
+
# The singleton for empty node
|
30
|
+
EMPTY = Node[nil, nil]
|
31
|
+
def EMPTY.next_node
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_atomic(:head)
|
36
|
+
private :head, :head=, :swap_head, :compare_and_set_head, :update_head
|
37
|
+
|
38
|
+
# @!visibility private
|
39
|
+
def self.of1(value)
|
40
|
+
new Node[value, EMPTY]
|
41
|
+
end
|
42
|
+
|
43
|
+
# @!visibility private
|
44
|
+
def self.of2(value1, value2)
|
45
|
+
new Node[value1, Node[value2, EMPTY]]
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param [Node] head
|
49
|
+
def initialize(head = EMPTY)
|
50
|
+
super()
|
51
|
+
self.head = head
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param [Node] head
|
55
|
+
# @return [true, false]
|
56
|
+
def empty?(head = head())
|
57
|
+
head.equal? EMPTY
|
58
|
+
end
|
59
|
+
|
60
|
+
# @param [Node] head
|
61
|
+
# @param [Object] value
|
62
|
+
# @return [true, false]
|
63
|
+
def compare_and_push(head, value)
|
64
|
+
compare_and_set_head head, Node[value, head]
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param [Object] value
|
68
|
+
# @return [self]
|
69
|
+
def push(value)
|
70
|
+
while true
|
71
|
+
current_head = head
|
72
|
+
return self if compare_and_set_head current_head, Node[value, current_head]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Node]
|
77
|
+
def peek
|
78
|
+
head
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param [Node] head
|
82
|
+
# @return [true, false]
|
83
|
+
def compare_and_pop(head)
|
84
|
+
compare_and_set_head head, head.next_node
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [Object]
|
88
|
+
def pop
|
89
|
+
while true
|
90
|
+
current_head = head
|
91
|
+
return current_head.value if compare_and_set_head current_head, current_head.next_node
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# @param [Node] head
|
96
|
+
# @return [true, false]
|
97
|
+
def compare_and_clear(head)
|
98
|
+
compare_and_set_head head, EMPTY
|
99
|
+
end
|
100
|
+
|
101
|
+
include Enumerable
|
102
|
+
|
103
|
+
# @param [Node] head
|
104
|
+
# @return [self]
|
105
|
+
def each(head = nil)
|
106
|
+
return to_enum(:each, head) unless block_given?
|
107
|
+
it = head || peek
|
108
|
+
until it.equal?(EMPTY)
|
109
|
+
yield it.value
|
110
|
+
it = it.next_node
|
111
|
+
end
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return [true, false]
|
116
|
+
def clear
|
117
|
+
while true
|
118
|
+
current_head = head
|
119
|
+
return false if current_head == EMPTY
|
120
|
+
return true if compare_and_set_head current_head, EMPTY
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# @param [Node] head
|
125
|
+
# @return [true, false]
|
126
|
+
def clear_if(head)
|
127
|
+
compare_and_set_head head, EMPTY
|
128
|
+
end
|
129
|
+
|
130
|
+
# @param [Node] head
|
131
|
+
# @param [Node] new_head
|
132
|
+
# @return [true, false]
|
133
|
+
def replace_if(head, new_head)
|
134
|
+
compare_and_set_head head, new_head
|
135
|
+
end
|
136
|
+
|
137
|
+
# @return [self]
|
138
|
+
# @yield over the cleared stack
|
139
|
+
# @yieldparam [Object] value
|
140
|
+
def clear_each(&block)
|
141
|
+
while true
|
142
|
+
current_head = head
|
143
|
+
return self if current_head == EMPTY
|
144
|
+
if compare_and_set_head current_head, EMPTY
|
145
|
+
each current_head, &block
|
146
|
+
return self
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# @return [String] Short string representation.
|
152
|
+
def to_s
|
153
|
+
format '%s %s>', super[0..-2], to_a.to_s
|
154
|
+
end
|
155
|
+
|
156
|
+
alias_method :inspect, :to_s
|
157
|
+
end
|
158
|
+
end
|