concurrent-ruby-edge 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,185 @@
1
+ module Concurrent
2
+ # @!macro [new] throttle.example.throttled_block
3
+ # @example
4
+ # max_two = Throttle.new 2
5
+ # 10.times.map do
6
+ # Thread.new do
7
+ # max_two.throttled_block do
8
+ # # Only 2 at the same time
9
+ # do_stuff
10
+ # end
11
+ # end
12
+ # end
13
+ # @!macro [new] throttle.example.throttled_future_chain
14
+ # @example
15
+ # throttle.throttled_future_chain do |trigger|
16
+ # trigger.
17
+ # # 2 throttled promises
18
+ # chain { 1 }.
19
+ # then(&:succ)
20
+ # end
21
+ # @!macro [new] throttle.example.then_throttled_by
22
+ # @example
23
+ # data = (1..5).to_a
24
+ # db = data.reduce({}) { |h, v| h.update v => v.to_s }
25
+ # max_two = Throttle.new 2
26
+ #
27
+ # futures = data.map do |data|
28
+ # Promises.future(data) do |data|
29
+ # # un-throttled, concurrency level equal data.size
30
+ # data + 1
31
+ # end.then_throttled_by(max_two, db) do |v, db|
32
+ # # throttled, only 2 tasks executed at the same time
33
+ # # e.g. limiting access to db
34
+ # db[v]
35
+ # end
36
+ # end
37
+ #
38
+ # futures.map(&:value!) # => [2, 3, 4, 5, nil]
39
+
40
+ # A tool manage concurrency level of future tasks.
41
+ #
42
+ # @!macro throttle.example.then_throttled_by
43
+ # @!macro throttle.example.throttled_future_chain
44
+ # @!macro throttle.example.throttled_block
45
+ class Throttle < Synchronization::Object
46
+ # TODO (pitr-ch 21-Dec-2016): consider using sized channel for implementation instead when available
47
+
48
+ safe_initialization!
49
+ private *attr_atomic(:can_run)
50
+
51
+ # New throttle.
52
+ # @param [Integer] limit
53
+ def initialize(limit)
54
+ super()
55
+ @Limit = limit
56
+ self.can_run = limit
57
+ @Queue = LockFreeQueue.new
58
+ end
59
+
60
+ # @return [Integer] The limit.
61
+ def limit
62
+ @Limit
63
+ end
64
+
65
+ # New event which will be resolved when depending tasks can execute.
66
+ # Has to be used and after the critical work is done {#release} must be called exactly once.
67
+ # @return [Promises::Event]
68
+ # @see #release
69
+ def trigger
70
+ while true
71
+ current_can_run = can_run
72
+ if compare_and_set_can_run current_can_run, current_can_run - 1
73
+ if current_can_run > 0
74
+ return Promises.resolved_event
75
+ else
76
+ event = Promises.resolvable_event
77
+ @Queue.push event
78
+ return event
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ # Has to be called once for each trigger after it is ok to execute another throttled task.
85
+ # @return [self]
86
+ # @see #trigger
87
+ def release
88
+ while true
89
+ current_can_run = can_run
90
+ if compare_and_set_can_run current_can_run, current_can_run + 1
91
+ if current_can_run < 0
92
+ Thread.pass until (trigger = @Queue.pop)
93
+ trigger.resolve
94
+ end
95
+ return self
96
+ end
97
+ end
98
+ end
99
+
100
+ # Blocks current thread until the block can be executed.
101
+ # @yield to throttled block
102
+ # @yieldreturn [Object] is used as a result of the method
103
+ # @return [Object] the result of the block
104
+ # @!macro throttle.example.throttled_block
105
+ def throttled_block(&block)
106
+ trigger.wait
107
+ block.call
108
+ ensure
109
+ release
110
+ end
111
+
112
+ # @return [String] Short string representation.
113
+ def to_s
114
+ format '<#%s:0x%x limit:%s can_run:%d>', self.class, object_id << 1, @Limit, can_run
115
+ end
116
+
117
+ alias_method :inspect, :to_s
118
+
119
+ module PromisesIntegration
120
+
121
+ # Allows to throttle a chain of promises.
122
+ # @yield [trigger] a trigger which has to be used to build up a chain of promises, the last one is result
123
+ # of the block. When the last one resolves, {Throttle#release} is called on the throttle.
124
+ # @yieldparam [Promises::Event, Promises::Future] trigger
125
+ # @yieldreturn [Promises::Event, Promises::Future] The final future of the throttled chain.
126
+ # @return [Promises::Event, Promises::Future] The final future of the throttled chain.
127
+ # @!macro throttle.example.throttled_future_chain
128
+ def throttled_future_chain(&throttled_futures)
129
+ throttled_futures.call(trigger).on_resolution! { release }
130
+ end
131
+
132
+ # Behaves as {Promises::FactoryMethods#future} but the future is throttled.
133
+ # @return [Promises::Future]
134
+ # @see Promises::FactoryMethods#future
135
+ def throttled_future(*args, &task)
136
+ trigger.chain(*args, &task).on_resolution! { release }
137
+ end
138
+ end
139
+
140
+ include PromisesIntegration
141
+ end
142
+
143
+ module Promises
144
+
145
+ class AbstractEventFuture < Synchronization::Object
146
+ module ThrottleIntegration
147
+ def throttled_by(throttle, &throttled_futures)
148
+ a_trigger = self & self.chain { throttle.trigger }.flat_event
149
+ throttled_futures.call(a_trigger).on_resolution! { throttle.release }
150
+ end
151
+
152
+ # Behaves as {Promises::AbstractEventFuture#chain} but the it is throttled.
153
+ # @return [Promises::Future, Promises::Event]
154
+ # @see Promises::AbstractEventFuture#chain
155
+ def chain_throttled_by(throttle, *args, &block)
156
+ throttled_by(throttle) { |trigger| trigger.chain(*args, &block) }
157
+ end
158
+ end
159
+
160
+ include ThrottleIntegration
161
+ end
162
+
163
+ class Future < AbstractEventFuture
164
+ module ThrottleIntegration
165
+
166
+ # Behaves as {Promises::Future#then} but the it is throttled.
167
+ # @return [Promises::Future]
168
+ # @see Promises::Future#then
169
+ # @!macro throttle.example.then_throttled_by
170
+ def then_throttled_by(throttle, *args, &block)
171
+ throttled_by(throttle) { |trigger| trigger.then(*args, &block) }
172
+ end
173
+
174
+ # Behaves as {Promises::Future#rescue} but the it is throttled.
175
+ # @return [Promises::Future]
176
+ # @see Promises::Future#rescue
177
+ def rescue_throttled_by(throttle, *args, &block)
178
+ throttled_by(throttle) { |trigger| trigger.rescue(*args, &block) }
179
+ end
180
+ end
181
+
182
+ include ThrottleIntegration
183
+ end
184
+ end
185
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: concurrent-ruby-edge
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jerry D'Antonio
@@ -10,22 +10,22 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-03-29 00:00:00.000000000 Z
13
+ date: 2016-12-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: concurrent-ruby
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - "~>"
19
+ - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: 1.0.3
21
+ version: 1.0.4
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
- - - "~>"
26
+ - - '='
27
27
  - !ruby/object:Gem::Version
28
- version: 1.0.3
28
+ version: 1.0.4
29
29
  description: |
30
30
  These features are under active development and may change frequently. They are expected not to
31
31
  keep backward compatibility (there may also lack tests and documentation). Semantic versions will
@@ -88,11 +88,15 @@ files:
88
88
  - lib/concurrent/channel/selector/take_clause.rb
89
89
  - lib/concurrent/channel/tick.rb
90
90
  - lib/concurrent/edge/atomic_markable_reference.rb
91
- - lib/concurrent/edge/future.rb
91
+ - lib/concurrent/edge/cancellation.rb
92
92
  - lib/concurrent/edge/lock_free_linked_set.rb
93
93
  - lib/concurrent/edge/lock_free_linked_set/node.rb
94
94
  - lib/concurrent/edge/lock_free_linked_set/window.rb
95
+ - lib/concurrent/edge/lock_free_queue.rb
95
96
  - lib/concurrent/edge/lock_free_stack.rb
97
+ - lib/concurrent/edge/old_channel_integration.rb
98
+ - lib/concurrent/edge/promises.rb
99
+ - lib/concurrent/edge/throttle.rb
96
100
  homepage: http://www.concurrent-ruby.com
97
101
  licenses:
98
102
  - MIT
@@ -113,8 +117,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
117
  version: '0'
114
118
  requirements: []
115
119
  rubyforge_project:
116
- rubygems_version: 2.6.8
120
+ rubygems_version: 2.5.1
117
121
  signing_key:
118
122
  specification_version: 4
119
123
  summary: Edge features and additions to the concurrent-ruby gem.
120
124
  test_files: []
125
+ has_rdoc:
@@ -1,1427 +0,0 @@
1
- require 'concurrent' # TODO do not require whole concurrent gem
2
- require 'concurrent/concern/deprecation'
3
- require 'concurrent/edge/lock_free_stack'
4
-
5
-
6
- # @note different name just not to collide for now
7
- module Concurrent
8
- module Edge
9
-
10
- # Provides edge features, which will be added to or replace features in main gem.
11
- #
12
- # Contains new unified implementation of Futures and Promises which combines Features of previous `Future`,
13
- # `Promise`, `IVar`, `Event`, `Probe`, `dataflow`, `Delay`, `TimerTask` into single framework. It uses extensively
14
- # new synchronization layer to make all the paths lock-free with exception of blocking threads on `#wait`.
15
- # It offers better performance and does not block threads (exception being #wait and similar methods where it's
16
- # intended).
17
- #
18
- # ## Examples
19
- # {include:file:examples/edge_futures.out.rb}
20
- #
21
- # @!macro edge_warning
22
- module FutureShortcuts
23
- # User is responsible for completing the event once by {Edge::CompletableEvent#complete}
24
- # @return [CompletableEvent]
25
- def event(default_executor = :io)
26
- CompletableEventPromise.new(default_executor).future
27
- end
28
-
29
- # @overload future(default_executor = :io, &task)
30
- # Constructs new Future which will be completed after block is evaluated on executor. Evaluation begins immediately.
31
- # @return [Future]
32
- # @overload future(default_executor = :io)
33
- # User is responsible for completing the future once by {Edge::CompletableFuture#success} or {Edge::CompletableFuture#fail}
34
- # @return [CompletableFuture]
35
- def future(default_executor = :io, &task)
36
- if task
37
- ImmediateEventPromise.new(default_executor).future.then(&task)
38
- else
39
- CompletableFuturePromise.new(default_executor).future
40
- end
41
- end
42
-
43
- # @return [Future] which is already completed
44
- def completed_future(success, value, reason, default_executor = :io)
45
- ImmediateFuturePromise.new(default_executor, success, value, reason).future
46
- end
47
-
48
- # @return [Future] which is already completed in success state with value
49
- def succeeded_future(value, default_executor = :io)
50
- completed_future true, value, nil, default_executor
51
- end
52
-
53
- # @return [Future] which is already completed in failed state with reason
54
- def failed_future(reason, default_executor = :io)
55
- completed_future false, nil, reason, default_executor
56
- end
57
-
58
- # @return [Event] which is already completed
59
- def completed_event(default_executor = :io)
60
- ImmediateEventPromise.new(default_executor).event
61
- end
62
-
63
- alias_method :async, :future
64
-
65
- # Constructs new Future which will evaluate to the block after
66
- # requested by calling `#wait`, `#value`, `#value!`, etc. on it or on any of the chained futures.
67
- # @return [Future]
68
- def delay(default_executor = :io, &task)
69
- DelayPromise.new(default_executor).future.then(&task)
70
- end
71
-
72
- # Schedules the block to be executed on executor in given intended_time.
73
- # @param [Numeric, Time] intended_time Numeric => run in `intended_time` seconds. Time => eun on time.
74
- # @return [Future]
75
- def schedule(intended_time, default_executor = :io, &task)
76
- ScheduledPromise.new(default_executor, intended_time).future.then(&task)
77
- end
78
-
79
- # Constructs new {Future} which is completed after all futures_and_or_events are complete. Its value is array
80
- # of dependent future values. If there is an error it fails with the first one. Event does not
81
- # have a value so it's represented by nil in the array of values.
82
- # @param [Event] futures_and_or_events
83
- # @return [Future]
84
- def zip_futures(*futures_and_or_events)
85
- ZipFuturesPromise.new(futures_and_or_events, :io).future
86
- end
87
-
88
- alias_method :zip, :zip_futures
89
-
90
- # Constructs new {Event} which is completed after all futures_and_or_events are complete
91
- # (Future is completed when Success or Failed).
92
- # @param [Event] futures_and_or_events
93
- # @return [Event]
94
- def zip_events(*futures_and_or_events)
95
- ZipEventsPromise.new(futures_and_or_events, :io).future
96
- end
97
-
98
- # Constructs new {Future} which is completed after first of the futures is complete.
99
- # @param [Event] futures
100
- # @return [Future]
101
- def any_complete(*futures)
102
- AnyCompletePromise.new(futures, :io).future
103
- end
104
-
105
- alias_method :any, :any_complete
106
-
107
- # Constructs new {Future} which becomes succeeded after first of the futures succeedes or
108
- # failed if all futures fail (reason is last error).
109
- # @param [Event] futures
110
- # @return [Future]
111
- def any_successful(*futures)
112
- AnySuccessfulPromise.new(futures, :io).future
113
- end
114
-
115
- # only proof of concept
116
- # @return [Future]
117
- def select(*channels)
118
- future do
119
- # noinspection RubyArgCount
120
- Channel.select do |s|
121
- channels.each do |ch|
122
- s.take(ch) { |value| [value, ch] }
123
- end
124
- end
125
- end
126
- end
127
-
128
- # post job on :fast executor
129
- # @return [true, false]
130
- def post!(*args, &job)
131
- post_on(:fast, *args, &job)
132
- end
133
-
134
- # post job on :io executor
135
- # @return [true, false]
136
- def post(*args, &job)
137
- post_on(:io, *args, &job)
138
- end
139
-
140
- # post job on executor
141
- # @return [true, false]
142
- def post_on(executor, *args, &job)
143
- Concurrent.executor(executor).post(*args, &job)
144
- end
145
-
146
- # TODO add first(futures, count=count)
147
- # TODO allow to to have a zip point for many futures and process them in batches by 10
148
- end
149
-
150
- # Represents an event which will happen in future (will be completed). It has to always happen.
151
- class Event < Synchronization::Object
152
- safe_initialization!
153
- private(*attr_atomic(:internal_state))
154
- # @!visibility private
155
- public :internal_state
156
- include Concern::Deprecation
157
- include Concern::Logging
158
-
159
- # @!visibility private
160
- class State
161
- def completed?
162
- raise NotImplementedError
163
- end
164
-
165
- def to_sym
166
- raise NotImplementedError
167
- end
168
- end
169
-
170
- # @!visibility private
171
- class Pending < State
172
- def completed?
173
- false
174
- end
175
-
176
- def to_sym
177
- :pending
178
- end
179
- end
180
-
181
- # @!visibility private
182
- class Completed < State
183
- def completed?
184
- true
185
- end
186
-
187
- def to_sym
188
- :completed
189
- end
190
- end
191
-
192
- # @!visibility private
193
- PENDING = Pending.new
194
- # @!visibility private
195
- COMPLETED = Completed.new
196
-
197
- def initialize(promise, default_executor)
198
- super()
199
- @Lock = Mutex.new
200
- @Condition = ConditionVariable.new
201
- @Promise = promise
202
- @DefaultExecutor = default_executor
203
- @Touched = AtomicBoolean.new false
204
- @Callbacks = LockFreeStack.new
205
- @Waiters = AtomicFixnum.new 0
206
- self.internal_state = PENDING
207
- end
208
-
209
- # @return [:pending, :completed]
210
- def state
211
- internal_state.to_sym
212
- end
213
-
214
- # Is Event/Future pending?
215
- # @return [Boolean]
216
- def pending?(state = internal_state)
217
- !state.completed?
218
- end
219
-
220
- def unscheduled?
221
- raise 'unsupported'
222
- end
223
-
224
- alias_method :incomplete?, :pending?
225
-
226
- # Has the Event been completed?
227
- # @return [Boolean]
228
- def completed?(state = internal_state)
229
- state.completed?
230
- end
231
-
232
- alias_method :complete?, :completed?
233
-
234
- # Wait until Event is #complete?
235
- # @param [Numeric] timeout the maximum time in second to wait.
236
- # @return [Event, true, false] self or true/false if timeout is used
237
- # @!macro [attach] edge.periodical_wait
238
- # @note a thread should wait only once! For repeated checking use faster `completed?` check.
239
- # If thread waits periodically it will dangerously grow the waiters stack.
240
- def wait(timeout = nil)
241
- touch
242
- result = wait_until_complete(timeout)
243
- timeout ? result : self
244
- end
245
-
246
- # @!visibility private
247
- def touch
248
- # distribute touch to promise only once
249
- @Promise.touch if @Touched.make_true
250
- self
251
- end
252
-
253
- # @return [Executor] current default executor
254
- # @see #with_default_executor
255
- def default_executor
256
- @DefaultExecutor
257
- end
258
-
259
- # @yield [success, value, reason] of the parent
260
- def chain(executor = nil, &callback)
261
- ChainPromise.new(self, @DefaultExecutor, executor || @DefaultExecutor, &callback).future
262
- end
263
-
264
- alias_method :then, :chain
265
-
266
- def chain_completable(completable_event)
267
- on_completion! { completable_event.complete_with COMPLETED }
268
- end
269
-
270
- alias_method :tangle, :chain_completable
271
-
272
- # Zip with future producing new Future
273
- # @return [Event]
274
- def zip(other)
275
- if other.is?(Future)
276
- ZipFutureEventPromise.new(other, self, @DefaultExecutor).future
277
- else
278
- ZipEventEventPromise.new(self, other, @DefaultExecutor).future
279
- end
280
- end
281
-
282
- alias_method :&, :zip
283
-
284
- # Inserts delay into the chain of Futures making rest of it lazy evaluated.
285
- # @return [Event]
286
- def delay
287
- ZipEventEventPromise.new(self, DelayPromise.new(@DefaultExecutor).event, @DefaultExecutor).event
288
- end
289
-
290
- # # Schedules rest of the chain for execution with specified time or on specified time
291
- # # @return [Event]
292
- # def schedule(intended_time)
293
- # chain do
294
- # ZipEventEventPromise.new(self,
295
- # ScheduledPromise.new(@DefaultExecutor, intended_time).event,
296
- # @DefaultExecutor).event
297
- # end.flat
298
- # end
299
-
300
- # Zips with selected value form the suplied channels
301
- # @return [Future]
302
- def then_select(*channels)
303
- ZipFutureEventPromise(Concurrent.select(*channels), self, @DefaultExecutor).future
304
- end
305
-
306
- # @yield [success, value, reason] executed async on `executor` when completed
307
- # @return self
308
- def on_completion(executor = nil, &callback)
309
- add_callback :async_callback_on_completion, executor || @DefaultExecutor, callback
310
- end
311
-
312
- # @yield [success, value, reason] executed sync when completed
313
- # @return self
314
- def on_completion!(&callback)
315
- add_callback :callback_on_completion, callback
316
- end
317
-
318
- # Changes default executor for rest of the chain
319
- # @return [Event]
320
- def with_default_executor(executor)
321
- EventWrapperPromise.new(self, executor).future
322
- end
323
-
324
- def to_s
325
- "<##{self.class}:0x#{'%x' % (object_id << 1)} #{state.to_sym}>"
326
- end
327
-
328
- def inspect
329
- "#{to_s[0..-2]} blocks:[#{blocks.map(&:to_s).join(', ')}]>"
330
- end
331
-
332
- def set(*args, &block)
333
- raise 'Use CompletableEvent#complete or CompletableFuture#complete instead, ' +
334
- 'constructed by Concurrent.event or Concurrent.future respectively.'
335
- end
336
-
337
- # @!visibility private
338
- def complete_with(state, raise_on_reassign = true)
339
- if compare_and_set_internal_state(PENDING, state)
340
- # go to synchronized block only if there were waiting threads
341
- @Lock.synchronize { @Condition.broadcast } unless @Waiters.value == 0
342
- call_callbacks
343
- else
344
- Concurrent::MultipleAssignmentError.new('Event can be completed only once') if raise_on_reassign
345
- return false
346
- end
347
- self
348
- end
349
-
350
- # @!visibility private
351
- # just for inspection
352
- # @return [Array<AbstractPromise>]
353
- def blocks
354
- @Callbacks.each_with_object([]) do |callback, promises|
355
- promises.push(*(callback.select { |v| v.is_a? AbstractPromise }))
356
- end
357
- end
358
-
359
- # @!visibility private
360
- # just for inspection
361
- def callbacks
362
- @Callbacks.each.to_a
363
- end
364
-
365
- # @!visibility private
366
- def add_callback(method, *args)
367
- if completed?
368
- call_callback method, *args
369
- else
370
- @Callbacks.push [method, *args]
371
- call_callbacks if completed?
372
- end
373
- self
374
- end
375
-
376
- # @!visibility private
377
- # only for inspection
378
- def promise
379
- @Promise
380
- end
381
-
382
- # @!visibility private
383
- # only for inspection
384
- def touched
385
- @Touched.value
386
- end
387
-
388
- # @!visibility private
389
- # only for debugging inspection
390
- def waiting_threads
391
- @Waiters.each.to_a
392
- end
393
-
394
- private
395
-
396
- # @return [true, false]
397
- def wait_until_complete(timeout)
398
- return true if completed?
399
-
400
- @Lock.synchronize do
401
- @Waiters.increment
402
- begin
403
- unless completed?
404
- @Condition.wait @Lock, timeout
405
- end
406
- ensure
407
- # JRuby may raise ConcurrencyError
408
- @Waiters.decrement
409
- end
410
- end
411
- completed?
412
- end
413
-
414
- def with_async(executor, *args, &block)
415
- Concurrent.post_on(executor, *args, &block)
416
- end
417
-
418
- def async_callback_on_completion(executor, callback)
419
- with_async(executor) { callback_on_completion callback }
420
- end
421
-
422
- def callback_on_completion(callback)
423
- callback.call
424
- end
425
-
426
- def callback_notify_blocked(promise)
427
- promise.on_done self
428
- end
429
-
430
- def call_callback(method, *args)
431
- self.send method, *args
432
- end
433
-
434
- def call_callbacks
435
- method, *args = @Callbacks.pop
436
- while method
437
- call_callback method, *args
438
- method, *args = @Callbacks.pop
439
- end
440
- end
441
- end
442
-
443
- # Represents a value which will become available in future. May fail with a reason instead.
444
- class Future < Event
445
- # @!visibility private
446
- class CompletedWithResult < Completed
447
- def result
448
- [success?, value, reason]
449
- end
450
-
451
- def success?
452
- raise NotImplementedError
453
- end
454
-
455
- def value
456
- raise NotImplementedError
457
- end
458
-
459
- def reason
460
- raise NotImplementedError
461
- end
462
- end
463
-
464
- # @!visibility private
465
- class Success < CompletedWithResult
466
- def initialize(value)
467
- @Value = value
468
- end
469
-
470
- def success?
471
- true
472
- end
473
-
474
- def apply(block)
475
- block.call value
476
- end
477
-
478
- def value
479
- @Value
480
- end
481
-
482
- def reason
483
- nil
484
- end
485
-
486
- def to_sym
487
- :success
488
- end
489
- end
490
-
491
- # @!visibility private
492
- class SuccessArray < Success
493
- def apply(block)
494
- block.call(*value)
495
- end
496
- end
497
-
498
- # @!visibility private
499
- class Failed < CompletedWithResult
500
- def initialize(reason)
501
- @Reason = reason
502
- end
503
-
504
- def success?
505
- false
506
- end
507
-
508
- def value
509
- nil
510
- end
511
-
512
- def reason
513
- @Reason
514
- end
515
-
516
- def to_sym
517
- :failed
518
- end
519
-
520
- def apply(block)
521
- block.call reason
522
- end
523
- end
524
-
525
- # @!visibility private
526
- class PartiallyFailed < CompletedWithResult
527
- def initialize(value, reason)
528
- super()
529
- @Value = value
530
- @Reason = reason
531
- end
532
-
533
- def success?
534
- false
535
- end
536
-
537
- def to_sym
538
- :failed
539
- end
540
-
541
- def value
542
- @Value
543
- end
544
-
545
- def reason
546
- @Reason
547
- end
548
-
549
- def apply(block)
550
- block.call(*reason)
551
- end
552
- end
553
-
554
- # @!method state
555
- # @return [:pending, :success, :failed]
556
-
557
- # Has Future been success?
558
- # @return [Boolean]
559
- def success?(state = internal_state)
560
- state.completed? && state.success?
561
- end
562
-
563
- def fulfilled?
564
- deprecated_method 'fulfilled?', 'success?'
565
- success?
566
- end
567
-
568
- # Has Future been failed?
569
- # @return [Boolean]
570
- def failed?(state = internal_state)
571
- state.completed? && !state.success?
572
- end
573
-
574
- def rejected?
575
- deprecated_method 'rejected?', 'failed?'
576
- failed?
577
- end
578
-
579
- # @return [Object, nil] the value of the Future when success, nil on timeout
580
- # @!macro [attach] edge.timeout_nil
581
- # @note If the Future can have value `nil` then it cannot be distinquished from `nil` returned on timeout.
582
- # In this case is better to use first `wait` then `value` (or similar).
583
- # @!macro edge.periodical_wait
584
- def value(timeout = nil)
585
- touch
586
- internal_state.value if wait_until_complete timeout
587
- end
588
-
589
- # @return [Exception, nil] the reason of the Future's failure
590
- # @!macro edge.timeout_nil
591
- # @!macro edge.periodical_wait
592
- def reason(timeout = nil)
593
- touch
594
- internal_state.reason if wait_until_complete timeout
595
- end
596
-
597
- # @return [Array(Boolean, Object, Exception), nil] triplet of success, value, reason
598
- # @!macro edge.timeout_nil
599
- # @!macro edge.periodical_wait
600
- def result(timeout = nil)
601
- touch
602
- internal_state.result if wait_until_complete timeout
603
- end
604
-
605
- # Wait until Future is #complete?
606
- # @param [Numeric] timeout the maximum time in second to wait.
607
- # @raise reason on failure
608
- # @return [Event, true, false] self or true/false if timeout is used
609
- # @!macro edge.periodical_wait
610
- def wait!(timeout = nil)
611
- touch
612
- result = wait_until_complete!(timeout)
613
- timeout ? result : self
614
- end
615
-
616
- # Wait until Future is #complete?
617
- # @param [Numeric] timeout the maximum time in second to wait.
618
- # @raise reason on failure
619
- # @return [Object, nil]
620
- # @!macro edge.timeout_nil
621
- # @!macro edge.periodical_wait
622
- def value!(timeout = nil)
623
- touch
624
- internal_state.value if wait_until_complete! timeout
625
- end
626
-
627
- # @example allows failed Future to be risen
628
- # raise Concurrent.future.fail
629
- def exception(*args)
630
- raise 'obligation is not failed' unless failed?
631
- reason = internal_state.reason
632
- if reason.is_a?(::Array)
633
- reason.each { |e| log ERROR, 'Edge::Future', e }
634
- Concurrent::Error.new 'multiple exceptions, inspect log'
635
- else
636
- reason.exception(*args)
637
- end
638
- end
639
-
640
- # @yield [value] executed only on parent success
641
- # @return [Future]
642
- def then(executor = nil, &callback)
643
- ThenPromise.new(self, @DefaultExecutor, executor || @DefaultExecutor, &callback).future
644
- end
645
-
646
- # Asks the actor with its value.
647
- # @return [Future] new future with the response form the actor
648
- def then_ask(actor)
649
- self.then { |v| actor.ask(v) }.flat
650
- end
651
-
652
- def chain_completable(completable_future)
653
- on_completion! { completable_future.complete_with internal_state }
654
- end
655
-
656
- alias_method :tangle, :chain_completable
657
-
658
- # @yield [reason] executed only on parent failure
659
- # @return [Future]
660
- def rescue(executor = nil, &callback)
661
- RescuePromise.new(self, @DefaultExecutor, executor || @DefaultExecutor, &callback).future
662
- end
663
-
664
- # zips with the Future in the value
665
- # @example
666
- # Concurrent.future { Concurrent.future { 1 } }.flat.value # => 1
667
- def flat(level = 1)
668
- FlatPromise.new(self, level, @DefaultExecutor).future
669
- end
670
-
671
- # @return [Future] which has first completed value from futures
672
- def any(*futures)
673
- AnyCompletePromise.new([self, *futures], @DefaultExecutor).future
674
- end
675
-
676
- # Inserts delay into the chain of Futures making rest of it lazy evaluated.
677
- # @return [Future]
678
- def delay
679
- ZipFutureEventPromise.new(self, DelayPromise.new(@DefaultExecutor).future, @DefaultExecutor).future
680
- end
681
-
682
- # Schedules rest of the chain for execution with specified time or on specified time
683
- # @return [Future]
684
- def schedule(intended_time)
685
- chain do
686
- ZipFutureEventPromise.new(self,
687
- ScheduledPromise.new(@DefaultExecutor, intended_time).event,
688
- @DefaultExecutor).future
689
- end.flat
690
- end
691
-
692
- # Zips with selected value form the suplied channels
693
- # @return [Future]
694
- def then_select(*channels)
695
- ZipFuturesPromise.new([self, Concurrent.select(*channels)], @DefaultExecutor).future
696
- end
697
-
698
- # Changes default executor for rest of the chain
699
- # @return [Future]
700
- def with_default_executor(executor)
701
- FutureWrapperPromise.new(self, executor).future
702
- end
703
-
704
- # Zip with future producing new Future
705
- # @return [Future]
706
- def zip(other)
707
- if other.is_a?(Future)
708
- ZipFutureFuturePromise.new(self, other, @DefaultExecutor).future
709
- else
710
- ZipFutureEventPromise.new(self, other, @DefaultExecutor).future
711
- end
712
- end
713
-
714
- alias_method :&, :zip
715
-
716
- alias_method :|, :any
717
-
718
- # @note may block
719
- # @note only proof of concept
720
- def then_put(channel)
721
- on_success(:io) { |value| channel.put value }
722
- end
723
-
724
- # @yield [value] executed async on `executor` when success
725
- # @return self
726
- def on_success(executor = nil, &callback)
727
- add_callback :async_callback_on_success, executor || @DefaultExecutor, callback
728
- end
729
-
730
- # @yield [reason] executed async on `executor` when failed?
731
- # @return self
732
- def on_failure(executor = nil, &callback)
733
- add_callback :async_callback_on_failure, executor || @DefaultExecutor, callback
734
- end
735
-
736
- # @yield [value] executed sync when success
737
- # @return self
738
- def on_success!(&callback)
739
- add_callback :callback_on_success, callback
740
- end
741
-
742
- # @yield [reason] executed sync when failed?
743
- # @return self
744
- def on_failure!(&callback)
745
- add_callback :callback_on_failure, callback
746
- end
747
-
748
- # @!visibility private
749
- def complete_with(state, raise_on_reassign = true)
750
- if compare_and_set_internal_state(PENDING, state)
751
- # go to synchronized block only if there were waiting threads
752
- @Lock.synchronize { @Condition.broadcast } unless @Waiters.value == 0
753
- call_callbacks state
754
- else
755
- if raise_on_reassign
756
- log ERROR, 'Edge::Future', reason if reason # print otherwise hidden error
757
- raise(Concurrent::MultipleAssignmentError.new(
758
- "Future can be completed only once. Current result is #{result}, " +
759
- "trying to set #{state.result}"))
760
- end
761
- return false
762
- end
763
- self
764
- end
765
-
766
- # @!visibility private
767
- def add_callback(method, *args)
768
- state = internal_state
769
- if completed?(state)
770
- call_callback method, state, *args
771
- else
772
- @Callbacks.push [method, *args]
773
- state = internal_state
774
- # take back if it was completed in the meanwhile
775
- call_callbacks state if completed?(state)
776
- end
777
- self
778
- end
779
-
780
- # @!visibility private
781
- def apply(block)
782
- internal_state.apply block
783
- end
784
-
785
- private
786
-
787
- def wait_until_complete!(timeout = nil)
788
- result = wait_until_complete(timeout)
789
- raise self if failed?
790
- result
791
- end
792
-
793
- def call_callbacks(state)
794
- method, *args = @Callbacks.pop
795
- while method
796
- call_callback method, state, *args
797
- method, *args = @Callbacks.pop
798
- end
799
- end
800
-
801
- def call_callback(method, state, *args)
802
- self.send method, state, *args
803
- end
804
-
805
- def async_callback_on_success(state, executor, callback)
806
- with_async(executor, state, callback) do |st, cb|
807
- callback_on_success st, cb
808
- end
809
- end
810
-
811
- def async_callback_on_failure(state, executor, callback)
812
- with_async(executor, state, callback) do |st, cb|
813
- callback_on_failure st, cb
814
- end
815
- end
816
-
817
- def callback_on_success(state, callback)
818
- state.apply callback if state.success?
819
- end
820
-
821
- def callback_on_failure(state, callback)
822
- state.apply callback unless state.success?
823
- end
824
-
825
- def callback_on_completion(state, callback)
826
- callback.call state.result
827
- end
828
-
829
- def callback_notify_blocked(state, promise)
830
- super(promise)
831
- end
832
-
833
- def async_callback_on_completion(state, executor, callback)
834
- with_async(executor, state, callback) do |st, cb|
835
- callback_on_completion st, cb
836
- end
837
- end
838
-
839
- end
840
-
841
- # A Event which can be completed by user.
842
- class CompletableEvent < Event
843
- # Complete the Event, `raise` if already completed
844
- def complete(raise_on_reassign = true)
845
- complete_with COMPLETED, raise_on_reassign
846
- end
847
-
848
- def hide_completable
849
- EventWrapperPromise.new(self, @DefaultExecutor).event
850
- end
851
- end
852
-
853
- # A Future which can be completed by user.
854
- class CompletableFuture < Future
855
- # Complete the future with triplet od `success`, `value`, `reason`
856
- # `raise` if already completed
857
- # return [self]
858
- def complete(success, value, reason, raise_on_reassign = true)
859
- complete_with(success ? Success.new(value) : Failed.new(reason), raise_on_reassign)
860
- end
861
-
862
- # Complete the future with value
863
- # return [self]
864
- def success(value)
865
- promise.success(value)
866
- end
867
-
868
- # Try to complete the future with value
869
- # return [self]
870
- def try_success(value)
871
- promise.try_success(value)
872
- end
873
-
874
- # Fail the future with reason
875
- # return [self]
876
- def fail(reason = StandardError.new)
877
- promise.fail(reason)
878
- end
879
-
880
- # Try to fail the future with reason
881
- # return [self]
882
- def try_fail(reason = StandardError.new)
883
- promise.try_fail(reason)
884
- end
885
-
886
- # Evaluate the future to value if there is an exception the future fails with it
887
- # return [self]
888
- def evaluate_to(*args, &block)
889
- promise.evaluate_to(*args, block)
890
- end
891
-
892
- # Evaluate the future to value if there is an exception the future fails with it
893
- # @raise the exception
894
- # return [self]
895
- def evaluate_to!(*args, &block)
896
- promise.evaluate_to!(*args, block)
897
- end
898
-
899
- def hide_completable
900
- FutureWrapperPromise.new(self, @DefaultExecutor).future
901
- end
902
- end
903
-
904
- # @abstract
905
- # @!visibility private
906
- class AbstractPromise < Synchronization::Object
907
- safe_initialization!
908
- include Concern::Logging
909
-
910
- def initialize(future)
911
- super()
912
- @Future = future
913
- end
914
-
915
- def future
916
- @Future
917
- end
918
-
919
- alias_method :event, :future
920
-
921
- def default_executor
922
- future.default_executor
923
- end
924
-
925
- def state
926
- future.state
927
- end
928
-
929
- def touch
930
- end
931
-
932
- def to_s
933
- "<##{self.class}:0x#{'%x' % (object_id << 1)} #{state}>"
934
- end
935
-
936
- def inspect
937
- to_s
938
- end
939
-
940
- private
941
-
942
- def complete_with(new_state, raise_on_reassign = true)
943
- @Future.complete_with(new_state, raise_on_reassign)
944
- end
945
-
946
- # @return [Future]
947
- def evaluate_to(*args, block)
948
- complete_with Future::Success.new(block.call(*args))
949
- rescue StandardError => error
950
- complete_with Future::Failed.new(error)
951
- rescue Exception => error
952
- log(ERROR, 'Edge::Future', error)
953
- complete_with Future::Failed.new(error)
954
- end
955
- end
956
-
957
- # @!visibility private
958
- class CompletableEventPromise < AbstractPromise
959
- def initialize(default_executor)
960
- super CompletableEvent.new(self, default_executor)
961
- end
962
- end
963
-
964
- # @!visibility private
965
- class CompletableFuturePromise < AbstractPromise
966
- def initialize(default_executor)
967
- super CompletableFuture.new(self, default_executor)
968
- end
969
-
970
- # Set the `Future` to a value and wake or notify all threads waiting on it.
971
- #
972
- # @param [Object] value the value to store in the `Future`
973
- # @raise [Concurrent::MultipleAssignmentError] if the `Future` has already been set or otherwise completed
974
- # @return [Future]
975
- def success(value)
976
- complete_with Future::Success.new(value)
977
- end
978
-
979
- def try_success(value)
980
- !!complete_with(Future::Success.new(value), false)
981
- end
982
-
983
- # Set the `Future` to failed due to some error and wake or notify all threads waiting on it.
984
- #
985
- # @param [Object] reason for the failure
986
- # @raise [Concurrent::MultipleAssignmentError] if the `Future` has already been set or otherwise completed
987
- # @return [Future]
988
- def fail(reason = StandardError.new)
989
- complete_with Future::Failed.new(reason)
990
- end
991
-
992
- def try_fail(reason = StandardError.new)
993
- !!complete_with(Future::Failed.new(reason), false)
994
- end
995
-
996
- public :evaluate_to
997
-
998
- # @return [Future]
999
- def evaluate_to!(*args, block)
1000
- evaluate_to(*args, block).wait!
1001
- end
1002
- end
1003
-
1004
- # @abstract
1005
- # @!visibility private
1006
- class InnerPromise < AbstractPromise
1007
- end
1008
-
1009
- # @abstract
1010
- # @!visibility private
1011
- class BlockedPromise < InnerPromise
1012
- def self.new(*args, &block)
1013
- promise = super(*args, &block)
1014
- promise.blocked_by.each { |f| f.add_callback :callback_notify_blocked, promise }
1015
- promise
1016
- end
1017
-
1018
- def initialize(future, blocked_by_futures, countdown)
1019
- super(future)
1020
- initialize_blocked_by(blocked_by_futures)
1021
- @Countdown = AtomicFixnum.new countdown
1022
- end
1023
-
1024
- # @api private
1025
- def on_done(future)
1026
- countdown = process_on_done(future)
1027
- completable = completable?(countdown, future)
1028
-
1029
- if completable
1030
- on_completable(future)
1031
- # futures could be deleted from blocked_by one by one here, but that would be too expensive,
1032
- # it's done once when all are done to free the reference
1033
- clear_blocked_by!
1034
- end
1035
- end
1036
-
1037
- def touch
1038
- blocked_by.each(&:touch)
1039
- end
1040
-
1041
- # !visibility private
1042
- # for inspection only
1043
- def blocked_by
1044
- @BlockedBy
1045
- end
1046
-
1047
- def inspect
1048
- "#{to_s[0..-2]} blocked_by:[#{ blocked_by.map(&:to_s).join(', ')}]>"
1049
- end
1050
-
1051
- private
1052
-
1053
- def initialize_blocked_by(blocked_by_futures)
1054
- @BlockedBy = [blocked_by_futures].flatten
1055
- end
1056
-
1057
- def clear_blocked_by!
1058
- # not synchronized because we do not care when this change propagates
1059
- @BlockedBy = []
1060
- nil
1061
- end
1062
-
1063
- # @return [true,false] if completable
1064
- def completable?(countdown, future)
1065
- countdown.zero?
1066
- end
1067
-
1068
- def process_on_done(future)
1069
- @Countdown.decrement
1070
- end
1071
-
1072
- def on_completable(done_future)
1073
- raise NotImplementedError
1074
- end
1075
- end
1076
-
1077
- # @abstract
1078
- # @!visibility private
1079
- class BlockedTaskPromise < BlockedPromise
1080
- def initialize(blocked_by_future, default_executor, executor, &task)
1081
- raise ArgumentError, 'no block given' unless block_given?
1082
- super Future.new(self, default_executor), blocked_by_future, 1
1083
- @Executor = executor
1084
- @Task = task
1085
- end
1086
-
1087
- def executor
1088
- @Executor
1089
- end
1090
- end
1091
-
1092
- # @!visibility private
1093
- class ThenPromise < BlockedTaskPromise
1094
- private
1095
-
1096
- def initialize(blocked_by_future, default_executor, executor, &task)
1097
- raise ArgumentError, 'only Future can be appended with then' unless blocked_by_future.is_a? Future
1098
- super blocked_by_future, default_executor, executor, &task
1099
- end
1100
-
1101
- def on_completable(done_future)
1102
- if done_future.success?
1103
- Concurrent.post_on(@Executor, done_future, @Task) do |future, task|
1104
- evaluate_to lambda { future.apply task }
1105
- end
1106
- else
1107
- complete_with done_future.internal_state
1108
- end
1109
- end
1110
- end
1111
-
1112
- # @!visibility private
1113
- class RescuePromise < BlockedTaskPromise
1114
- private
1115
-
1116
- def initialize(blocked_by_future, default_executor, executor, &task)
1117
- super blocked_by_future, default_executor, executor, &task
1118
- end
1119
-
1120
- def on_completable(done_future)
1121
- if done_future.failed?
1122
- Concurrent.post_on(@Executor, done_future, @Task) do |future, task|
1123
- evaluate_to lambda { future.apply task }
1124
- end
1125
- else
1126
- complete_with done_future.internal_state
1127
- end
1128
- end
1129
- end
1130
-
1131
- # @!visibility private
1132
- class ChainPromise < BlockedTaskPromise
1133
- private
1134
-
1135
- def on_completable(done_future)
1136
- if Future === done_future
1137
- Concurrent.post_on(@Executor, done_future, @Task) { |future, task| evaluate_to(*future.result, task) }
1138
- else
1139
- Concurrent.post_on(@Executor, @Task) { |task| evaluate_to task }
1140
- end
1141
- end
1142
- end
1143
-
1144
- # will be immediately completed
1145
- # @!visibility private
1146
- class ImmediateEventPromise < InnerPromise
1147
- def initialize(default_executor)
1148
- super Event.new(self, default_executor).complete_with(Event::COMPLETED)
1149
- end
1150
- end
1151
-
1152
- # @!visibility private
1153
- class ImmediateFuturePromise < InnerPromise
1154
- def initialize(default_executor, success, value, reason)
1155
- super Future.new(self, default_executor).
1156
- complete_with(success ? Future::Success.new(value) : Future::Failed.new(reason))
1157
- end
1158
- end
1159
-
1160
- # @!visibility private
1161
- class FlatPromise < BlockedPromise
1162
-
1163
- # !visibility private
1164
- def blocked_by
1165
- @BlockedBy.each.to_a
1166
- end
1167
-
1168
- private
1169
-
1170
- def process_on_done(future)
1171
- countdown = super(future)
1172
- if countdown.nonzero?
1173
- internal_state = future.internal_state
1174
-
1175
- unless internal_state.success?
1176
- complete_with internal_state
1177
- return countdown
1178
- end
1179
-
1180
- value = internal_state.value
1181
- case value
1182
- when Future
1183
- value.touch if self.future.touched
1184
- @BlockedBy.push value
1185
- value.add_callback :callback_notify_blocked, self
1186
- @Countdown.value
1187
- when Event
1188
- evaluate_to(lambda { raise TypeError, 'cannot flatten to Event' })
1189
- else
1190
- evaluate_to(lambda { raise TypeError, "returned value #{value.inspect} is not a Future" })
1191
- end
1192
- end
1193
- countdown
1194
- end
1195
-
1196
- def initialize(blocked_by_future, levels, default_executor)
1197
- raise ArgumentError, 'levels has to be higher than 0' if levels < 1
1198
- super Future.new(self, default_executor), blocked_by_future, 1 + levels
1199
- end
1200
-
1201
- def initialize_blocked_by(blocked_by_future)
1202
- @BlockedBy = LockFreeStack.new.push(blocked_by_future)
1203
- end
1204
-
1205
- def on_completable(done_future)
1206
- complete_with done_future.internal_state
1207
- end
1208
-
1209
- def clear_blocked_by!
1210
- @BlockedBy.clear
1211
- nil
1212
- end
1213
-
1214
- def completable?(countdown, future)
1215
- !@Future.internal_state.completed? && super(countdown, future)
1216
- end
1217
- end
1218
-
1219
- # @!visibility private
1220
- class ZipEventEventPromise < BlockedPromise
1221
- def initialize(event1, event2, default_executor)
1222
- super Event.new(self, default_executor), [event1, event2], 2
1223
- end
1224
-
1225
- def on_completable(done_future)
1226
- complete_with Event::COMPLETED
1227
- end
1228
- end
1229
-
1230
- # @!visibility private
1231
- class ZipFutureEventPromise < BlockedPromise
1232
- def initialize(future, event, default_executor)
1233
- super Future.new(self, default_executor), [future, event], 2
1234
- @FutureResult = future
1235
- end
1236
-
1237
- def on_completable(done_future)
1238
- complete_with @FutureResult.internal_state
1239
- end
1240
- end
1241
-
1242
- # @!visibility private
1243
- class ZipFutureFuturePromise < BlockedPromise
1244
- def initialize(future1, future2, default_executor)
1245
- super Future.new(self, default_executor), [future1, future2], 2
1246
- @Future1Result = future1
1247
- @Future2Result = future2
1248
- end
1249
-
1250
- def on_completable(done_future)
1251
- success1, value1, reason1 = @Future1Result.result
1252
- success2, value2, reason2 = @Future2Result.result
1253
- success = success1 && success2
1254
- new_state = if success
1255
- Future::SuccessArray.new([value1, value2])
1256
- else
1257
- Future::PartiallyFailed.new([value1, value2], [reason1, reason2])
1258
- end
1259
- complete_with new_state
1260
- end
1261
- end
1262
-
1263
- # @!visibility private
1264
- class EventWrapperPromise < BlockedPromise
1265
- def initialize(event, default_executor)
1266
- super Event.new(self, default_executor), event, 1
1267
- end
1268
-
1269
- def on_completable(done_future)
1270
- complete_with Event::COMPLETED
1271
- end
1272
- end
1273
-
1274
- # @!visibility private
1275
- class FutureWrapperPromise < BlockedPromise
1276
- def initialize(future, default_executor)
1277
- super Future.new(self, default_executor), future, 1
1278
- end
1279
-
1280
- def on_completable(done_future)
1281
- complete_with done_future.internal_state
1282
- end
1283
- end
1284
-
1285
- # @!visibility private
1286
- class ZipFuturesPromise < BlockedPromise
1287
-
1288
- private
1289
-
1290
- def initialize(blocked_by_futures, default_executor)
1291
- super(Future.new(self, default_executor), blocked_by_futures, blocked_by_futures.size)
1292
-
1293
- on_completable nil if blocked_by_futures.empty?
1294
- end
1295
-
1296
- def on_completable(done_future)
1297
- all_success = true
1298
- values = Array.new(blocked_by.size)
1299
- reasons = Array.new(blocked_by.size)
1300
-
1301
- blocked_by.each_with_index do |future, i|
1302
- if future.is_a?(Future)
1303
- success, values[i], reasons[i] = future.result
1304
- all_success &&= success
1305
- else
1306
- values[i] = reasons[i] = nil
1307
- end
1308
- end
1309
-
1310
- if all_success
1311
- complete_with Future::SuccessArray.new(values)
1312
- else
1313
- complete_with Future::PartiallyFailed.new(values, reasons)
1314
- end
1315
- end
1316
- end
1317
-
1318
- # @!visibility private
1319
- class ZipEventsPromise < BlockedPromise
1320
-
1321
- private
1322
-
1323
- def initialize(blocked_by_futures, default_executor)
1324
- super(Event.new(self, default_executor), blocked_by_futures, blocked_by_futures.size)
1325
-
1326
- on_completable nil if blocked_by_futures.empty?
1327
- end
1328
-
1329
- def on_completable(done_future)
1330
- complete_with Event::COMPLETED
1331
- end
1332
- end
1333
-
1334
- # @!visibility private
1335
- class AnyCompletePromise < BlockedPromise
1336
-
1337
- private
1338
-
1339
- def initialize(blocked_by_futures, default_executor)
1340
- blocked_by_futures.all? { |f| f.is_a? Future } or
1341
- raise ArgumentError, 'accepts only Futures not Events'
1342
- super(Future.new(self, default_executor), blocked_by_futures, blocked_by_futures.size)
1343
- end
1344
-
1345
- def completable?(countdown, future)
1346
- true
1347
- end
1348
-
1349
- def on_completable(done_future)
1350
- complete_with done_future.internal_state, false
1351
- end
1352
- end
1353
-
1354
- # @!visibility private
1355
- class AnySuccessfulPromise < BlockedPromise
1356
-
1357
- private
1358
-
1359
- def initialize(blocked_by_futures, default_executor)
1360
- blocked_by_futures.all? { |f| f.is_a? Future } or
1361
- raise ArgumentError, 'accepts only Futures not Events'
1362
- super(Future.new(self, default_executor), blocked_by_futures, blocked_by_futures.size)
1363
- end
1364
-
1365
- def completable?(countdown, future)
1366
- future.success? || super(countdown, future)
1367
- end
1368
-
1369
- def on_completable(done_future)
1370
- complete_with done_future.internal_state, false
1371
- end
1372
- end
1373
-
1374
- # @!visibility private
1375
- class DelayPromise < InnerPromise
1376
- def touch
1377
- @Future.complete_with Event::COMPLETED
1378
- end
1379
-
1380
- private
1381
-
1382
- def initialize(default_executor)
1383
- super Event.new(self, default_executor)
1384
- end
1385
- end
1386
-
1387
- # will be evaluated to task in intended_time
1388
- # @!visibility private
1389
- class ScheduledPromise < InnerPromise
1390
- def intended_time
1391
- @IntendedTime
1392
- end
1393
-
1394
- def inspect
1395
- "#{to_s[0..-2]} intended_time:[#{@IntendedTime}}>"
1396
- end
1397
-
1398
- private
1399
-
1400
- def initialize(default_executor, intended_time)
1401
- super Event.new(self, default_executor)
1402
-
1403
- @IntendedTime = intended_time
1404
-
1405
- in_seconds = begin
1406
- now = Time.now
1407
- schedule_time = if @IntendedTime.is_a? Time
1408
- @IntendedTime
1409
- else
1410
- now + @IntendedTime
1411
- end
1412
- [0, schedule_time.to_f - now.to_f].max
1413
- end
1414
-
1415
- Concurrent.global_timer_set.post(in_seconds) do
1416
- @Future.complete_with Event::COMPLETED
1417
- end
1418
- end
1419
- end
1420
- end
1421
- end
1422
-
1423
- Concurrent::Edge.send :extend, Concurrent::Edge::FutureShortcuts
1424
- Concurrent::Edge.send :include, Concurrent::Edge::FutureShortcuts
1425
-
1426
- Concurrent.send :extend, Concurrent::Edge::FutureShortcuts
1427
- Concurrent.send :include, Concurrent::Edge::FutureShortcuts