Skip to content

Commit d1a5954

Browse files
committed
Merge pull request #86 from pitr-ch/minor
Convenience methods for Obligation and add_observer returning self
2 parents 9e4b9ec + 154e1ba commit d1a5954

File tree

4 files changed

+185
-43
lines changed

4 files changed

+185
-43
lines changed

lib/concurrent/obligation.rb

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,37 @@ def incomplete?
4242
[:unscheduled, :pending].include? state
4343
end
4444

45+
# @returns [Object] see Dereferenceable#deref
4546
def value(timeout = nil)
47+
wait timeout
48+
deref
49+
end
50+
51+
# wait until Obligation is #complete?
52+
# @param [Numeric] timeout the maximum time in second to wait.
53+
# @return [Obligation] self
54+
def wait(timeout = nil)
4655
event.wait(timeout) if timeout != 0 && incomplete?
47-
super()
56+
self
57+
end
58+
59+
# wait until Obligation is #complete?
60+
# @param [Numeric] timeout the maximum time in second to wait.
61+
# @return [Obligation] self
62+
# @raise [Exception] when #rejected? it raises #reason
63+
def no_error!(timeout = nil)
64+
wait(timeout).tap { raise self if rejected? }
65+
end
66+
67+
# @raise [Exception] when #rejected? it raises #reason
68+
# @returns [Object] see Dereferenceable#deref
69+
def value!(timeout = nil)
70+
wait(timeout)
71+
if rejected?
72+
raise self
73+
else
74+
deref
75+
end
4876
end
4977

5078
def state
@@ -61,6 +89,14 @@ def reason
6189
mutex.unlock
6290
end
6391

92+
# @example allows Obligation to be risen
93+
# rejected_ivar = Ivar.new.fail
94+
# raise rejected_ivar
95+
def exception(*args)
96+
raise 'obligation is not rejected' unless rejected?
97+
reason.exception(*args)
98+
end
99+
64100
protected
65101

66102
# @!visibility private
@@ -81,13 +117,16 @@ def set_state(success, value, reason) # :nodoc:
81117
@state = :fulfilled
82118
else
83119
@reason = reason
84-
@state = :rejected
120+
@state = :rejected
85121
end
86122
end
87123

88124
# @!visibility private
89125
def state=(value) # :nodoc:
90-
mutex.synchronize { @state = value }
126+
mutex.lock
127+
@state = value
128+
ensure
129+
mutex.unlock
91130
end
92131

93132
# atomic compare and set operation
@@ -100,14 +139,15 @@ def state=(value) # :nodoc:
100139
#
101140
# @!visibility private
102141
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
142+
mutex.lock
143+
if @state == expected_current
144+
@state = next_state
145+
true
146+
else
147+
false
110148
end
149+
ensure
150+
mutex.unlock
111151
end
112152

113153
# executes the block within mutex if current state is included in expected_states
@@ -116,15 +156,16 @@ def compare_and_set_state(next_state, expected_current) # :nodoc:
116156
#
117157
# @!visibility private
118158
def if_state(*expected_states) # :nodoc:
159+
mutex.lock
119160
raise ArgumentError.new('no block given') unless block_given?
120161

121-
mutex.synchronize do
122-
if expected_states.include? @state
123-
yield
124-
else
125-
false
126-
end
162+
if expected_states.include? @state
163+
yield
164+
else
165+
false
127166
end
167+
ensure
168+
mutex.unlock
128169
end
129170
end
130171
end

lib/concurrent/observable.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ def add_observer(*args, &block)
1010
observers.add_observer(*args, &block)
1111
end
1212

13+
# as #add_observer but it can be used for chaning
14+
# @return [Observable] self
15+
def with_observer(*args, &block)
16+
add_observer *args, &block
17+
self
18+
end
19+
1320
# @return [Object] the deleted observer
1421
def delete_observer(*args)
1522
observers.delete_observer(*args)

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

spec/concurrent/observable_spec.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@ module Concurrent
2323
end
2424

2525
it 'uses the given observer set' do
26-
expected = CopyOnWriteObserverSet.new
26+
expected = CopyOnWriteObserverSet.new
2727
subject.observers = expected
2828
subject.observers.should eql expected
2929
end
3030

3131
it 'delegates #add_observer' do
32-
observer_set.should_receive(:add_observer).with(:observer)
33-
subject.add_observer(:observer)
32+
observer_set.should_receive(:add_observer).with(:observer).and_return { |v| v }
33+
subject.add_observer(:observer).should eq :observer
34+
end
35+
36+
it 'delegates #with_observer' do
37+
observer_set.should_receive(:add_observer).with(:observer).and_return { |v| v }
38+
subject.with_observer(:observer).should eq subject
3439
end
3540

3641
it 'delegates #delete_observer' do

0 commit comments

Comments
 (0)