Skip to content

Commit 90fd2f3

Browse files
committed
Add 4 convenience methods to obligation
wait - waits and returns self no_error! - waits and raises on rejection, otherwise returns self value! - returns value, or raises if rejected exception - allows to call `raise rejected_ivar`
1 parent 9e4b9ec commit 90fd2f3

File tree

2 files changed

+160
-40
lines changed

2 files changed

+160
-40
lines changed

lib/concurrent/obligation.rb

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,26 @@ def incomplete?
4343
end
4444

4545
def value(timeout = nil)
46+
wait timeout
47+
deref
48+
end
49+
50+
def wait(timeout = nil)
4651
event.wait(timeout) if timeout != 0 && incomplete?
47-
super()
52+
self
53+
end
54+
55+
def no_error!(timeout = nil)
56+
wait(timeout).tap { raise self if rejected? }
57+
end
58+
59+
def value!(timeout = nil)
60+
wait(timeout)
61+
if rejected?
62+
raise self
63+
else
64+
deref
65+
end
4866
end
4967

5068
def state
@@ -61,6 +79,14 @@ def reason
6179
mutex.unlock
6280
end
6381

82+
# @example allows Obligation to be risen
83+
# rejected_ivar = Ivar.new.fail
84+
# raise rejected_ivar
85+
def exception(*args)
86+
raise 'obligation is not rejected' unless rejected?
87+
reason.exception(*args)
88+
end
89+
6490
protected
6591

6692
# @!visibility private
@@ -81,13 +107,16 @@ def set_state(success, value, reason) # :nodoc:
81107
@state = :fulfilled
82108
else
83109
@reason = reason
84-
@state = :rejected
110+
@state = :rejected
85111
end
86112
end
87113

88114
# @!visibility private
89115
def state=(value) # :nodoc:
90-
mutex.synchronize { @state = value }
116+
mutex.lock
117+
@state = value
118+
ensure
119+
mutex.unlock
91120
end
92121

93122
# atomic compare and set operation
@@ -100,14 +129,15 @@ def state=(value) # :nodoc:
100129
#
101130
# @!visibility private
102131
def compare_and_set_state(next_state, expected_current) # :nodoc:
103-
mutex.synchronize do
104-
if @state == expected_current
105-
@state = next_state
106-
true
107-
else
108-
false
109-
end
132+
mutex.lock
133+
if @state == expected_current
134+
@state = next_state
135+
true
136+
else
137+
false
110138
end
139+
ensure
140+
mutex.unlock
111141
end
112142

113143
# executes the block within mutex if current state is included in expected_states
@@ -116,15 +146,16 @@ def compare_and_set_state(next_state, expected_current) # :nodoc:
116146
#
117147
# @!visibility private
118148
def if_state(*expected_states) # :nodoc:
149+
mutex.lock
119150
raise ArgumentError.new('no block given') unless block_given?
120151

121-
mutex.synchronize do
122-
if expected_states.include? @state
123-
yield
124-
else
125-
false
126-
end
152+
if expected_states.include? @state
153+
yield
154+
else
155+
false
127156
end
157+
ensure
158+
mutex.unlock
128159
end
129160
end
130161
end

spec/concurrent/obligation_spec.rb

Lines changed: 113 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,28 @@ def initialize
3030
obligation.should be_incomplete
3131
end
3232

33-
describe '#value' do
33+
methods = [:value, :value!, :no_error!]
34+
methods.each do |method|
35+
describe "##{method}" do
3436

35-
it 'should return immediately if timeout is zero' do
36-
obligation.value(0).should be_nil
37-
end
37+
it 'should return immediately if timeout is zero' do
38+
obligation.send(method, 0).should(method == :no_error! ? eq(obligation) : be_nil)
39+
end
3840

39-
it 'should block on the event if timeout is not set' do
40-
obligation.stub(:event).and_return(event)
41-
event.should_receive(:wait).with(nil)
41+
it 'should block on the event if timeout is not set' do
42+
obligation.stub(:event).and_return(event)
43+
event.should_receive(:wait).with(nil)
4244

43-
obligation.value
44-
end
45+
obligation.send method
46+
end
47+
48+
it 'should block on the event if timeout is not zero' do
49+
obligation.stub(:event).and_return(event)
50+
event.should_receive(:wait).with(5)
4551

46-
it 'should block on the event if timeout is not zero' do
47-
obligation.stub(:event).and_return(event)
48-
event.should_receive(:wait).with(5)
52+
obligation.send(method, 5)
53+
end
4954

50-
obligation.value(5)
5155
end
5256
end
5357
end
@@ -98,6 +102,45 @@ def initialize
98102

99103
end
100104

105+
describe '#value!' do
106+
107+
it 'should return immediately if timeout is zero' do
108+
obligation.value!(0).should eq 42
109+
end
110+
111+
it 'should return immediately if timeout is not set' do
112+
event.should_not_receive(:wait)
113+
114+
obligation.value!.should eq 42
115+
end
116+
117+
it 'should return immediately if timeout is not zero' do
118+
event.should_not_receive(:wait)
119+
120+
obligation.value!(5).should eq 42
121+
end
122+
123+
end
124+
125+
describe '#no_error!' do
126+
127+
it 'should return immediately if timeout is zero' do
128+
obligation.no_error!(0).should eq obligation
129+
end
130+
131+
it 'should return immediately if timeout is not set' do
132+
event.should_not_receive(:wait)
133+
134+
obligation.no_error!.should eq obligation
135+
end
136+
137+
it 'should return immediately if timeout is not zero' do
138+
event.should_not_receive(:wait)
139+
140+
obligation.no_error!(5).should eq obligation
141+
end
142+
143+
end
101144

102145
end
103146

@@ -115,26 +158,72 @@ def initialize
115158
it 'should be not incomplete' do
116159
obligation.should_not be_incomplete
117160
end
118-
end
119161

120-
describe '#value' do
121162

122-
it 'should return immediately if timeout is zero' do
123-
event.should_not_receive(:wait)
163+
describe '#value' do
164+
165+
it 'should return immediately if timeout is zero' do
166+
event.should_not_receive(:wait)
167+
168+
obligation.value(0).should be_nil
169+
end
170+
171+
it 'should return immediately if timeout is not set' do
172+
event.should_not_receive(:wait)
173+
174+
obligation.value.should be_nil
175+
end
176+
177+
it 'should return immediately if timeout is not zero' do
178+
event.should_not_receive(:wait)
179+
180+
obligation.value(5).should be_nil
181+
end
124182

125-
obligation.value(0).should be_nil
126183
end
127184

128-
it 'should return immediately if timeout is not set' do
129-
event.should_not_receive(:wait)
185+
describe '#value!' do
186+
187+
it 'should return immediately if timeout is zero' do
188+
event.should_not_receive(:wait)
189+
190+
-> { obligation.value!(0) }.should raise_error
191+
end
192+
193+
it 'should return immediately if timeout is not set' do
194+
event.should_not_receive(:wait)
195+
196+
-> { obligation.value! }.should raise_error
197+
end
198+
199+
it 'should return immediately if timeout is not zero' do
200+
event.should_not_receive(:wait)
201+
202+
-> { obligation.value!(5) }.should raise_error
203+
end
130204

131-
obligation.value.should be_nil
132205
end
133206

134-
it 'should return immediately if timeout is not zero' do
135-
event.should_not_receive(:wait)
207+
describe '#no_error!' do
208+
209+
it 'should return immediately if timeout is zero' do
210+
event.should_not_receive(:wait)
211+
212+
-> { obligation.no_error!(0) }.should raise_error
213+
end
214+
215+
it 'should return immediately if timeout is not set' do
216+
event.should_not_receive(:wait)
217+
218+
-> { obligation.no_error! }.should raise_error
219+
end
220+
221+
it 'should return immediately if timeout is not zero' do
222+
event.should_not_receive(:wait)
223+
224+
-> { obligation.no_error!(5) }.should raise_error
225+
end
136226

137-
obligation.value(5).should be_nil
138227
end
139228

140229
end

0 commit comments

Comments
 (0)