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,229 @@
|
|
1
|
+
require 'concurrent/synchronization/abstract_struct'
|
2
|
+
require 'concurrent/synchronization'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# An thread-safe variation of Ruby's standard `Struct`. Values can be set at
|
7
|
+
# construction or safely changed at any time during the object's lifecycle.
|
8
|
+
#
|
9
|
+
# @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
|
10
|
+
module MutableStruct
|
11
|
+
include Synchronization::AbstractStruct
|
12
|
+
|
13
|
+
# @!macro struct_new
|
14
|
+
#
|
15
|
+
# Factory for creating new struct classes.
|
16
|
+
#
|
17
|
+
# ```
|
18
|
+
# new([class_name] [, member_name]+>) -> StructClass click to toggle source
|
19
|
+
# new([class_name] [, member_name]+>) {|StructClass| block } -> StructClass
|
20
|
+
# new(value, ...) -> obj
|
21
|
+
# StructClass[value, ...] -> obj
|
22
|
+
# ```
|
23
|
+
#
|
24
|
+
# The first two forms are used to create a new struct subclass `class_name`
|
25
|
+
# that can contain a value for each member_name . This subclass can be
|
26
|
+
# used to create instances of the structure like any other Class .
|
27
|
+
#
|
28
|
+
# If the `class_name` is omitted an anonymous struct class will be created.
|
29
|
+
# Otherwise, the name of this struct will appear as a constant in the struct class,
|
30
|
+
# so it must be unique for all structs under this base class and must start with a
|
31
|
+
# capital letter. Assigning a struct class to a constant also gives the class
|
32
|
+
# the name of the constant.
|
33
|
+
#
|
34
|
+
# If a block is given it will be evaluated in the context of `StructClass`, passing
|
35
|
+
# the created class as a parameter. This is the recommended way to customize a struct.
|
36
|
+
# Subclassing an anonymous struct creates an extra anonymous class that will never be used.
|
37
|
+
#
|
38
|
+
# The last two forms create a new instance of a struct subclass. The number of value
|
39
|
+
# parameters must be less than or equal to the number of attributes defined for the
|
40
|
+
# struct. Unset parameters default to nil. Passing more parameters than number of attributes
|
41
|
+
# will raise an `ArgumentError`.
|
42
|
+
#
|
43
|
+
# @see http://ruby-doc.org/core-2.2.0/Struct.html#method-c-new Ruby standard library `Struct#new`
|
44
|
+
|
45
|
+
# @!macro struct_values
|
46
|
+
#
|
47
|
+
# Returns the values for this struct as an Array.
|
48
|
+
#
|
49
|
+
# @return [Array] the values for this struct
|
50
|
+
#
|
51
|
+
def values
|
52
|
+
synchronize { ns_values }
|
53
|
+
end
|
54
|
+
alias_method :to_a, :values
|
55
|
+
|
56
|
+
# @!macro struct_values_at
|
57
|
+
#
|
58
|
+
# Returns the struct member values for each selector as an Array.
|
59
|
+
#
|
60
|
+
# A selector may be either an Integer offset or a Range of offsets (as in `Array#values_at`).
|
61
|
+
#
|
62
|
+
# @param [Fixnum, Range] indexes the index(es) from which to obatin the values (in order)
|
63
|
+
def values_at(*indexes)
|
64
|
+
synchronize { ns_values_at(indexes) }
|
65
|
+
end
|
66
|
+
|
67
|
+
# @!macro struct_inspect
|
68
|
+
#
|
69
|
+
# Describe the contents of this struct in a string.
|
70
|
+
#
|
71
|
+
# @return [String] the contents of this struct in a string
|
72
|
+
def inspect
|
73
|
+
synchronize { ns_inspect }
|
74
|
+
end
|
75
|
+
alias_method :to_s, :inspect
|
76
|
+
|
77
|
+
# @!macro struct_merge
|
78
|
+
#
|
79
|
+
# Returns a new struct containing the contents of `other` and the contents
|
80
|
+
# of `self`. If no block is specified, the value for entries with duplicate
|
81
|
+
# keys will be that of `other`. Otherwise the value for each duplicate key
|
82
|
+
# is determined by calling the block with the key, its value in `self` and
|
83
|
+
# its value in `other`.
|
84
|
+
#
|
85
|
+
# @param [Hash] other the hash from which to set the new values
|
86
|
+
# @yield an options block for resolving duplicate keys
|
87
|
+
# @yieldparam [String, Symbol] member the name of the member which is duplicated
|
88
|
+
# @yieldparam [Object] selfvalue the value of the member in `self`
|
89
|
+
# @yieldparam [Object] othervalue the value of the member in `other`
|
90
|
+
#
|
91
|
+
# @return [Synchronization::AbstractStruct] a new struct with the new values
|
92
|
+
#
|
93
|
+
# @raise [ArgumentError] of given a member that is not defined in the struct
|
94
|
+
def merge(other, &block)
|
95
|
+
synchronize { ns_merge(other, &block) }
|
96
|
+
end
|
97
|
+
|
98
|
+
# @!macro struct_to_h
|
99
|
+
#
|
100
|
+
# Returns a hash containing the names and values for the struct’s members.
|
101
|
+
#
|
102
|
+
# @return [Hash] the names and values for the struct’s members
|
103
|
+
def to_h
|
104
|
+
synchronize { ns_to_h }
|
105
|
+
end
|
106
|
+
|
107
|
+
# @!macro struct_get
|
108
|
+
#
|
109
|
+
# Attribute Reference
|
110
|
+
#
|
111
|
+
# @param [Symbol, String, Integer] member the string or symbol name of the member
|
112
|
+
# for which to obtain the value or the member's index
|
113
|
+
#
|
114
|
+
# @return [Object] the value of the given struct member or the member at the given index.
|
115
|
+
#
|
116
|
+
# @raise [NameError] if the member does not exist
|
117
|
+
# @raise [IndexError] if the index is out of range.
|
118
|
+
def [](member)
|
119
|
+
synchronize { ns_get(member) }
|
120
|
+
end
|
121
|
+
|
122
|
+
# @!macro struct_equality
|
123
|
+
#
|
124
|
+
# Equality
|
125
|
+
#
|
126
|
+
# @return [Boolean] true if other has the same struct subclass and has
|
127
|
+
# equal member values (according to `Object#==`)
|
128
|
+
def ==(other)
|
129
|
+
synchronize { ns_equality(other) }
|
130
|
+
end
|
131
|
+
|
132
|
+
# @!macro struct_each
|
133
|
+
#
|
134
|
+
# Yields the value of each struct member in order. If no block is given
|
135
|
+
# an enumerator is returned.
|
136
|
+
#
|
137
|
+
# @yield the operation to be performed on each struct member
|
138
|
+
# @yieldparam [Object] value each struct value (in order)
|
139
|
+
def each(&block)
|
140
|
+
return enum_for(:each) unless block_given?
|
141
|
+
synchronize { ns_each(&block) }
|
142
|
+
end
|
143
|
+
|
144
|
+
# @!macro struct_each_pair
|
145
|
+
#
|
146
|
+
# Yields the name and value of each struct member in order. If no block is
|
147
|
+
# given an enumerator is returned.
|
148
|
+
#
|
149
|
+
# @yield the operation to be performed on each struct member/value pair
|
150
|
+
# @yieldparam [Object] member each struct member (in order)
|
151
|
+
# @yieldparam [Object] value each struct value (in order)
|
152
|
+
def each_pair(&block)
|
153
|
+
return enum_for(:each_pair) unless block_given?
|
154
|
+
synchronize { ns_each_pair(&block) }
|
155
|
+
end
|
156
|
+
|
157
|
+
# @!macro struct_select
|
158
|
+
#
|
159
|
+
# Yields each member value from the struct to the block and returns an Array
|
160
|
+
# containing the member values from the struct for which the given block
|
161
|
+
# returns a true value (equivalent to `Enumerable#select`).
|
162
|
+
#
|
163
|
+
# @yield the operation to be performed on each struct member
|
164
|
+
# @yieldparam [Object] value each struct value (in order)
|
165
|
+
#
|
166
|
+
# @return [Array] an array containing each value for which the block returns true
|
167
|
+
def select(&block)
|
168
|
+
return enum_for(:select) unless block_given?
|
169
|
+
synchronize { ns_select(&block) }
|
170
|
+
end
|
171
|
+
|
172
|
+
# @!macro struct_set
|
173
|
+
#
|
174
|
+
# Attribute Assignment
|
175
|
+
#
|
176
|
+
# Sets the value of the given struct member or the member at the given index.
|
177
|
+
#
|
178
|
+
# @param [Symbol, String, Integer] member the string or symbol name of the member
|
179
|
+
# for which to obtain the value or the member's index
|
180
|
+
#
|
181
|
+
# @return [Object] the value of the given struct member or the member at the given index.
|
182
|
+
#
|
183
|
+
# @raise [NameError] if the name does not exist
|
184
|
+
# @raise [IndexError] if the index is out of range.
|
185
|
+
def []=(member, value)
|
186
|
+
if member.is_a? Integer
|
187
|
+
length = synchronize { @values.length }
|
188
|
+
if member >= length
|
189
|
+
raise IndexError.new("offset #{member} too large for struct(size:#{length})")
|
190
|
+
end
|
191
|
+
synchronize { @values[member] = value }
|
192
|
+
else
|
193
|
+
send("#{member}=", value)
|
194
|
+
end
|
195
|
+
rescue NoMethodError
|
196
|
+
raise NameError.new("no member '#{member}' in struct")
|
197
|
+
end
|
198
|
+
|
199
|
+
# @!macro struct_new
|
200
|
+
def self.new(*args, &block)
|
201
|
+
clazz_name = nil
|
202
|
+
if args.length == 0
|
203
|
+
raise ArgumentError.new('wrong number of arguments (0 for 1+)')
|
204
|
+
elsif args.length > 0 && args.first.is_a?(String)
|
205
|
+
clazz_name = args.shift
|
206
|
+
end
|
207
|
+
FACTORY.define_struct(clazz_name, args, &block)
|
208
|
+
end
|
209
|
+
|
210
|
+
FACTORY = Class.new(Synchronization::LockableObject) do
|
211
|
+
def define_struct(name, members, &block)
|
212
|
+
synchronize do
|
213
|
+
clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::LockableObject, name, members, &block)
|
214
|
+
members.each_with_index do |member, index|
|
215
|
+
clazz.send :remove_method, member
|
216
|
+
clazz.send(:define_method, member) do
|
217
|
+
synchronize { @values[index] }
|
218
|
+
end
|
219
|
+
clazz.send(:define_method, "#{member}=") do |value|
|
220
|
+
synchronize { @values[index] = value }
|
221
|
+
end
|
222
|
+
end
|
223
|
+
clazz
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end.new
|
227
|
+
private_constant :FACTORY
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
require 'concurrent/concern/dereferenceable'
|
2
|
+
require 'concurrent/synchronization'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# An `MVar` is a synchronized single element container. They are empty or
|
7
|
+
# contain one item. Taking a value from an empty `MVar` blocks, as does
|
8
|
+
# putting a value into a full one. You can either think of them as blocking
|
9
|
+
# queue of length one, or a special kind of mutable variable.
|
10
|
+
#
|
11
|
+
# On top of the fundamental `#put` and `#take` operations, we also provide a
|
12
|
+
# `#mutate` that is atomic with respect to operations on the same instance.
|
13
|
+
# These operations all support timeouts.
|
14
|
+
#
|
15
|
+
# We also support non-blocking operations `#try_put!` and `#try_take!`, a
|
16
|
+
# `#set!` that ignores existing values, a `#value` that returns the value
|
17
|
+
# without removing it or returns `MVar::EMPTY`, and a `#modify!` that yields
|
18
|
+
# `MVar::EMPTY` if the `MVar` is empty and can be used to set `MVar::EMPTY`.
|
19
|
+
# You shouldn't use these operations in the first instance.
|
20
|
+
#
|
21
|
+
# `MVar` is a [Dereferenceable](Dereferenceable).
|
22
|
+
#
|
23
|
+
# `MVar` is related to M-structures in Id, `MVar` in Haskell and `SyncVar` in Scala.
|
24
|
+
#
|
25
|
+
# Note that unlike the original Haskell paper, our `#take` is blocking. This is how
|
26
|
+
# Haskell and Scala do it today.
|
27
|
+
#
|
28
|
+
# @!macro copy_options
|
29
|
+
#
|
30
|
+
# ## See Also
|
31
|
+
#
|
32
|
+
# 1. P. Barth, R. Nikhil, and Arvind. [M-Structures: Extending a parallel, non- strict, functional language with state](http://dl.acm.org/citation.cfm?id=652538). In Proceedings of the 5th
|
33
|
+
# ACM Conference on Functional Programming Languages and Computer Architecture (FPCA), 1991.
|
34
|
+
#
|
35
|
+
# 2. S. Peyton Jones, A. Gordon, and S. Finne. [Concurrent Haskell](http://dl.acm.org/citation.cfm?id=237794).
|
36
|
+
# In Proceedings of the 23rd Symposium on Principles of Programming Languages
|
37
|
+
# (PoPL), 1996.
|
38
|
+
class MVar < Synchronization::Object
|
39
|
+
include Concern::Dereferenceable
|
40
|
+
safe_initialization!
|
41
|
+
|
42
|
+
# Unique value that represents that an `MVar` was empty
|
43
|
+
EMPTY = ::Object.new
|
44
|
+
|
45
|
+
# Unique value that represents that an `MVar` timed out before it was able
|
46
|
+
# to produce a value.
|
47
|
+
TIMEOUT = ::Object.new
|
48
|
+
|
49
|
+
# Create a new `MVar`, either empty or with an initial value.
|
50
|
+
#
|
51
|
+
# @param [Hash] opts the options controlling how the future will be processed
|
52
|
+
#
|
53
|
+
# @!macro deref_options
|
54
|
+
def initialize(value = EMPTY, opts = {})
|
55
|
+
@value = value
|
56
|
+
@mutex = Mutex.new
|
57
|
+
@empty_condition = ConditionVariable.new
|
58
|
+
@full_condition = ConditionVariable.new
|
59
|
+
set_deref_options(opts)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Remove the value from an `MVar`, leaving it empty, and blocking if there
|
63
|
+
# isn't a value. A timeout can be set to limit the time spent blocked, in
|
64
|
+
# which case it returns `TIMEOUT` if the time is exceeded.
|
65
|
+
# @return [Object] the value that was taken, or `TIMEOUT`
|
66
|
+
def take(timeout = nil)
|
67
|
+
@mutex.synchronize do
|
68
|
+
wait_for_full(timeout)
|
69
|
+
|
70
|
+
# If we timed out we'll still be empty
|
71
|
+
if unlocked_full?
|
72
|
+
value = @value
|
73
|
+
@value = EMPTY
|
74
|
+
@empty_condition.signal
|
75
|
+
apply_deref_options(value)
|
76
|
+
else
|
77
|
+
TIMEOUT
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# acquires lock on the from an `MVAR`, yields the value to provided block,
|
83
|
+
# and release lock. A timeout can be set to limit the time spent blocked,
|
84
|
+
# in which case it returns `TIMEOUT` if the time is exceeded.
|
85
|
+
# @return [Object] the value returned by the block, or `TIMEOUT`
|
86
|
+
def borrow(timeout = nil)
|
87
|
+
@mutex.synchronize do
|
88
|
+
wait_for_full(timeout)
|
89
|
+
|
90
|
+
# if we timeoud out we'll still be empty
|
91
|
+
if unlocked_full?
|
92
|
+
yield @value
|
93
|
+
else
|
94
|
+
TIMEOUT
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Put a value into an `MVar`, blocking if there is already a value until
|
100
|
+
# it is empty. A timeout can be set to limit the time spent blocked, in
|
101
|
+
# which case it returns `TIMEOUT` if the time is exceeded.
|
102
|
+
# @return [Object] the value that was put, or `TIMEOUT`
|
103
|
+
def put(value, timeout = nil)
|
104
|
+
@mutex.synchronize do
|
105
|
+
wait_for_empty(timeout)
|
106
|
+
|
107
|
+
# If we timed out we won't be empty
|
108
|
+
if unlocked_empty?
|
109
|
+
@value = value
|
110
|
+
@full_condition.signal
|
111
|
+
apply_deref_options(value)
|
112
|
+
else
|
113
|
+
TIMEOUT
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Atomically `take`, yield the value to a block for transformation, and then
|
119
|
+
# `put` the transformed value. Returns the transformed value. A timeout can
|
120
|
+
# be set to limit the time spent blocked, in which case it returns `TIMEOUT`
|
121
|
+
# if the time is exceeded.
|
122
|
+
# @return [Object] the transformed value, or `TIMEOUT`
|
123
|
+
def modify(timeout = nil)
|
124
|
+
raise ArgumentError.new('no block given') unless block_given?
|
125
|
+
|
126
|
+
@mutex.synchronize do
|
127
|
+
wait_for_full(timeout)
|
128
|
+
|
129
|
+
# If we timed out we'll still be empty
|
130
|
+
if unlocked_full?
|
131
|
+
value = @value
|
132
|
+
@value = yield value
|
133
|
+
@full_condition.signal
|
134
|
+
apply_deref_options(value)
|
135
|
+
else
|
136
|
+
TIMEOUT
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Non-blocking version of `take`, that returns `EMPTY` instead of blocking.
|
142
|
+
def try_take!
|
143
|
+
@mutex.synchronize do
|
144
|
+
if unlocked_full?
|
145
|
+
value = @value
|
146
|
+
@value = EMPTY
|
147
|
+
@empty_condition.signal
|
148
|
+
apply_deref_options(value)
|
149
|
+
else
|
150
|
+
EMPTY
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Non-blocking version of `put`, that returns whether or not it was successful.
|
156
|
+
def try_put!(value)
|
157
|
+
@mutex.synchronize do
|
158
|
+
if unlocked_empty?
|
159
|
+
@value = value
|
160
|
+
@full_condition.signal
|
161
|
+
true
|
162
|
+
else
|
163
|
+
false
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Non-blocking version of `put` that will overwrite an existing value.
|
169
|
+
def set!(value)
|
170
|
+
@mutex.synchronize do
|
171
|
+
old_value = @value
|
172
|
+
@value = value
|
173
|
+
@full_condition.signal
|
174
|
+
apply_deref_options(old_value)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Non-blocking version of `modify` that will yield with `EMPTY` if there is no value yet.
|
179
|
+
def modify!
|
180
|
+
raise ArgumentError.new('no block given') unless block_given?
|
181
|
+
|
182
|
+
@mutex.synchronize do
|
183
|
+
value = @value
|
184
|
+
@value = yield value
|
185
|
+
if unlocked_empty?
|
186
|
+
@empty_condition.signal
|
187
|
+
else
|
188
|
+
@full_condition.signal
|
189
|
+
end
|
190
|
+
apply_deref_options(value)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Returns if the `MVar` is currently empty.
|
195
|
+
def empty?
|
196
|
+
@mutex.synchronize { @value == EMPTY }
|
197
|
+
end
|
198
|
+
|
199
|
+
# Returns if the `MVar` currently contains a value.
|
200
|
+
def full?
|
201
|
+
!empty?
|
202
|
+
end
|
203
|
+
|
204
|
+
protected
|
205
|
+
|
206
|
+
def synchronize(&block)
|
207
|
+
@mutex.synchronize(&block)
|
208
|
+
end
|
209
|
+
|
210
|
+
private
|
211
|
+
|
212
|
+
def unlocked_empty?
|
213
|
+
@value == EMPTY
|
214
|
+
end
|
215
|
+
|
216
|
+
def unlocked_full?
|
217
|
+
! unlocked_empty?
|
218
|
+
end
|
219
|
+
|
220
|
+
def wait_for_full(timeout)
|
221
|
+
wait_while(@full_condition, timeout) { unlocked_empty? }
|
222
|
+
end
|
223
|
+
|
224
|
+
def wait_for_empty(timeout)
|
225
|
+
wait_while(@empty_condition, timeout) { unlocked_full? }
|
226
|
+
end
|
227
|
+
|
228
|
+
def wait_while(condition, timeout)
|
229
|
+
if timeout.nil?
|
230
|
+
while yield
|
231
|
+
condition.wait(@mutex)
|
232
|
+
end
|
233
|
+
else
|
234
|
+
stop = Concurrent.monotonic_time + timeout
|
235
|
+
while yield && timeout > 0.0
|
236
|
+
condition.wait(@mutex, timeout)
|
237
|
+
timeout = stop - Concurrent.monotonic_time
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|