44
55module Concurrent
66
7+ # An `MVar` is a single-element container that blocks on `get` if it is empty,
8+ # and blocks on `put` if it is full. It is safe to use an `MVar` from
9+ # multiple threads. `MVar` can be seen as a single-element blocking queue, or
10+ # a rendezvous variable.
11+ #
12+ # An `MVar` is typically used to transfer objects between threads, where the
13+ # sending thread will block if the previous message hasn't been taken yet by the
14+ # receiving thread. It can also be used to control access to some global shared
15+ # state, where threads `take` the value, perform some operation, and then
16+ # `put` it back.
717 class MVar
818
919 include Dereferenceable
1020
21+ # Unique value that represents that an `MVar` was empty
1122 EMPTY = Object . new
23+
24+ # Unique value that represents that an `MVar` timed out before it was able
25+ # to produce a value.
1226 TIMEOUT = Object . new
1327
28+ # Create a new `MVar`, either empty or with an initial value.
29+ #
30+ # @param [Hash] opts the options controlling how the future will be processed
31+ # @option opts [Boolean] :operation (false) when `true` will execute the future on the global
32+ # operation pool (for long-running operations), when `false` will execute the future on the
33+ # global task pool (for short-running tasks)
34+ # @option opts [object] :executor when provided will run all operations on
35+ # this executor rather than the global thread pool (overrides :operation)
36+ # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
37+ # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
38+ # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
39+ # returning the value returned from the proc
1440 def initialize ( value = EMPTY , opts = { } )
1541 @value = value
1642 @mutex = Mutex . new
@@ -19,6 +45,10 @@ def initialize(value = EMPTY, opts = {})
1945 set_deref_options ( opts )
2046 end
2147
48+ # Remove the value from an `MVar`, leaving it empty, and blocking if there
49+ # isn't a value. A timeout can be set to limit the time spent blocked, in
50+ # which case it returns `TIMEOUT` if the time is exceeded.
51+ # @return [Object] the value that was taken, or `TIMEOUT`
2252 def take ( timeout = nil )
2353 @mutex . synchronize do
2454 wait_for_full ( timeout )
@@ -35,6 +65,10 @@ def take(timeout = nil)
3565 end
3666 end
3767
68+ # Put a value into an `MVar`, blocking if there is already a value until
69+ # it is empty. A timeout can be set to limit the time spent blocked, in
70+ # which case it returns `TIMEOUT` if the time is exceeded.
71+ # @return [Object] the value that was put, or `TIMEOUT`
3872 def put ( value , timeout = nil )
3973 @mutex . synchronize do
4074 wait_for_empty ( timeout )
@@ -50,6 +84,11 @@ def put(value, timeout = nil)
5084 end
5185 end
5286
87+ # Atomically `take`, yield the value to a block for transformation, and then
88+ # `put` the transformed value. Returns the transformed value. A timeout can
89+ # be set to limit the time spent blocked, in which case it returns `TIMEOUT`
90+ # if the time is exceeded.
91+ # @return [Object] the transformed value, or `TIMEOUT`
5392 def modify ( timeout = nil )
5493 raise ArgumentError . new ( 'no block given' ) unless block_given?
5594
@@ -68,6 +107,7 @@ def modify(timeout = nil)
68107 end
69108 end
70109
110+ # Non-blocking version of `take`, that returns `EMPTY` instead of blocking.
71111 def try_take!
72112 @mutex . synchronize do
73113 if unlocked_full?
@@ -81,6 +121,7 @@ def try_take!
81121 end
82122 end
83123
124+ # Non-blocking version of `put`, that returns whether or not it was successful.
84125 def try_put! ( value )
85126 @mutex . synchronize do
86127 if unlocked_empty?
@@ -93,6 +134,7 @@ def try_put!(value)
93134 end
94135 end
95136
137+ # Non-blocking version of `put` that will overwrite an existing value.
96138 def set! ( value )
97139 @mutex . synchronize do
98140 old_value = @value
@@ -102,6 +144,7 @@ def set!(value)
102144 end
103145 end
104146
147+ # Non-blocking version of `modify` that will yield with `EMPTY` if there is no value yet.
105148 def modify!
106149 raise ArgumentError . new ( 'no block given' ) unless block_given?
107150
@@ -117,10 +160,12 @@ def modify!
117160 end
118161 end
119162
163+ # Returns if the `MVar` is currently empty.
120164 def empty?
121165 @mutex . synchronize { @value == EMPTY }
122166 end
123167
168+ # Returns if the `MVar` currently contains a value.
124169 def full?
125170 not empty?
126171 end
0 commit comments