concurrent-ruby 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
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