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.
Files changed (143) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +478 -0
  3. data/Gemfile +41 -0
  4. data/LICENSE.md +23 -0
  5. data/README.md +381 -0
  6. data/Rakefile +327 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/concurrent-ruby.rb +1 -0
  23. data/lib/concurrent.rb +134 -0
  24. data/lib/concurrent/agent.rb +587 -0
  25. data/lib/concurrent/array.rb +66 -0
  26. data/lib/concurrent/async.rb +459 -0
  27. data/lib/concurrent/atom.rb +222 -0
  28. data/lib/concurrent/atomic/abstract_thread_local_var.rb +66 -0
  29. data/lib/concurrent/atomic/atomic_boolean.rb +126 -0
  30. data/lib/concurrent/atomic/atomic_fixnum.rb +143 -0
  31. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  32. data/lib/concurrent/atomic/atomic_reference.rb +204 -0
  33. data/lib/concurrent/atomic/count_down_latch.rb +100 -0
  34. data/lib/concurrent/atomic/cyclic_barrier.rb +128 -0
  35. data/lib/concurrent/atomic/event.rb +109 -0
  36. data/lib/concurrent/atomic/java_count_down_latch.rb +42 -0
  37. data/lib/concurrent/atomic/java_thread_local_var.rb +37 -0
  38. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
  39. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
  40. data/lib/concurrent/atomic/mutex_count_down_latch.rb +44 -0
  41. data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
  42. data/lib/concurrent/atomic/read_write_lock.rb +254 -0
  43. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +379 -0
  44. data/lib/concurrent/atomic/ruby_thread_local_var.rb +161 -0
  45. data/lib/concurrent/atomic/semaphore.rb +145 -0
  46. data/lib/concurrent/atomic/thread_local_var.rb +104 -0
  47. data/lib/concurrent/atomic_reference/mutex_atomic.rb +56 -0
  48. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
  49. data/lib/concurrent/atomics.rb +10 -0
  50. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
  51. data/lib/concurrent/collection/copy_on_write_observer_set.rb +111 -0
  52. data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  53. data/lib/concurrent/collection/lock_free_stack.rb +158 -0
  54. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  55. data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
  56. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
  57. data/lib/concurrent/collection/map/synchronized_map_backend.rb +82 -0
  58. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  59. data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
  60. data/lib/concurrent/concern/deprecation.rb +34 -0
  61. data/lib/concurrent/concern/dereferenceable.rb +73 -0
  62. data/lib/concurrent/concern/logging.rb +32 -0
  63. data/lib/concurrent/concern/obligation.rb +220 -0
  64. data/lib/concurrent/concern/observable.rb +110 -0
  65. data/lib/concurrent/concurrent_ruby.jar +0 -0
  66. data/lib/concurrent/configuration.rb +184 -0
  67. data/lib/concurrent/constants.rb +8 -0
  68. data/lib/concurrent/dataflow.rb +81 -0
  69. data/lib/concurrent/delay.rb +199 -0
  70. data/lib/concurrent/errors.rb +69 -0
  71. data/lib/concurrent/exchanger.rb +352 -0
  72. data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
  73. data/lib/concurrent/executor/cached_thread_pool.rb +62 -0
  74. data/lib/concurrent/executor/executor_service.rb +185 -0
  75. data/lib/concurrent/executor/fixed_thread_pool.rb +206 -0
  76. data/lib/concurrent/executor/immediate_executor.rb +66 -0
  77. data/lib/concurrent/executor/indirect_immediate_executor.rb +44 -0
  78. data/lib/concurrent/executor/java_executor_service.rb +91 -0
  79. data/lib/concurrent/executor/java_single_thread_executor.rb +29 -0
  80. data/lib/concurrent/executor/java_thread_pool_executor.rb +123 -0
  81. data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
  82. data/lib/concurrent/executor/ruby_single_thread_executor.rb +22 -0
  83. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +362 -0
  84. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  85. data/lib/concurrent/executor/serial_executor_service.rb +34 -0
  86. data/lib/concurrent/executor/serialized_execution.rb +107 -0
  87. data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
  88. data/lib/concurrent/executor/simple_executor_service.rb +100 -0
  89. data/lib/concurrent/executor/single_thread_executor.rb +56 -0
  90. data/lib/concurrent/executor/thread_pool_executor.rb +87 -0
  91. data/lib/concurrent/executor/timer_set.rb +173 -0
  92. data/lib/concurrent/executors.rb +20 -0
  93. data/lib/concurrent/future.rb +141 -0
  94. data/lib/concurrent/hash.rb +59 -0
  95. data/lib/concurrent/immutable_struct.rb +93 -0
  96. data/lib/concurrent/ivar.rb +207 -0
  97. data/lib/concurrent/map.rb +337 -0
  98. data/lib/concurrent/maybe.rb +229 -0
  99. data/lib/concurrent/mutable_struct.rb +229 -0
  100. data/lib/concurrent/mvar.rb +242 -0
  101. data/lib/concurrent/options.rb +42 -0
  102. data/lib/concurrent/promise.rb +579 -0
  103. data/lib/concurrent/promises.rb +2167 -0
  104. data/lib/concurrent/re_include.rb +58 -0
  105. data/lib/concurrent/scheduled_task.rb +318 -0
  106. data/lib/concurrent/set.rb +66 -0
  107. data/lib/concurrent/settable_struct.rb +129 -0
  108. data/lib/concurrent/synchronization.rb +30 -0
  109. data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  110. data/lib/concurrent/synchronization/abstract_object.rb +24 -0
  111. data/lib/concurrent/synchronization/abstract_struct.rb +160 -0
  112. data/lib/concurrent/synchronization/condition.rb +60 -0
  113. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  114. data/lib/concurrent/synchronization/jruby_object.rb +45 -0
  115. data/lib/concurrent/synchronization/lock.rb +36 -0
  116. data/lib/concurrent/synchronization/lockable_object.rb +74 -0
  117. data/lib/concurrent/synchronization/mri_object.rb +44 -0
  118. data/lib/concurrent/synchronization/mutex_lockable_object.rb +76 -0
  119. data/lib/concurrent/synchronization/object.rb +183 -0
  120. data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
  121. data/lib/concurrent/synchronization/rbx_object.rb +49 -0
  122. data/lib/concurrent/synchronization/truffleruby_object.rb +47 -0
  123. data/lib/concurrent/synchronization/volatile.rb +36 -0
  124. data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  125. data/lib/concurrent/thread_safe/util.rb +16 -0
  126. data/lib/concurrent/thread_safe/util/adder.rb +74 -0
  127. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  128. data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
  129. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  130. data/lib/concurrent/thread_safe/util/striped64.rb +246 -0
  131. data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
  132. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  133. data/lib/concurrent/timer_task.rb +334 -0
  134. data/lib/concurrent/tuple.rb +86 -0
  135. data/lib/concurrent/tvar.rb +258 -0
  136. data/lib/concurrent/utility/at_exit.rb +97 -0
  137. data/lib/concurrent/utility/engine.rb +56 -0
  138. data/lib/concurrent/utility/monotonic_time.rb +58 -0
  139. data/lib/concurrent/utility/native_extension_loader.rb +79 -0
  140. data/lib/concurrent/utility/native_integer.rb +53 -0
  141. data/lib/concurrent/utility/processor_counter.rb +158 -0
  142. data/lib/concurrent/version.rb +3 -0
  143. 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