concurrent-ruby-edge 0.1.0.pre2 → 0.1.0.pre3
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.
- checksums.yaml +4 -4
- data/lib/concurrent/actor/utils/pool.rb +4 -4
- data/lib/concurrent/agent.rb +1 -9
- data/lib/concurrent/edge/future.rb +328 -156
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3b0a6f42dd53529fb35370f6d9d6b1957aa576c
|
4
|
+
data.tar.gz: b960e25b2834254aaa00685cf38e41fc3a481e9f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aec6112c612dbfa9f709181cef273ba892ae5db92915875e7750d18e159c821e613f5c02520530eb27805e60531e55e68021ac003279a94b8d78b63ef184948e
|
7
|
+
data.tar.gz: bb59836fc4e5da39fc2c3f7885d1a123f0a9100850c9c486871830d9421c5b2e0756f45e9f7c7dee7f538adfd030f765b3a54fa33fd94726c13e05b8d08b4977
|
@@ -12,14 +12,14 @@ module Concurrent
|
|
12
12
|
# @yieldparam [Integer] index of the worker, usually used in its name
|
13
13
|
# @yieldreturn [Reference] the reference of newly created worker
|
14
14
|
# @example
|
15
|
-
# class Worker < Concurrent::Actor::
|
16
|
-
# def
|
15
|
+
# class Worker < Concurrent::Actor::RestartingContext
|
16
|
+
# def on_message(message)
|
17
17
|
# p message * 5
|
18
18
|
# end
|
19
19
|
# end
|
20
20
|
#
|
21
|
-
# pool = Concurrent::Actor::Utils::Pool.spawn! 'pool', 5 do |
|
22
|
-
# Worker.spawn name: "worker-#{index}", supervise: true, args: [
|
21
|
+
# pool = Concurrent::Actor::Utils::Pool.spawn! 'pool', 5 do |index|
|
22
|
+
# Worker.spawn name: "worker-#{index}", supervise: true, args: []
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
# pool << 'asd' << 2
|
data/lib/concurrent/agent.rb
CHANGED
@@ -93,15 +93,7 @@ module Concurrent
|
|
93
93
|
#
|
94
94
|
# @param [Object] initial the initial value
|
95
95
|
#
|
96
|
-
# @!macro
|
97
|
-
#
|
98
|
-
# @param [Hash] opts the options used to define the behavior at update and deref
|
99
|
-
# and to specify the executor on which to perform actions
|
100
|
-
# @option opts [Executor] :executor when set use the given `Executor` instance.
|
101
|
-
# Three special values are also supported: `:task` returns the global task pool,
|
102
|
-
# `:operation` returns the global operation pool, and `:immediate` returns a new
|
103
|
-
# `ImmediateExecutor` object.
|
104
|
-
# @!macro deref_options
|
96
|
+
# @!macro executor_and_deref_options
|
105
97
|
def initialize(initial, opts = {})
|
106
98
|
@value = initial
|
107
99
|
@rescuers = []
|
@@ -31,42 +31,38 @@ module Concurrent
|
|
31
31
|
# @overload future(default_executor = :io)
|
32
32
|
# User is responsible for completing the future once by {Edge::CompletableFuture#success} or {Edge::CompletableFuture#fail}
|
33
33
|
# @return [CompletableFuture]
|
34
|
-
def future(
|
35
|
-
future_on :io, *args, &task
|
36
|
-
end
|
37
|
-
|
38
|
-
def future_on(default_executor, *args, &task)
|
34
|
+
def future(default_executor = :io, &task)
|
39
35
|
if task
|
40
|
-
|
36
|
+
ImmediateEventPromise.new(default_executor).future.then(&task)
|
41
37
|
else
|
42
38
|
CompletableFuturePromise.new(default_executor).future
|
43
39
|
end
|
44
40
|
end
|
45
41
|
|
42
|
+
# @return [Future] which is already completed with value
|
43
|
+
def completed_future(value, default_executor = :io)
|
44
|
+
ImmediateFuturePromise.new(default_executor, value).future
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Event] which is already completed
|
48
|
+
def completed_event(default_executor = :io)
|
49
|
+
ImmediateEventPromise.new(default_executor).event
|
50
|
+
end
|
51
|
+
|
46
52
|
alias_method :async, :future
|
47
53
|
|
48
54
|
# Constructs new Future which will evaluate to the block after
|
49
55
|
# requested by calling `#wait`, `#value`, `#value!`, etc. on it or on any of the chained futures.
|
50
56
|
# @return [Future]
|
51
|
-
def delay(
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
# @return [Future]
|
56
|
-
def delay_on(default_executor, *args, &task)
|
57
|
-
Delay.new(default_executor, *args).future.then(&task)
|
57
|
+
def delay(default_executor = :io, &task)
|
58
|
+
Delay.new(default_executor).future.then(&task)
|
58
59
|
end
|
59
60
|
|
60
61
|
# Schedules the block to be executed on executor in given intended_time.
|
61
62
|
# @param [Numeric, Time] intended_time Numeric => run in `intended_time` seconds. Time => eun on time.
|
62
63
|
# @return [Future]
|
63
|
-
def schedule(intended_time,
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
# @return [Future]
|
68
|
-
def schedule_on(default_executor, intended_time, *args, &task)
|
69
|
-
ScheduledPromise.new(default_executor, intended_time, *args).future.then(&task)
|
64
|
+
def schedule(intended_time, default_executor = :io, &task)
|
65
|
+
ScheduledPromise.new(default_executor, intended_time).future.then(&task)
|
70
66
|
end
|
71
67
|
|
72
68
|
# Constructs new {Future} which is completed after all futures are complete. Its value is array
|
@@ -74,7 +70,7 @@ module Concurrent
|
|
74
70
|
# @param [Event] futures
|
75
71
|
# @return [Future]
|
76
72
|
def zip(*futures)
|
77
|
-
|
73
|
+
ZipPromise.new(futures, :io).future
|
78
74
|
end
|
79
75
|
|
80
76
|
# Constructs new {Future} which is completed after first of the futures is complete.
|
@@ -121,6 +117,7 @@ module Concurrent
|
|
121
117
|
class Event < Synchronization::Object
|
122
118
|
include Concern::Deprecation
|
123
119
|
|
120
|
+
# @!visibility private
|
124
121
|
class State
|
125
122
|
def completed?
|
126
123
|
raise NotImplementedError
|
@@ -131,6 +128,7 @@ module Concurrent
|
|
131
128
|
end
|
132
129
|
end
|
133
130
|
|
131
|
+
# @!visibility private
|
134
132
|
class Pending < State
|
135
133
|
def completed?
|
136
134
|
false
|
@@ -141,6 +139,7 @@ module Concurrent
|
|
141
139
|
end
|
142
140
|
end
|
143
141
|
|
142
|
+
# @!visibility private
|
144
143
|
class Completed < State
|
145
144
|
def completed?
|
146
145
|
true
|
@@ -151,7 +150,9 @@ module Concurrent
|
|
151
150
|
end
|
152
151
|
end
|
153
152
|
|
153
|
+
# @!visibility private
|
154
154
|
PENDING = Pending.new
|
155
|
+
# @!visibility private
|
155
156
|
COMPLETED = Completed.new
|
156
157
|
|
157
158
|
def initialize(promise, default_executor)
|
@@ -159,7 +160,7 @@ module Concurrent
|
|
159
160
|
@DefaultExecutor = default_executor
|
160
161
|
@Touched = AtomicBoolean.new(false)
|
161
162
|
@Callbacks = LockFreeStack.new
|
162
|
-
@Waiters = LockFreeStack.new
|
163
|
+
@Waiters = LockFreeStack.new # TODO replace with AtomicFixnum, avoid aba problem
|
163
164
|
@State = AtomicReference.new PENDING
|
164
165
|
super()
|
165
166
|
ensure_ivar_visibility!
|
@@ -192,11 +193,14 @@ module Concurrent
|
|
192
193
|
|
193
194
|
# Wait until Event is #complete?
|
194
195
|
# @param [Numeric] timeout the maximum time in second to wait.
|
195
|
-
# @return [Event] self
|
196
|
+
# @return [Event, true, false] self or true/false if timeout is used
|
197
|
+
# @!macro [attach] edge.periodical_wait
|
198
|
+
# @note a thread should wait only once! For repeated checking use faster `completed?` check.
|
199
|
+
# If thread waits periodically it will dangerously grow the waiters stack.
|
196
200
|
def wait(timeout = nil)
|
197
201
|
touch
|
198
|
-
wait_until_complete
|
199
|
-
self
|
202
|
+
result = wait_until_complete(timeout)
|
203
|
+
timeout ? result : self
|
200
204
|
end
|
201
205
|
|
202
206
|
# @!visibility private
|
@@ -219,31 +223,44 @@ module Concurrent
|
|
219
223
|
|
220
224
|
alias_method :then, :chain
|
221
225
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
226
|
+
def chain_completable(completable_event)
|
227
|
+
on_completion! { completable_event.complete_with COMPLETED }
|
228
|
+
end
|
229
|
+
|
230
|
+
alias_method :tangle, :chain_completable
|
231
|
+
|
232
|
+
# Zip with future producing new Future
|
233
|
+
# @return [Event]
|
234
|
+
def zip(other)
|
235
|
+
if other.is?(Future)
|
236
|
+
ZipFutureEventPromise.new(other, self, @DefaultExecutor).future
|
237
|
+
else
|
238
|
+
ZipEventEventPromise.new(self, other, @DefaultExecutor).future
|
239
|
+
end
|
227
240
|
end
|
228
241
|
|
229
242
|
alias_method :&, :zip
|
230
243
|
|
231
244
|
# Inserts delay into the chain of Futures making rest of it lazy evaluated.
|
232
|
-
# @return [
|
245
|
+
# @return [Event]
|
233
246
|
def delay
|
234
|
-
|
247
|
+
ZipEventEventPromise.new(self, Delay.new(@DefaultExecutor).event, @DefaultExecutor).event
|
235
248
|
end
|
236
249
|
|
237
|
-
# Schedules rest of the chain for execution with specified time or on specified time
|
238
|
-
# @return [
|
239
|
-
def schedule(intended_time)
|
240
|
-
|
241
|
-
|
250
|
+
# # Schedules rest of the chain for execution with specified time or on specified time
|
251
|
+
# # @return [Event]
|
252
|
+
# def schedule(intended_time)
|
253
|
+
# chain do
|
254
|
+
# ZipEventEventPromise.new(self,
|
255
|
+
# ScheduledPromise.new(@DefaultExecutor, intended_time).event,
|
256
|
+
# @DefaultExecutor).event
|
257
|
+
# end.flat
|
258
|
+
# end
|
242
259
|
|
243
260
|
# Zips with selected value form the suplied channels
|
244
261
|
# @return [Future]
|
245
262
|
def then_select(*channels)
|
246
|
-
|
263
|
+
ZipFutureEventPromise(Concurrent.select(*channels), self, @DefaultExecutor).future
|
247
264
|
end
|
248
265
|
|
249
266
|
# @yield [success, value, reason] executed async on `executor` when completed
|
@@ -259,9 +276,9 @@ module Concurrent
|
|
259
276
|
end
|
260
277
|
|
261
278
|
# Changes default executor for rest of the chain
|
262
|
-
# @return [
|
279
|
+
# @return [Event]
|
263
280
|
def with_default_executor(executor)
|
264
|
-
|
281
|
+
EventWrapperPromise.new(self, executor).future
|
265
282
|
end
|
266
283
|
|
267
284
|
def to_s
|
@@ -278,13 +295,14 @@ module Concurrent
|
|
278
295
|
end
|
279
296
|
|
280
297
|
# @!visibility private
|
281
|
-
def
|
282
|
-
if
|
298
|
+
def complete_with(state, raise_on_reassign = true)
|
299
|
+
if @State.compare_and_set(PENDING, state)
|
300
|
+
(state)
|
283
301
|
# go to synchronized block only if there were waiting threads
|
284
302
|
synchronize { ns_broadcast } if @Waiters.clear
|
285
303
|
call_callbacks
|
286
304
|
else
|
287
|
-
Concurrent::MultipleAssignmentError.new('
|
305
|
+
Concurrent::MultipleAssignmentError.new('Event can be completed only once') if raise_on_reassign
|
288
306
|
return false
|
289
307
|
end
|
290
308
|
self
|
@@ -328,31 +346,32 @@ module Concurrent
|
|
328
346
|
@Touched.value
|
329
347
|
end
|
330
348
|
|
349
|
+
# @!visibility private
|
331
350
|
# only for debugging inspection
|
332
351
|
def waiting_threads
|
333
352
|
@Waiters.each.to_a
|
334
353
|
end
|
335
354
|
|
355
|
+
# @!visibility private
|
356
|
+
def internal_state
|
357
|
+
@State.get
|
358
|
+
end
|
359
|
+
|
336
360
|
private
|
337
361
|
|
362
|
+
# @return [true, false]
|
338
363
|
def wait_until_complete(timeout)
|
339
364
|
while true
|
340
365
|
last_waiter = @Waiters.peek # waiters' state before completion
|
341
|
-
|
366
|
+
return true if completed?
|
342
367
|
|
343
368
|
# synchronize so it cannot be signaled before it waits
|
344
369
|
synchronize do
|
345
370
|
# ok only if completing thread did not start signaling
|
346
371
|
next unless @Waiters.compare_and_push last_waiter, Thread.current
|
347
|
-
ns_wait_until(timeout) { completed? }
|
348
|
-
break
|
372
|
+
return ns_wait_until(timeout) { completed? }
|
349
373
|
end
|
350
374
|
end
|
351
|
-
self
|
352
|
-
end
|
353
|
-
|
354
|
-
def complete_state
|
355
|
-
COMPLETED if @State.compare_and_set(PENDING, COMPLETED)
|
356
375
|
end
|
357
376
|
|
358
377
|
def pr_with_async(executor, *args, &block)
|
@@ -386,6 +405,7 @@ module Concurrent
|
|
386
405
|
|
387
406
|
# Represents a value which will become available in future. May fail with a reason instead.
|
388
407
|
class Future < Event
|
408
|
+
# @!visibility private
|
389
409
|
class CompletedWithResult < Completed
|
390
410
|
def result
|
391
411
|
[success?, value, reason]
|
@@ -404,6 +424,7 @@ module Concurrent
|
|
404
424
|
end
|
405
425
|
end
|
406
426
|
|
427
|
+
# @!visibility private
|
407
428
|
class Success < CompletedWithResult
|
408
429
|
def initialize(value)
|
409
430
|
@Value = value
|
@@ -430,12 +451,14 @@ module Concurrent
|
|
430
451
|
end
|
431
452
|
end
|
432
453
|
|
454
|
+
# @!visibility private
|
433
455
|
class SuccessArray < Success
|
434
456
|
def apply(block)
|
435
457
|
block.call *value
|
436
458
|
end
|
437
459
|
end
|
438
460
|
|
461
|
+
# @!visibility private
|
439
462
|
class Failed < CompletedWithResult
|
440
463
|
def initialize(reason)
|
441
464
|
@Reason = reason
|
@@ -456,6 +479,39 @@ module Concurrent
|
|
456
479
|
def to_sym
|
457
480
|
:failed
|
458
481
|
end
|
482
|
+
|
483
|
+
def apply(block)
|
484
|
+
block.call reason
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
# @!visibility private
|
489
|
+
class PartiallyFailed < CompletedWithResult
|
490
|
+
def initialize(value, reason)
|
491
|
+
@Value = value
|
492
|
+
@Reason = reason
|
493
|
+
super()
|
494
|
+
end
|
495
|
+
|
496
|
+
def success?
|
497
|
+
false
|
498
|
+
end
|
499
|
+
|
500
|
+
def to_sym
|
501
|
+
:failed
|
502
|
+
end
|
503
|
+
|
504
|
+
def value
|
505
|
+
@Value
|
506
|
+
end
|
507
|
+
|
508
|
+
def reason
|
509
|
+
@Reason
|
510
|
+
end
|
511
|
+
|
512
|
+
def apply(block)
|
513
|
+
block.call *reason
|
514
|
+
end
|
459
515
|
end
|
460
516
|
|
461
517
|
# @!method state
|
@@ -464,7 +520,7 @@ module Concurrent
|
|
464
520
|
# Has Future been success?
|
465
521
|
# @return [Boolean]
|
466
522
|
def success?(state = @State.get)
|
467
|
-
state.success?
|
523
|
+
state.completed? && state.success?
|
468
524
|
end
|
469
525
|
|
470
526
|
def fulfilled?
|
@@ -475,7 +531,7 @@ module Concurrent
|
|
475
531
|
# Has Future been failed?
|
476
532
|
# @return [Boolean]
|
477
533
|
def failed?(state = @State.get)
|
478
|
-
!success?
|
534
|
+
state.completed? && !state.success?
|
479
535
|
end
|
480
536
|
|
481
537
|
def rejected?
|
@@ -483,52 +539,65 @@ module Concurrent
|
|
483
539
|
failed?
|
484
540
|
end
|
485
541
|
|
486
|
-
# @return [Object] the value of the Future when success
|
542
|
+
# @return [Object, nil] the value of the Future when success, nil on timeout
|
543
|
+
# @!macro [attach] edge.timeout_nil
|
544
|
+
# @note If the Future can have value `nil` then it cannot be distinquished from `nil` returned on timeout.
|
545
|
+
# In this case is better to use first `wait` then `value` (or similar).
|
546
|
+
# @!macro edge.periodical_wait
|
487
547
|
def value(timeout = nil)
|
488
548
|
touch
|
489
|
-
wait_until_complete timeout
|
490
|
-
@State.get.value
|
549
|
+
@State.get.value if wait_until_complete timeout
|
491
550
|
end
|
492
551
|
|
493
|
-
# @return [Exception] the reason of the Future's failure
|
552
|
+
# @return [Exception, nil] the reason of the Future's failure
|
553
|
+
# @!macro edge.timeout_nil
|
554
|
+
# @!macro edge.periodical_wait
|
494
555
|
def reason(timeout = nil)
|
495
556
|
touch
|
496
|
-
wait_until_complete timeout
|
497
|
-
@State.get.reason
|
557
|
+
@State.get.reason if wait_until_complete timeout
|
498
558
|
end
|
499
559
|
|
500
|
-
# @return [Array(Boolean, Object, Exception)] triplet of success, value, reason
|
560
|
+
# @return [Array(Boolean, Object, Exception), nil] triplet of success, value, reason
|
561
|
+
# @!macro edge.timeout_nil
|
562
|
+
# @!macro edge.periodical_wait
|
501
563
|
def result(timeout = nil)
|
502
564
|
touch
|
503
|
-
wait_until_complete timeout
|
504
|
-
@State.get.result
|
565
|
+
@State.get.result if wait_until_complete timeout
|
505
566
|
end
|
506
567
|
|
507
568
|
# Wait until Future is #complete?
|
508
569
|
# @param [Numeric] timeout the maximum time in second to wait.
|
509
570
|
# @raise reason on failure
|
510
|
-
# @return [Event] self
|
571
|
+
# @return [Event, true, false] self or true/false if timeout is used
|
572
|
+
# @!macro edge.periodical_wait
|
511
573
|
def wait!(timeout = nil)
|
512
574
|
touch
|
513
|
-
wait_until_complete!
|
575
|
+
result = wait_until_complete!(timeout)
|
576
|
+
timeout ? result : self
|
514
577
|
end
|
515
578
|
|
516
579
|
# Wait until Future is #complete?
|
517
580
|
# @param [Numeric] timeout the maximum time in second to wait.
|
518
581
|
# @raise reason on failure
|
519
|
-
# @return [Object]
|
582
|
+
# @return [Object, nil]
|
583
|
+
# @!macro edge.timeout_nil
|
584
|
+
# @!macro edge.periodical_wait
|
520
585
|
def value!(timeout = nil)
|
521
586
|
touch
|
522
|
-
wait_until_complete!
|
523
|
-
@State.get.value
|
587
|
+
@State.get.value if wait_until_complete! timeout
|
524
588
|
end
|
525
589
|
|
526
590
|
# @example allows failed Future to be risen
|
527
591
|
# raise Concurrent.future.fail
|
528
592
|
def exception(*args)
|
529
|
-
touch
|
530
593
|
raise 'obligation is not failed' unless failed?
|
531
|
-
@State.get.reason
|
594
|
+
reason = @State.get.reason
|
595
|
+
if reason.is_a?(Array)
|
596
|
+
reason.each { |e| log Error, 'Edge::Future', e }
|
597
|
+
Concurrent::Error.new 'multiple exceptions, inspect log'
|
598
|
+
else
|
599
|
+
reason.exception(*args)
|
600
|
+
end
|
532
601
|
end
|
533
602
|
|
534
603
|
# @yield [value] executed only on parent success
|
@@ -543,6 +612,12 @@ module Concurrent
|
|
543
612
|
self.then { |v| actor.ask(v) }.flat
|
544
613
|
end
|
545
614
|
|
615
|
+
def chain_completable(completable_future)
|
616
|
+
on_completion! { completable_future.complete_with internal_state }
|
617
|
+
end
|
618
|
+
|
619
|
+
alias_method :tangle, :chain_completable
|
620
|
+
|
546
621
|
# @yield [reason] executed only on parent failure
|
547
622
|
# @return [Future]
|
548
623
|
def rescue(executor = nil, &callback)
|
@@ -553,7 +628,7 @@ module Concurrent
|
|
553
628
|
# @example
|
554
629
|
# Concurrent.future { Concurrent.future { 1 } }.flat.vale # => 1
|
555
630
|
def flat(level = 1)
|
556
|
-
|
631
|
+
FlatPromise.new(self, level, @DefaultExecutor).future
|
557
632
|
end
|
558
633
|
|
559
634
|
# @return [Future] which has first completed value from futures
|
@@ -561,11 +636,52 @@ module Concurrent
|
|
561
636
|
AnyPromise.new([self, *futures], @DefaultExecutor).future
|
562
637
|
end
|
563
638
|
|
639
|
+
# Inserts delay into the chain of Futures making rest of it lazy evaluated.
|
640
|
+
# @return [Future]
|
641
|
+
def delay
|
642
|
+
ZipFutureEventPromise.new(self, Delay.new(@DefaultExecutor).future, @DefaultExecutor).future
|
643
|
+
end
|
644
|
+
|
645
|
+
# Schedules rest of the chain for execution with specified time or on specified time
|
646
|
+
# @return [Future]
|
647
|
+
def schedule(intended_time)
|
648
|
+
chain do
|
649
|
+
ZipFutureEventPromise.new(self,
|
650
|
+
ScheduledPromise.new(@DefaultExecutor, intended_time).event,
|
651
|
+
@DefaultExecutor).future
|
652
|
+
end.flat
|
653
|
+
end
|
654
|
+
|
655
|
+
# Zips with selected value form the suplied channels
|
656
|
+
# @return [Future]
|
657
|
+
def then_select(*channels)
|
658
|
+
ZipPromise.new([self, Concurrent.select(*channels)], @DefaultExecutor).future
|
659
|
+
end
|
660
|
+
|
661
|
+
# Changes default executor for rest of the chain
|
662
|
+
# @return [Future]
|
663
|
+
def with_default_executor(executor)
|
664
|
+
FutureWrapperPromise.new(self, executor).future
|
665
|
+
end
|
666
|
+
|
667
|
+
# Zip with future producing new Future
|
668
|
+
# @return [Future]
|
669
|
+
def zip(other)
|
670
|
+
if other.is_a?(Future)
|
671
|
+
ZipFutureFuturePromise.new(self, other, @DefaultExecutor).future
|
672
|
+
else
|
673
|
+
ZipFutureEventPromise.new(self, other, @DefaultExecutor).future
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
alias_method :&, :zip
|
678
|
+
|
564
679
|
alias_method :|, :any
|
565
680
|
|
566
681
|
# only proof of concept
|
682
|
+
# @note may block
|
567
683
|
def then_push(channel)
|
568
|
-
on_success { |value| channel.push value }
|
684
|
+
on_success { |value| channel.push value }
|
569
685
|
end
|
570
686
|
|
571
687
|
# @yield [value] executed async on `executor` when success
|
@@ -593,13 +709,18 @@ module Concurrent
|
|
593
709
|
end
|
594
710
|
|
595
711
|
# @!visibility private
|
596
|
-
def
|
597
|
-
if (
|
712
|
+
def complete_with(state, raise_on_reassign = true)
|
713
|
+
if @State.compare_and_set(PENDING, state)
|
598
714
|
@Waiters.clear
|
599
715
|
synchronize { ns_broadcast }
|
600
|
-
call_callbacks
|
716
|
+
call_callbacks state
|
601
717
|
else
|
602
|
-
|
718
|
+
if raise_on_reassign
|
719
|
+
log ERROR, 'Edge::Future', reason if reason # print otherwise hidden error
|
720
|
+
raise(Concurrent::MultipleAssignmentError.new(
|
721
|
+
"Future can be completed only once. Current result is #{result}, " +
|
722
|
+
"trying to set #{state.result}"))
|
723
|
+
end
|
603
724
|
return false
|
604
725
|
end
|
605
726
|
self
|
@@ -627,22 +748,9 @@ module Concurrent
|
|
627
748
|
private
|
628
749
|
|
629
750
|
def wait_until_complete!(timeout = nil)
|
630
|
-
wait_until_complete(timeout)
|
751
|
+
result = wait_until_complete(timeout)
|
631
752
|
raise self if failed?
|
632
|
-
|
633
|
-
end
|
634
|
-
|
635
|
-
def complete_state(success, value, reason)
|
636
|
-
new_state = if success
|
637
|
-
if value.is_a?(Array)
|
638
|
-
SuccessArray.new(value)
|
639
|
-
else
|
640
|
-
Success.new(value)
|
641
|
-
end
|
642
|
-
else
|
643
|
-
Failed.new(reason)
|
644
|
-
end
|
645
|
-
new_state if @State.compare_and_set(PENDING, new_state)
|
753
|
+
result
|
646
754
|
end
|
647
755
|
|
648
756
|
def call_callbacks(state)
|
@@ -697,11 +805,11 @@ module Concurrent
|
|
697
805
|
class CompletableEvent < Event
|
698
806
|
# Complete the Event, `raise` if already completed
|
699
807
|
def complete(raise_on_reassign = true)
|
700
|
-
|
808
|
+
complete_with COMPLETED, raise_on_reassign
|
701
809
|
end
|
702
810
|
|
703
811
|
def hide_completable
|
704
|
-
|
812
|
+
EventWrapperPromise.new(self, @DefaultExecutor).event
|
705
813
|
end
|
706
814
|
end
|
707
815
|
|
@@ -711,7 +819,7 @@ module Concurrent
|
|
711
819
|
# `raise` if already completed
|
712
820
|
# return [self]
|
713
821
|
def complete(success, value, reason, raise_on_reassign = true)
|
714
|
-
|
822
|
+
complete_with(success ? Success.new(value) : Failed.new(reason), raise_on_reassign)
|
715
823
|
end
|
716
824
|
|
717
825
|
# Complete the future with value
|
@@ -752,12 +860,10 @@ module Concurrent
|
|
752
860
|
end
|
753
861
|
|
754
862
|
def hide_completable
|
755
|
-
|
863
|
+
FutureWrapperPromise.new(self, @DefaultExecutor).future
|
756
864
|
end
|
757
865
|
end
|
758
866
|
|
759
|
-
# TODO modularize blocked_by and notify blocked
|
760
|
-
|
761
867
|
# @abstract
|
762
868
|
# @!visibility private
|
763
869
|
class AbstractPromise < Synchronization::Object
|
@@ -793,22 +899,20 @@ module Concurrent
|
|
793
899
|
|
794
900
|
private
|
795
901
|
|
796
|
-
def
|
797
|
-
@Future.
|
902
|
+
def complete_with(new_state, raise_on_reassign = true)
|
903
|
+
@Future.complete_with(new_state, raise_on_reassign)
|
798
904
|
end
|
799
905
|
|
800
906
|
# @return [Future]
|
801
907
|
def evaluate_to(*args, block)
|
802
|
-
|
908
|
+
complete_with Future::Success.new(block.call(*args))
|
803
909
|
rescue => error
|
804
|
-
|
910
|
+
complete_with Future::Failed.new(error)
|
805
911
|
end
|
806
912
|
end
|
807
913
|
|
808
914
|
# @!visibility private
|
809
915
|
class CompletableEventPromise < AbstractPromise
|
810
|
-
public :complete
|
811
|
-
|
812
916
|
def initialize(default_executor)
|
813
917
|
super CompletableEvent.new(self, default_executor)
|
814
918
|
end
|
@@ -816,8 +920,6 @@ module Concurrent
|
|
816
920
|
|
817
921
|
# @!visibility private
|
818
922
|
class CompletableFuturePromise < AbstractPromise
|
819
|
-
# TODO consider to allow being blocked_by
|
820
|
-
|
821
923
|
def initialize(default_executor)
|
822
924
|
super CompletableFuture.new(self, default_executor)
|
823
925
|
end
|
@@ -828,11 +930,11 @@ module Concurrent
|
|
828
930
|
# @raise [Concurrent::MultipleAssignmentError] if the `Future` has already been set or otherwise completed
|
829
931
|
# @return [Future]
|
830
932
|
def success(value)
|
831
|
-
|
933
|
+
complete_with Future::Success.new(value)
|
832
934
|
end
|
833
935
|
|
834
936
|
def try_success(value)
|
835
|
-
|
937
|
+
!!complete_with(Future::Success.new(value), false)
|
836
938
|
end
|
837
939
|
|
838
940
|
# Set the `Future` to failed due to some error and wake or notify all threads waiting on it.
|
@@ -841,14 +943,13 @@ module Concurrent
|
|
841
943
|
# @raise [Concurrent::MultipleAssignmentError] if the `Future` has already been set or otherwise completed
|
842
944
|
# @return [Future]
|
843
945
|
def fail(reason = StandardError.new)
|
844
|
-
|
946
|
+
complete_with Future::Failed.new(reason)
|
845
947
|
end
|
846
948
|
|
847
949
|
def try_fail(reason = StandardError.new)
|
848
|
-
!!
|
950
|
+
!!complete_with(Future::Failed.new(reason), false)
|
849
951
|
end
|
850
952
|
|
851
|
-
public :complete
|
852
953
|
public :evaluate_to
|
853
954
|
|
854
955
|
# @return [Future]
|
@@ -865,7 +966,7 @@ module Concurrent
|
|
865
966
|
# @abstract
|
866
967
|
# @!visibility private
|
867
968
|
class BlockedPromise < InnerPromise
|
868
|
-
def initialize(future, blocked_by_futures, countdown
|
969
|
+
def initialize(future, blocked_by_futures, countdown)
|
869
970
|
initialize_blocked_by(blocked_by_futures)
|
870
971
|
@Countdown = AtomicFixnum.new countdown
|
871
972
|
|
@@ -956,7 +1057,7 @@ module Concurrent
|
|
956
1057
|
evaluate_to lambda { done_future.apply task }
|
957
1058
|
end
|
958
1059
|
else
|
959
|
-
|
1060
|
+
complete_with Future::Failed.new(done_future.reason)
|
960
1061
|
end
|
961
1062
|
end
|
962
1063
|
end
|
@@ -966,15 +1067,16 @@ module Concurrent
|
|
966
1067
|
private
|
967
1068
|
|
968
1069
|
def initialize(blocked_by_future, default_executor, executor, &task)
|
969
|
-
raise ArgumentError, 'only Future can be rescued' unless blocked_by_future.is_a? Future
|
970
1070
|
super blocked_by_future, default_executor, executor, &task
|
971
1071
|
end
|
972
1072
|
|
973
1073
|
def on_completable(done_future)
|
974
1074
|
if done_future.failed?
|
975
|
-
Concurrent.post_on(@Executor, done_future
|
1075
|
+
Concurrent.post_on(@Executor, done_future, @Task) do |done_future, task|
|
1076
|
+
evaluate_to lambda { done_future.apply task }
|
1077
|
+
end
|
976
1078
|
else
|
977
|
-
|
1079
|
+
complete_with done_future.internal_state
|
978
1080
|
end
|
979
1081
|
end
|
980
1082
|
end
|
@@ -994,18 +1096,21 @@ module Concurrent
|
|
994
1096
|
|
995
1097
|
# will be immediately completed
|
996
1098
|
# @!visibility private
|
997
|
-
class
|
998
|
-
def initialize(default_executor
|
999
|
-
super(
|
1000
|
-
Event.new(self, default_executor).complete
|
1001
|
-
else
|
1002
|
-
Future.new(self, default_executor).complete(true, args, nil)
|
1003
|
-
end)
|
1099
|
+
class ImmediateEventPromise < InnerPromise
|
1100
|
+
def initialize(default_executor)
|
1101
|
+
super Event.new(self, default_executor).complete_with(Event::COMPLETED)
|
1004
1102
|
end
|
1005
1103
|
end
|
1006
1104
|
|
1007
1105
|
# @!visibility private
|
1008
|
-
class
|
1106
|
+
class ImmediateFuturePromise < InnerPromise
|
1107
|
+
def initialize(default_executor, value)
|
1108
|
+
super Future.new(self, default_executor).complete_with(Future::Success.new(value))
|
1109
|
+
end
|
1110
|
+
end
|
1111
|
+
|
1112
|
+
# @!visibility private
|
1113
|
+
class FlatPromise < BlockedPromise
|
1009
1114
|
|
1010
1115
|
# !visibility private
|
1011
1116
|
def blocked_by
|
@@ -1034,8 +1139,6 @@ module Concurrent
|
|
1034
1139
|
|
1035
1140
|
def initialize(blocked_by_future, levels, default_executor)
|
1036
1141
|
raise ArgumentError, 'levels has to be higher than 0' if levels < 1
|
1037
|
-
blocked_by_future.is_a? Future or
|
1038
|
-
raise ArgumentError, 'only Future can be flatten'
|
1039
1142
|
super Future.new(self, default_executor), blocked_by_future, 1 + levels
|
1040
1143
|
end
|
1041
1144
|
|
@@ -1044,7 +1147,7 @@ module Concurrent
|
|
1044
1147
|
end
|
1045
1148
|
|
1046
1149
|
def on_completable(done_future)
|
1047
|
-
|
1150
|
+
complete_with done_future.internal_state
|
1048
1151
|
end
|
1049
1152
|
|
1050
1153
|
def clear_blocked_by!
|
@@ -1054,7 +1157,73 @@ module Concurrent
|
|
1054
1157
|
end
|
1055
1158
|
|
1056
1159
|
# @!visibility private
|
1057
|
-
class
|
1160
|
+
class ZipEventEventPromise < BlockedPromise
|
1161
|
+
def initialize(event1, event2, default_executor)
|
1162
|
+
super Event.new(self, default_executor), [event1, event2], 2
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
def on_completable(done_future)
|
1166
|
+
complete_with Event::COMPLETED
|
1167
|
+
end
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
# @!visibility private
|
1171
|
+
class ZipFutureEventPromise < BlockedPromise
|
1172
|
+
def initialize(future, event, default_executor)
|
1173
|
+
@FutureResult = future
|
1174
|
+
super Future.new(self, default_executor), [future, event], 2
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
def on_completable(done_future)
|
1178
|
+
complete_with @FutureResult.internal_state
|
1179
|
+
end
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
# @!visibility private
|
1183
|
+
class ZipFutureFuturePromise < BlockedPromise
|
1184
|
+
def initialize(future1, future2, default_executor)
|
1185
|
+
@Future1Result = future1
|
1186
|
+
@Future2Result = future2
|
1187
|
+
super Future.new(self, default_executor), [future1, future2], 2
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
def on_completable(done_future)
|
1191
|
+
success1, value1, reason1 = @Future1Result.result
|
1192
|
+
success2, value2, reason2 = @Future2Result.result
|
1193
|
+
success = success1 && success2
|
1194
|
+
new_state = if success
|
1195
|
+
Future::SuccessArray.new([value1, value2])
|
1196
|
+
else
|
1197
|
+
Future::PartiallyFailed.new([value1, value2], [reason1, reason2])
|
1198
|
+
end
|
1199
|
+
complete_with new_state
|
1200
|
+
end
|
1201
|
+
end
|
1202
|
+
|
1203
|
+
# @!visibility private
|
1204
|
+
class EventWrapperPromise < BlockedPromise
|
1205
|
+
def initialize(event, default_executor)
|
1206
|
+
super Event.new(self, default_executor), event, 1
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
def on_completable(done_future)
|
1210
|
+
complete_with Event::COMPLETED
|
1211
|
+
end
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
# @!visibility private
|
1215
|
+
class FutureWrapperPromise < BlockedPromise
|
1216
|
+
def initialize(future, default_executor)
|
1217
|
+
super Future.new(self, default_executor), future, 1
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
def on_completable(done_future)
|
1221
|
+
complete_with done_future.internal_state
|
1222
|
+
end
|
1223
|
+
end
|
1224
|
+
|
1225
|
+
# @!visibility private
|
1226
|
+
class ZipPromise < BlockedPromise
|
1058
1227
|
|
1059
1228
|
private
|
1060
1229
|
|
@@ -1071,6 +1240,10 @@ module Concurrent
|
|
1071
1240
|
|
1072
1241
|
# noinspection RubyArgCount
|
1073
1242
|
super(klass.new(self, default_executor), blocked_by_futures, blocked_by_futures.size)
|
1243
|
+
|
1244
|
+
if blocked_by_futures.empty?
|
1245
|
+
on_completable nil
|
1246
|
+
end
|
1074
1247
|
end
|
1075
1248
|
|
1076
1249
|
def on_completable(done_future)
|
@@ -1092,13 +1265,16 @@ module Concurrent
|
|
1092
1265
|
|
1093
1266
|
if all_success
|
1094
1267
|
if values.empty?
|
1095
|
-
|
1268
|
+
complete_with Event::COMPLETED
|
1096
1269
|
else
|
1097
|
-
|
1270
|
+
if values.size == 1
|
1271
|
+
complete_with Future::Success.new(values.first)
|
1272
|
+
else
|
1273
|
+
complete_with Future::SuccessArray.new(values)
|
1274
|
+
end
|
1098
1275
|
end
|
1099
1276
|
else
|
1100
|
-
|
1101
|
-
complete(false, nil, reasons.compact.first)
|
1277
|
+
complete_with Future::PartiallyFailed.new(values, reasons)
|
1102
1278
|
end
|
1103
1279
|
end
|
1104
1280
|
end
|
@@ -1119,29 +1295,34 @@ module Concurrent
|
|
1119
1295
|
end
|
1120
1296
|
|
1121
1297
|
def on_completable(done_future)
|
1122
|
-
|
1298
|
+
complete_with done_future.internal_state, false
|
1123
1299
|
end
|
1124
1300
|
end
|
1125
1301
|
|
1126
1302
|
# @!visibility private
|
1127
1303
|
class Delay < InnerPromise
|
1128
1304
|
def touch
|
1129
|
-
|
1130
|
-
@Future.complete
|
1131
|
-
else
|
1132
|
-
@Future.complete(true, @Args, nil)
|
1133
|
-
end
|
1305
|
+
@Future.complete_with Event::COMPLETED
|
1134
1306
|
end
|
1135
1307
|
|
1136
1308
|
private
|
1137
1309
|
|
1138
|
-
def initialize(default_executor
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1310
|
+
def initialize(default_executor)
|
1311
|
+
super Event.new(self, default_executor)
|
1312
|
+
end
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
# @!visibility private
|
1316
|
+
class DelayValue < InnerPromise
|
1317
|
+
def touch
|
1318
|
+
@Future.complete_with Future::Success.new(@Value)
|
1319
|
+
end
|
1320
|
+
|
1321
|
+
private
|
1322
|
+
|
1323
|
+
def initialize(default_executor, value)
|
1324
|
+
@Value = value
|
1325
|
+
super Future.new(self, default_executor)
|
1145
1326
|
end
|
1146
1327
|
end
|
1147
1328
|
|
@@ -1158,7 +1339,7 @@ module Concurrent
|
|
1158
1339
|
|
1159
1340
|
private
|
1160
1341
|
|
1161
|
-
def initialize(default_executor, intended_time
|
1342
|
+
def initialize(default_executor, intended_time)
|
1162
1343
|
@IntendedTime = intended_time
|
1163
1344
|
|
1164
1345
|
in_seconds = begin
|
@@ -1171,19 +1352,10 @@ module Concurrent
|
|
1171
1352
|
[0, schedule_time.to_f - now.to_f].max
|
1172
1353
|
end
|
1173
1354
|
|
1174
|
-
|
1175
|
-
super(if use_event
|
1176
|
-
Event.new(self, default_executor)
|
1177
|
-
else
|
1178
|
-
Future.new(self, default_executor)
|
1179
|
-
end)
|
1355
|
+
super Event.new(self, default_executor)
|
1180
1356
|
|
1181
|
-
Concurrent.global_timer_set.post(in_seconds
|
1182
|
-
|
1183
|
-
@Future.complete
|
1184
|
-
else
|
1185
|
-
@Future.complete(true, args, nil)
|
1186
|
-
end
|
1357
|
+
Concurrent.global_timer_set.post(in_seconds) do
|
1358
|
+
@Future.complete_with Event::COMPLETED
|
1187
1359
|
end
|
1188
1360
|
end
|
1189
1361
|
end
|
metadata
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concurrent-ruby-edge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.pre3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jerry D'Antonio
|
8
|
+
- Petr Chalupa
|
8
9
|
- The Ruby Concurrency Team
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2015-06-
|
13
|
+
date: 2015-06-22 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: concurrent-ruby
|
@@ -17,14 +18,14 @@ dependencies:
|
|
17
18
|
requirements:
|
18
19
|
- - "~>"
|
19
20
|
- !ruby/object:Gem::Version
|
20
|
-
version: 0.9.0.
|
21
|
+
version: 0.9.0.pre3
|
21
22
|
type: :runtime
|
22
23
|
prerelease: false
|
23
24
|
version_requirements: !ruby/object:Gem::Requirement
|
24
25
|
requirements:
|
25
26
|
- - "~>"
|
26
27
|
- !ruby/object:Gem::Version
|
27
|
-
version: 0.9.0.
|
28
|
+
version: 0.9.0.pre3
|
28
29
|
description: |
|
29
30
|
These features are under active development and may change frequently. They are expected not to
|
30
31
|
keep backward compatibility (there may also lack tests and documentation). Semantic versions will
|
@@ -102,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
103
|
version: 1.3.1
|
103
104
|
requirements: []
|
104
105
|
rubyforge_project:
|
105
|
-
rubygems_version: 2.4.
|
106
|
+
rubygems_version: 2.4.8
|
106
107
|
signing_key:
|
107
108
|
specification_version: 4
|
108
109
|
summary: Edge features and additions to the concurrent-ruby gem.
|