concurrent-ruby-edge 0.1.0.pre2 → 0.1.0.pre3
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|