Skip to content

Commit 242a9b7

Browse files
committed
MVar: #modify.
1 parent f2e29bf commit 242a9b7

File tree

2 files changed

+73
-2
lines changed

2 files changed

+73
-2
lines changed

lib/concurrent/mvar.rb

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ def take(timeout = nil)
2929
TIMEOUT
3030
end
3131
end
32-
33-
3432
end
3533

3634
def put(value, timeout = nil)
@@ -49,6 +47,25 @@ def put(value, timeout = nil)
4947
end
5048
end
5149

50+
def modify(timeout = nil)
51+
raise ArgumentError.new('no block given') unless block_given?
52+
53+
@mutex.synchronize do
54+
# If the value isn't empty, wait for full to be signalled
55+
@full_condition.wait(@mutex, timeout) if empty?
56+
57+
# If we timed out we'll still be empty
58+
if full?
59+
value = @value
60+
@value = yield value
61+
@full_condition.signal
62+
value
63+
else
64+
TIMEOUT
65+
end
66+
end
67+
end
68+
5269
def empty?
5370
@value == EMPTY
5471
end

spec/concurrent/mvar_spec.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,60 @@ module Concurrent
124124

125125
end
126126

127+
context '#modify' do
128+
129+
it 'raises an exception when no block given' do
130+
m = MVar.new(14)
131+
expect { m.modify }.to raise_error(ArgumentError)
132+
end
133+
134+
it 'modifies a full MVar' do
135+
m = MVar.new(14)
136+
m.modify{ |v| v + 2 }
137+
m.take.should eq 16
138+
end
139+
140+
it 'returns the unmodified value' do
141+
m = MVar.new(14)
142+
m.modify{ |v| v + 2 }.should eq 14
143+
end
144+
145+
it 'waits for another thread to #put' do
146+
m = MVar.new
147+
148+
putter = Thread.new {
149+
sleep(0.5)
150+
m.put 14
151+
}
152+
153+
m.modify{ |v| v + 2 }.should eq 14
154+
end
155+
156+
it 'is atomic' do
157+
m = MVar.new(0)
158+
159+
# #modify conceptually does #take and #put - but it should be atomic.
160+
# Check that another #put can't sneak it during the #modify.
161+
162+
modifier = Thread.new {
163+
m.modify do |v|
164+
sleep(1)
165+
1
166+
end
167+
}
168+
169+
sleep(0.5)
170+
m.put(2, 1).should eq MVar::TIMEOUT
171+
m.take.should eq 1
172+
end
173+
174+
it 'returns TIMEOUT on timeout on an empty MVar' do
175+
m = MVar.new
176+
m.modify(0.5){ |v| v + 2 }.should eq MVar::TIMEOUT
177+
end
178+
179+
end
180+
127181
end
128182

129183
end

0 commit comments

Comments
 (0)