Skip to content

Commit 2b868ae

Browse files
author
Petr Chalupa
committed
Merge pull request #349 from pitr-ch/futures
Edge::Future updates
2 parents 1831906 + 505b9f4 commit 2b868ae

File tree

7 files changed

+434
-245
lines changed

7 files changed

+434
-245
lines changed

concurrent-ruby-edge.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
1010
s.name = 'concurrent-ruby-edge'
1111
s.version = Concurrent::EDGE_VERSION
1212
s.platform = Gem::Platform::RUBY
13-
s.authors = ["Jerry D'Antonio", 'The Ruby Concurrency Team']
13+
s.authors = ["Jerry D'Antonio", 'Petr Chalupa', 'The Ruby Concurrency Team']
1414
1515
s.homepage = 'http://www.concurrent-ruby.com'
1616
s.summary = 'Edge features and additions to the concurrent-ruby gem.'

examples/edge_futures.in.rb

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
### Chaining
2121

22-
head = Concurrent.future { 1 } #
22+
head = Concurrent.completed_future 1 #
2323
branch1 = head.then(&:succ) #
2424
branch2 = head.then(&:succ).then(&:succ) #
2525
branch1.zip(branch2).value!
@@ -29,10 +29,6 @@
2929
# pick only first completed
3030
(branch1 | branch2).value!
3131

32-
# auto splat arrays for blocks
33-
Concurrent.future { [1, 2] }.then(&:+).value!
34-
35-
3632
### Error handling
3733

3834
Concurrent.future { Object.new }.then(&:succ).then(&:succ).rescue { |e| e.class }.value # error propagates
@@ -94,6 +90,7 @@
9490

9591
future = Concurrent.future
9692
event = Concurrent.event
93+
# Don't forget to keep the reference, `Concurrent.future.then { |v| v }` is incompletable
9794

9895
# will be blocked until completed
9996
t1 = Thread.new { future.value } #

examples/edge_futures.out.rb

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
### Simple asynchronous task
22

33
future = Concurrent.future { sleep 0.1; 1 + 1 } # evaluation starts immediately
4-
# => <#Concurrent::Edge::Future:0x7fcc038ca588 pending blocks:[]>
4+
# => <#Concurrent::Edge::Future:0x7fd41a956d70 pending blocks:[]>
55
future.completed? # => false
66
# block until evaluated
77
future.value # => 2
@@ -11,7 +11,7 @@
1111
### Failing asynchronous task
1212

1313
future = Concurrent.future { raise 'Boom' }
14-
# => <#Concurrent::Edge::Future:0x7fcc038c05b0 pending blocks:[]>
14+
# => <#Concurrent::Edge::Future:0x7fd41a946c18 failed blocks:[]>
1515
future.value # => nil
1616
future.value! rescue $! # => #<RuntimeError: Boom>
1717
future.reason # => #<RuntimeError: Boom>
@@ -21,7 +21,7 @@
2121

2222
### Chaining
2323

24-
head = Concurrent.future { 1 }
24+
head = Concurrent.completed_future 1
2525
branch1 = head.then(&:succ)
2626
branch2 = head.then(&:succ).then(&:succ)
2727
branch1.zip(branch2).value! # => [2, 3]
@@ -32,10 +32,6 @@
3232
# pick only first completed
3333
(branch1 | branch2).value! # => 2
3434

35-
# auto splat arrays for blocks
36-
Concurrent.future { [1, 2] }.then(&:+).value! # => 3
37-
38-
3935
### Error handling
4036

4137
Concurrent.future { Object.new }.then(&:succ).then(&:succ).rescue { |e| e.class }.value # error propagates
@@ -50,28 +46,28 @@
5046

5147
# will not evaluate until asked by #value or other method requiring completion
5248
future = Concurrent.delay { 'lazy' }
53-
# => <#Concurrent::Edge::Future:0x7fcc022afaa0 pending blocks:[]>
49+
# => <#Concurrent::Edge::Future:0x7fd41a8f6ec0 pending blocks:[]>
5450
sleep 0.1
5551
future.completed? # => false
5652
future.value # => "lazy"
5753

5854
# propagates trough chain allowing whole or partial lazy chains
5955

6056
head = Concurrent.delay { 1 }
61-
# => <#Concurrent::Edge::Future:0x7fcc022a5640 pending blocks:[]>
57+
# => <#Concurrent::Edge::Future:0x7fd41a8ee1a8 pending blocks:[]>
6258
branch1 = head.then(&:succ)
63-
# => <#Concurrent::Edge::Future:0x7fcc0229c478 pending blocks:[]>
59+
# => <#Concurrent::Edge::Future:0x7fd41a8ed438 pending blocks:[]>
6460
branch2 = head.delay.then(&:succ)
65-
# => <#Concurrent::Edge::Future:0x7fcc0228f318 pending blocks:[]>
61+
# => <#Concurrent::Edge::Future:0x7fd41a8e7588 pending blocks:[]>
6662
join = branch1 & branch2
67-
# => <#Concurrent::Edge::Future:0x7fcc0228de78 pending blocks:[]>
63+
# => <#Concurrent::Edge::Future:0x7fd41a8e4ec8 pending blocks:[]>
6864

6965
sleep 0.1 # nothing will complete # => 0
7066
[head, branch1, branch2, join].map(&:completed?) # => [false, false, false, false]
7167

7268
branch1.value # => 2
7369
sleep 0.1 # forces only head to complete, branch 2 stays incomplete
74-
# => 0
70+
# => 1
7571
[head, branch1, branch2, join].map(&:completed?) # => [true, true, false, false]
7672

7773
join.value # => [2, 2]
@@ -92,14 +88,14 @@
9288
### Schedule
9389

9490
scheduled = Concurrent.schedule(0.1) { 1 }
95-
# => <#Concurrent::Edge::Future:0x7fcc0385b368 pending blocks:[]>
91+
# => <#Concurrent::Edge::Future:0x7fd41a8a4468 pending blocks:[]>
9692

9793
scheduled.completed? # => false
9894
scheduled.value # available after 0.1sec # => 1
9995

10096
# and in chain
10197
scheduled = Concurrent.delay { 1 }.schedule(0.1).then(&:succ)
102-
# => <#Concurrent::Edge::Future:0x7fcc03843948 pending blocks:[]>
98+
# => <#Concurrent::Edge::Future:0x7fd41a895828 pending blocks:[]>
10399
# will not be scheduled until value is requested
104100
sleep 0.1
105101
scheduled.value # returns after another 0.1sec # => 2
@@ -108,35 +104,36 @@
108104
### Completable Future and Event
109105

110106
future = Concurrent.future
111-
# => <#Concurrent::Edge::CompletableFuture:0x7fcc0304fdb8 pending blocks:[]>
107+
# => <#Concurrent::Edge::CompletableFuture:0x7fd41a87c648 pending blocks:[]>
112108
event = Concurrent.event
113-
# => <#Concurrent::Edge::CompletableEvent:0x7fcc0304e210 pending blocks:[]>
109+
# => <#Concurrent::Edge::CompletableEvent:0x7fd41a8770d0 pending blocks:[]>
110+
# Don't forget to keep the reference, `Concurrent.future.then { |v| v }` is incompletable
114111

115112
# will be blocked until completed
116113
t1 = Thread.new { future.value }
117114
t2 = Thread.new { event.wait }
118115

119116
future.success 1
120-
# => <#Concurrent::Edge::CompletableFuture:0x7fcc0304fdb8 success blocks:[]>
117+
# => <#Concurrent::Edge::CompletableFuture:0x7fd41a87c648 success blocks:[]>
121118
future.success 1 rescue $!
122-
# => #<Concurrent::MultipleAssignmentError: multiple assignment>
119+
# => #<Concurrent::MultipleAssignmentError: Future can be completed only once. Current result is [true, 1, nil], trying to set [true, 1, nil]>
123120
future.try_success 2 # => false
124121
event.complete
125-
# => <#Concurrent::Edge::CompletableEvent:0x7fcc0304e210 completed blocks:[]>
122+
# => <#Concurrent::Edge::CompletableEvent:0x7fd41a8770d0 completed blocks:[]>
126123

127124
[t1, t2].each &:join
128125

129126

130127
### Callbacks
131128

132-
queue = Queue.new # => #<Thread::Queue:0x007fcc03101720>
129+
queue = Queue.new # => #<Thread::Queue:0x007fd41b0543e8>
133130
future = Concurrent.delay { 1 + 1 }
134-
# => <#Concurrent::Edge::Future:0x7fcc030fbaf0 pending blocks:[]>
131+
# => <#Concurrent::Edge::Future:0x7fd41b04f028 pending blocks:[]>
135132

136133
future.on_success { queue << 1 } # evaluated asynchronously
137-
# => <#Concurrent::Edge::Future:0x7fcc030fbaf0 pending blocks:[]>
134+
# => <#Concurrent::Edge::Future:0x7fd41b04f028 pending blocks:[]>
138135
future.on_success! { queue << 2 } # evaluated on completing thread
139-
# => <#Concurrent::Edge::Future:0x7fcc030fbaf0 pending blocks:[]>
136+
# => <#Concurrent::Edge::Future:0x7fd41b04f028 pending blocks:[]>
140137

141138
queue.empty? # => true
142139
future.value # => 2
@@ -147,15 +144,15 @@
147144
### Thread-pools
148145

149146
Concurrent.future(:fast) { 2 }.then(:io) { File.read __FILE__ }.wait
150-
# => <#Concurrent::Edge::Future:0x7fcc030e8bd0 success blocks:[]>
147+
# => <#Concurrent::Edge::Future:0x7fd41a857550 success blocks:[]>
151148

152149

153150
### Interoperability with actors
154151

155152
actor = Concurrent::Actor::Utils::AdHoc.spawn :square do
156153
-> v { v ** 2 }
157154
end
158-
# => #<Concurrent::Actor::Reference:0x7fcc0223f020 /square (Concurrent::Actor::Utils::AdHoc)>
155+
# => #<Concurrent::Actor::Reference:0x7fd41c03b5b8 /square (Concurrent::Actor::Utils::AdHoc)>
159156

160157
Concurrent.
161158
future { 2 }.
@@ -168,32 +165,32 @@
168165

169166
### Interoperability with channels
170167

171-
ch1 = Concurrent::Edge::Channel.new # => #<Concurrent::Edge::Channel:0x007fcc021ec6b8>
172-
ch2 = Concurrent::Edge::Channel.new # => #<Concurrent::Edge::Channel:0x007fcc021e7b18>
168+
ch1 = Concurrent::Edge::Channel.new # => #<Concurrent::Edge::Channel:0x007fd41a297458>
169+
ch2 = Concurrent::Edge::Channel.new # => #<Concurrent::Edge::Channel:0x007fd41a296940>
173170

174171
result = Concurrent.select(ch1, ch2)
175-
# => <#Concurrent::Edge::CompletableFuture:0x7fcc021e6060 pending blocks:[]>
172+
# => <#Concurrent::Edge::CompletableFuture:0x7fd41a295c98 pending blocks:[]>
176173
ch1.push 1 # => nil
177174
result.value!
178-
# => [1, #<Concurrent::Edge::Channel:0x007fcc021ec6b8>]
175+
# => [1, #<Concurrent::Edge::Channel:0x007fd41a297458>]
179176

180177
Concurrent.
181178
future { 1+1 }.
182179
then_push(ch1)
183-
# => <#Concurrent::Edge::Future:0x7fcc021dc3d0 pending blocks:[]>
180+
# => <#Concurrent::Edge::Future:0x7fd41a284010 pending blocks:[]>
184181
result = Concurrent.
185182
future { '%02d' }.
186183
then_select(ch1, ch2).
187184
then { |format, (value, channel)| format format, value }
188-
# => <#Concurrent::Edge::Future:0x7fcc021cd9e8 pending blocks:[]>
185+
# => <#Concurrent::Edge::Future:0x7fd41a25d938 pending blocks:[]>
189186
result.value! # => "02"
190187

191188

192189
### Common use-cases Examples
193190

194191
# simple background processing
195192
Concurrent.future { do_stuff }
196-
# => <#Concurrent::Edge::Future:0x7fcc021b7530 pending blocks:[]>
193+
# => <#Concurrent::Edge::Future:0x7fd41a23d520 pending blocks:[]>
197194

198195
# parallel background processing
199196
jobs = 10.times.map { |i| Concurrent.future { i } }
@@ -210,7 +207,7 @@ def schedule_job
210207
end # => :schedule_job
211208

212209
schedule_job
213-
# => <#Concurrent::Edge::Future:0x7fcc0218faf8 pending blocks:[]>
210+
# => <#Concurrent::Edge::Future:0x7fd41a2245e8 pending blocks:[]>
214211
@end = true # => true
215212

216213

@@ -223,7 +220,7 @@ def schedule_job
223220
data[message]
224221
end
225222
end
226-
# => #<Concurrent::Actor::Reference:0x7fcc0214f458 /db (Concurrent::Actor::Utils::AdHoc)>
223+
# => #<Concurrent::Actor::Reference:0x7fd41a206228 /db (Concurrent::Actor::Utils::AdHoc)>
227224

228225
concurrent_jobs = 11.times.map do |v|
229226
Concurrent.
@@ -253,7 +250,7 @@ def schedule_job
253250
end
254251
end
255252
end
256-
# => #<Concurrent::Actor::Reference:0x7fcc02930398 /DB-pool (Concurrent::Actor::Utils::Pool)>
253+
# => #<Concurrent::Actor::Reference:0x7fd41a0530c0 /DB-pool (Concurrent::Actor::Utils::Pool)>
257254

258255
concurrent_jobs = 11.times.map do |v|
259256
Concurrent.

lib/concurrent/edge.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ module Concurrent
2121
#
2222
# @!macro [attach] edge_warning
2323
# @api Edge
24-
# @note **Edge Feature:** Edge features are under active development and may
25-
# change frequently. They are not expected to keep backward compatibility.
26-
# They may also lack tests and documentation. Use with caution.
24+
# @note **Edge Feature:** Edge features are under active development and may change frequently. They are expected not to
25+
# keep backward compatibility (there may also lack tests and documentation). Semantic versions will
26+
# be obeyed though. Features developed in `concurrent-ruby-edge` are expected to move
27+
# to `concurrent-ruby` when final.
2728
module Edge
28-
2929
end
3030
end

0 commit comments

Comments
 (0)