concurrent-ruby-edge 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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