concurrent-ruby 1.1.4 → 1.1.5
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/CHANGELOG.md +13 -0
- data/Gemfile +9 -7
- data/README.md +34 -2
- data/Rakefile +33 -52
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +9 -8
- data/lib/concurrent/array.rb +3 -3
- data/lib/concurrent/async.rb +14 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +2 -2
- data/lib/concurrent/collection/lock_free_stack.rb +1 -1
- data/lib/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent/executor/timer_set.rb +13 -15
- data/lib/concurrent/hash.rb +3 -3
- data/lib/concurrent/promises.rb +348 -117
- data/lib/concurrent/synchronization/abstract_struct.rb +1 -0
- data/lib/concurrent/synchronization/condition.rb +2 -0
- data/lib/concurrent/synchronization/jruby_object.rb +1 -0
- data/lib/concurrent/synchronization/lock.rb +2 -0
- data/lib/concurrent/synchronization/mri_object.rb +1 -0
- data/lib/concurrent/synchronization/object.rb +46 -20
- data/lib/concurrent/synchronization/rbx_object.rb +1 -0
- data/lib/concurrent/synchronization/truffleruby_object.rb +1 -0
- data/lib/concurrent/version.rb +1 -2
- metadata +5 -4
data/lib/concurrent/hash.rb
CHANGED
@@ -15,8 +15,9 @@ module Concurrent
|
|
15
15
|
# @!macro internal_implementation_note
|
16
16
|
HashImplementation = case
|
17
17
|
when Concurrent.on_cruby?
|
18
|
-
#
|
19
|
-
#
|
18
|
+
# Hash is thread-safe in practice because CRuby runs
|
19
|
+
# threads one at a time and does not do context
|
20
|
+
# switching during the execution of C functions.
|
20
21
|
::Hash
|
21
22
|
|
22
23
|
when Concurrent.on_jruby?
|
@@ -56,4 +57,3 @@ module Concurrent
|
|
56
57
|
end
|
57
58
|
|
58
59
|
end
|
59
|
-
|
data/lib/concurrent/promises.rb
CHANGED
@@ -43,6 +43,7 @@ module Concurrent
|
|
43
43
|
# new.
|
44
44
|
module FactoryMethods
|
45
45
|
extend ReInclude
|
46
|
+
extend self
|
46
47
|
|
47
48
|
module Configuration
|
48
49
|
# @return [Executor, :io, :fast] the executor which is used when none is supplied
|
@@ -92,16 +93,14 @@ module Concurrent
|
|
92
93
|
future_on(default_executor, *args, &task)
|
93
94
|
end
|
94
95
|
|
95
|
-
#
|
96
|
-
# Constructs new Future which will be resolved after block is evaluated on default executor.
|
96
|
+
# Constructs new Future which will be resolved after block is evaluated on default executor.
|
97
97
|
# Evaluation begins immediately.
|
98
98
|
#
|
99
|
-
# @!macro promises.
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
# @return [Future]
|
99
|
+
# @!macro promises.param.default_executor
|
100
|
+
# @!macro promises.param.args
|
101
|
+
# @yield [*args] to the task.
|
102
|
+
# @!macro promise.param.task-future
|
103
|
+
# @return [Future]
|
105
104
|
def future_on(default_executor, *args, &task)
|
106
105
|
ImmediateEventPromise.new(default_executor).future.then(*args, &task)
|
107
106
|
end
|
@@ -109,6 +108,9 @@ module Concurrent
|
|
109
108
|
# Creates resolved future with will be either fulfilled with the given value or rejection with
|
110
109
|
# the given reason.
|
111
110
|
#
|
111
|
+
# @param [true, false] fulfilled
|
112
|
+
# @param [Object] value
|
113
|
+
# @param [Object] reason
|
112
114
|
# @!macro promises.param.default_executor
|
113
115
|
# @return [Future]
|
114
116
|
def resolved_future(fulfilled, value, reason, default_executor = self.default_executor)
|
@@ -118,6 +120,7 @@ module Concurrent
|
|
118
120
|
# Creates resolved future with will be fulfilled with the given value.
|
119
121
|
#
|
120
122
|
# @!macro promises.param.default_executor
|
123
|
+
# @param [Object] value
|
121
124
|
# @return [Future]
|
122
125
|
def fulfilled_future(value, default_executor = self.default_executor)
|
123
126
|
resolved_future true, value, nil, default_executor
|
@@ -126,6 +129,7 @@ module Concurrent
|
|
126
129
|
# Creates resolved future with will be rejected with the given reason.
|
127
130
|
#
|
128
131
|
# @!macro promises.param.default_executor
|
132
|
+
# @param [Object] reason
|
129
133
|
# @return [Future]
|
130
134
|
def rejected_future(reason, default_executor = self.default_executor)
|
131
135
|
resolved_future false, nil, reason, default_executor
|
@@ -146,23 +150,23 @@ module Concurrent
|
|
146
150
|
# @!macro promises.param.default_executor
|
147
151
|
# @return [Event, Future]
|
148
152
|
#
|
149
|
-
# @overload
|
153
|
+
# @overload make_future(nil, default_executor = self.default_executor)
|
150
154
|
# @param [nil] nil
|
151
155
|
# @return [Event] resolved event.
|
152
156
|
#
|
153
|
-
# @overload
|
157
|
+
# @overload make_future(a_future, default_executor = self.default_executor)
|
154
158
|
# @param [Future] a_future
|
155
159
|
# @return [Future] a future which will be resolved when a_future is.
|
156
160
|
#
|
157
|
-
# @overload
|
161
|
+
# @overload make_future(an_event, default_executor = self.default_executor)
|
158
162
|
# @param [Event] an_event
|
159
163
|
# @return [Event] an event which will be resolved when an_event is.
|
160
164
|
#
|
161
|
-
# @overload
|
165
|
+
# @overload make_future(exception, default_executor = self.default_executor)
|
162
166
|
# @param [Exception] exception
|
163
167
|
# @return [Future] a rejected future with the exception as its reason.
|
164
168
|
#
|
165
|
-
# @overload
|
169
|
+
# @overload make_future(value, default_executor = self.default_executor)
|
166
170
|
# @param [Object] value when none of the above overloads fits
|
167
171
|
# @return [Future] a fulfilled future with the value.
|
168
172
|
def make_future(argument = nil, default_executor = self.default_executor)
|
@@ -180,34 +184,53 @@ module Concurrent
|
|
180
184
|
end
|
181
185
|
|
182
186
|
# @!macro promises.shortcut.on
|
183
|
-
# @return [Future]
|
187
|
+
# @return [Future, Event]
|
184
188
|
def delay(*args, &task)
|
185
189
|
delay_on default_executor, *args, &task
|
186
190
|
end
|
187
191
|
|
188
|
-
#
|
189
|
-
#
|
192
|
+
# Creates new event or future which is resolved only after it is touched,
|
193
|
+
# see {Concurrent::AbstractEventFuture#touch}.
|
190
194
|
#
|
191
|
-
# @!macro promises.
|
195
|
+
# @!macro promises.param.default_executor
|
196
|
+
# @overload delay_on(default_executor, *args, &task)
|
197
|
+
# If task is provided it returns a {Future} representing the result of the task.
|
198
|
+
# @!macro promises.param.args
|
199
|
+
# @yield [*args] to the task.
|
200
|
+
# @!macro promise.param.task-future
|
201
|
+
# @return [Future]
|
202
|
+
# @overload delay_on(default_executor)
|
203
|
+
# If no task is provided, it returns an {Event}
|
204
|
+
# @return [Event]
|
192
205
|
def delay_on(default_executor, *args, &task)
|
193
|
-
DelayPromise.new(default_executor).event
|
206
|
+
event = DelayPromise.new(default_executor).event
|
207
|
+
task ? event.chain(*args, &task) : event
|
194
208
|
end
|
195
209
|
|
196
210
|
# @!macro promises.shortcut.on
|
197
|
-
# @return [Future]
|
211
|
+
# @return [Future, Event]
|
198
212
|
def schedule(intended_time, *args, &task)
|
199
213
|
schedule_on default_executor, intended_time, *args, &task
|
200
214
|
end
|
201
215
|
|
202
|
-
#
|
203
|
-
# The task is planned for execution in intended_time.
|
216
|
+
# Creates new event or future which is resolved in intended_time.
|
204
217
|
#
|
205
|
-
# @!macro promises.
|
218
|
+
# @!macro promises.param.default_executor
|
206
219
|
# @!macro promises.param.intended_time
|
207
220
|
# @param [Numeric, Time] intended_time `Numeric` means to run in `intended_time` seconds.
|
208
221
|
# `Time` means to run on `intended_time`.
|
222
|
+
# @overload schedule_on(default_executor, intended_time, *args, &task)
|
223
|
+
# If task is provided it returns a {Future} representing the result of the task.
|
224
|
+
# @!macro promises.param.args
|
225
|
+
# @yield [*args] to the task.
|
226
|
+
# @!macro promise.param.task-future
|
227
|
+
# @return [Future]
|
228
|
+
# @overload schedule_on(default_executor, intended_time)
|
229
|
+
# If no task is provided, it returns an {Event}
|
230
|
+
# @return [Event]
|
209
231
|
def schedule_on(default_executor, intended_time, *args, &task)
|
210
|
-
ScheduledPromise.new(default_executor, intended_time).event
|
232
|
+
event = ScheduledPromise.new(default_executor, intended_time).event
|
233
|
+
task ? event.chain(*args, &task) : event
|
211
234
|
end
|
212
235
|
|
213
236
|
# @!macro promises.shortcut.on
|
@@ -259,7 +282,7 @@ module Concurrent
|
|
259
282
|
# Creates new future which is resolved after first futures_and_or_events is resolved.
|
260
283
|
# Its result equals result of the first resolved future.
|
261
284
|
# @!macro promises.any-touch
|
262
|
-
# If resolved it does not propagate {AbstractEventFuture#touch}, leaving delayed
|
285
|
+
# If resolved it does not propagate {Concurrent::AbstractEventFuture#touch}, leaving delayed
|
263
286
|
# futures un-executed if they are not required any more.
|
264
287
|
# @!macro promises.event-conversion
|
265
288
|
#
|
@@ -311,7 +334,7 @@ module Concurrent
|
|
311
334
|
end
|
312
335
|
|
313
336
|
module InternalStates
|
314
|
-
#
|
337
|
+
# @!visibility private
|
315
338
|
class State
|
316
339
|
def resolved?
|
317
340
|
raise NotImplementedError
|
@@ -322,9 +345,7 @@ module Concurrent
|
|
322
345
|
end
|
323
346
|
end
|
324
347
|
|
325
|
-
|
326
|
-
|
327
|
-
# @private
|
348
|
+
# @!visibility private
|
328
349
|
class Pending < State
|
329
350
|
def resolved?
|
330
351
|
false
|
@@ -335,9 +356,11 @@ module Concurrent
|
|
335
356
|
end
|
336
357
|
end
|
337
358
|
|
338
|
-
|
359
|
+
# @!visibility private
|
360
|
+
class Reserved < Pending
|
361
|
+
end
|
339
362
|
|
340
|
-
#
|
363
|
+
# @!visibility private
|
341
364
|
class ResolvedWithResult < State
|
342
365
|
def resolved?
|
343
366
|
true
|
@@ -368,9 +391,7 @@ module Concurrent
|
|
368
391
|
end
|
369
392
|
end
|
370
393
|
|
371
|
-
|
372
|
-
|
373
|
-
# @private
|
394
|
+
# @!visibility private
|
374
395
|
class Fulfilled < ResolvedWithResult
|
375
396
|
|
376
397
|
def initialize(value)
|
@@ -398,18 +419,14 @@ module Concurrent
|
|
398
419
|
end
|
399
420
|
end
|
400
421
|
|
401
|
-
|
402
|
-
|
403
|
-
# @private
|
422
|
+
# @!visibility private
|
404
423
|
class FulfilledArray < Fulfilled
|
405
424
|
def apply(args, block)
|
406
425
|
block.call(*value, *args)
|
407
426
|
end
|
408
427
|
end
|
409
428
|
|
410
|
-
|
411
|
-
|
412
|
-
# @private
|
429
|
+
# @!visibility private
|
413
430
|
class Rejected < ResolvedWithResult
|
414
431
|
def initialize(reason)
|
415
432
|
@Reason = reason
|
@@ -436,9 +453,7 @@ module Concurrent
|
|
436
453
|
end
|
437
454
|
end
|
438
455
|
|
439
|
-
|
440
|
-
|
441
|
-
# @private
|
456
|
+
# @!visibility private
|
442
457
|
class PartiallyRejected < ResolvedWithResult
|
443
458
|
def initialize(value, reason)
|
444
459
|
super()
|
@@ -467,24 +482,38 @@ module Concurrent
|
|
467
482
|
end
|
468
483
|
end
|
469
484
|
|
470
|
-
|
471
|
-
|
472
|
-
|
485
|
+
# @!visibility private
|
486
|
+
PENDING = Pending.new
|
487
|
+
# @!visibility private
|
488
|
+
RESERVED = Reserved.new
|
489
|
+
# @!visibility private
|
473
490
|
RESOLVED = Fulfilled.new(nil)
|
474
491
|
|
475
492
|
def RESOLVED.to_sym
|
476
493
|
:resolved
|
477
494
|
end
|
478
|
-
|
479
|
-
private_constant :PENDING, :RESOLVED
|
480
495
|
end
|
481
496
|
|
482
497
|
private_constant :InternalStates
|
483
498
|
|
499
|
+
# @!macro promises.shortcut.event-future
|
500
|
+
# @see Event#$0
|
501
|
+
# @see Future#$0
|
502
|
+
|
503
|
+
# @!macro promises.param.timeout
|
504
|
+
# @param [Numeric] timeout the maximum time in second to wait.
|
505
|
+
|
506
|
+
# @!macro promises.warn.blocks
|
507
|
+
# @note This function potentially blocks current thread until the Future is resolved.
|
508
|
+
# Be careful it can deadlock. Try to chain instead.
|
509
|
+
|
484
510
|
# Common ancestor of {Event} and {Future} classes, many shared methods are defined here.
|
485
511
|
class AbstractEventFuture < Synchronization::Object
|
486
512
|
safe_initialization!
|
487
|
-
|
513
|
+
attr_atomic(:internal_state)
|
514
|
+
private :internal_state=, :swap_internal_state, :compare_and_set_internal_state, :update_internal_state
|
515
|
+
# @!method internal_state
|
516
|
+
# @!visibility private
|
488
517
|
|
489
518
|
include InternalStates
|
490
519
|
|
@@ -501,17 +530,6 @@ module Concurrent
|
|
501
530
|
|
502
531
|
private :initialize
|
503
532
|
|
504
|
-
# @!macro promises.shortcut.event-future
|
505
|
-
# @see Event#$0
|
506
|
-
# @see Future#$0
|
507
|
-
|
508
|
-
# @!macro promises.param.timeout
|
509
|
-
# @param [Numeric] timeout the maximum time in second to wait.
|
510
|
-
|
511
|
-
# @!macro promises.warn.blocks
|
512
|
-
# @note This function potentially blocks current thread until the Future is resolved.
|
513
|
-
# Be careful it can deadlock. Try to chain instead.
|
514
|
-
|
515
533
|
# Returns its state.
|
516
534
|
# @return [Symbol]
|
517
535
|
#
|
@@ -545,7 +563,7 @@ module Concurrent
|
|
545
563
|
end
|
546
564
|
|
547
565
|
# @!macro promises.touches
|
548
|
-
# Calls {AbstractEventFuture#touch}.
|
566
|
+
# Calls {Concurrent::AbstractEventFuture#touch}.
|
549
567
|
|
550
568
|
# @!macro promises.method.wait
|
551
569
|
# Wait (block the Thread) until receiver is {#resolved?}.
|
@@ -553,7 +571,7 @@ module Concurrent
|
|
553
571
|
#
|
554
572
|
# @!macro promises.warn.blocks
|
555
573
|
# @!macro promises.param.timeout
|
556
|
-
# @return [
|
574
|
+
# @return [self, true, false] self implies timeout was not used, true implies timeout was used
|
557
575
|
# and it was resolved, false implies it was not resolved within timeout.
|
558
576
|
def wait(timeout = nil)
|
559
577
|
result = wait_until_resolved(timeout)
|
@@ -590,7 +608,7 @@ module Concurrent
|
|
590
608
|
# @yield [fulfilled, value, reason, *args] to the task.
|
591
609
|
# @yieldparam [true, false] fulfilled
|
592
610
|
# @yieldparam [Object] value
|
593
|
-
# @yieldparam [
|
611
|
+
# @yieldparam [Object] reason
|
594
612
|
def chain_on(executor, *args, &task)
|
595
613
|
ChainPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future
|
596
614
|
end
|
@@ -631,7 +649,7 @@ module Concurrent
|
|
631
649
|
# @yield [fulfilled, value, reason, *args] to the callback.
|
632
650
|
# @yieldparam [true, false] fulfilled
|
633
651
|
# @yieldparam [Object] value
|
634
|
-
# @yieldparam [
|
652
|
+
# @yieldparam [Object] reason
|
635
653
|
def on_resolution!(*args, &callback)
|
636
654
|
add_callback :callback_on_resolution, args, callback
|
637
655
|
end
|
@@ -649,7 +667,7 @@ module Concurrent
|
|
649
667
|
# @yield [fulfilled, value, reason, *args] to the callback.
|
650
668
|
# @yieldparam [true, false] fulfilled
|
651
669
|
# @yieldparam [Object] value
|
652
|
-
# @yieldparam [
|
670
|
+
# @yieldparam [Object] reason
|
653
671
|
def on_resolution_using(executor, *args, &callback)
|
654
672
|
add_callback :async_callback_on_resolution, executor, args, callback
|
655
673
|
end
|
@@ -665,8 +683,8 @@ module Concurrent
|
|
665
683
|
end
|
666
684
|
|
667
685
|
# @!visibility private
|
668
|
-
def resolve_with(state, raise_on_reassign = true)
|
669
|
-
if compare_and_set_internal_state(PENDING, state)
|
686
|
+
def resolve_with(state, raise_on_reassign = true, reserved = false)
|
687
|
+
if compare_and_set_internal_state(reserved ? RESERVED : PENDING, state)
|
670
688
|
# go to synchronized block only if there were waiting threads
|
671
689
|
@Lock.synchronize { @Condition.broadcast } unless @Waiters.value == 0
|
672
690
|
call_callbacks state
|
@@ -719,6 +737,12 @@ module Concurrent
|
|
719
737
|
add_callback(:callback_clear_delayed_node, node)
|
720
738
|
end
|
721
739
|
|
740
|
+
# @!visibility private
|
741
|
+
def with_hidden_resolvable
|
742
|
+
# TODO (pitr-ch 10-Dec-2018): documentation, better name if in edge
|
743
|
+
self
|
744
|
+
end
|
745
|
+
|
722
746
|
private
|
723
747
|
|
724
748
|
def add_callback(method, *args)
|
@@ -907,9 +931,17 @@ module Concurrent
|
|
907
931
|
# @!macro promises.warn.blocks
|
908
932
|
# @!macro promises.warn.nil
|
909
933
|
# @!macro promises.param.timeout
|
910
|
-
#
|
911
|
-
|
912
|
-
|
934
|
+
# @!macro promises.param.timeout_value
|
935
|
+
# @param [Object] timeout_value a value returned by the method when it times out
|
936
|
+
# @return [Object, nil, timeout_value] the value of the Future when fulfilled,
|
937
|
+
# timeout_value on timeout,
|
938
|
+
# nil on rejection.
|
939
|
+
def value(timeout = nil, timeout_value = nil)
|
940
|
+
if wait_until_resolved timeout
|
941
|
+
internal_state.value
|
942
|
+
else
|
943
|
+
timeout_value
|
944
|
+
end
|
913
945
|
end
|
914
946
|
|
915
947
|
# Returns reason of future's rejection.
|
@@ -918,9 +950,14 @@ module Concurrent
|
|
918
950
|
# @!macro promises.warn.blocks
|
919
951
|
# @!macro promises.warn.nil
|
920
952
|
# @!macro promises.param.timeout
|
921
|
-
#
|
922
|
-
|
923
|
-
|
953
|
+
# @!macro promises.param.timeout_value
|
954
|
+
# @return [Object, timeout_value] the reason, or timeout_value on timeout, or nil on fulfillment.
|
955
|
+
def reason(timeout = nil, timeout_value = nil)
|
956
|
+
if wait_until_resolved timeout
|
957
|
+
internal_state.reason
|
958
|
+
else
|
959
|
+
timeout_value
|
960
|
+
end
|
924
961
|
end
|
925
962
|
|
926
963
|
# Returns triplet fulfilled?, value, reason.
|
@@ -928,7 +965,7 @@ module Concurrent
|
|
928
965
|
#
|
929
966
|
# @!macro promises.warn.blocks
|
930
967
|
# @!macro promises.param.timeout
|
931
|
-
# @return [Array(Boolean, Object,
|
968
|
+
# @return [Array(Boolean, Object, Object), nil] triplet of fulfilled?, value, reason, or nil
|
932
969
|
# on timeout.
|
933
970
|
def result(timeout = nil)
|
934
971
|
internal_state.result if wait_until_resolved timeout
|
@@ -942,26 +979,40 @@ module Concurrent
|
|
942
979
|
end
|
943
980
|
|
944
981
|
# @!macro promises.method.value
|
945
|
-
# @return [Object, nil] the value of the Future when fulfilled,
|
982
|
+
# @return [Object, nil, timeout_value] the value of the Future when fulfilled,
|
983
|
+
# or nil on rejection,
|
984
|
+
# or timeout_value on timeout.
|
946
985
|
# @raise [Exception] {#reason} on rejection
|
947
|
-
def value!(timeout = nil)
|
948
|
-
|
986
|
+
def value!(timeout = nil, timeout_value = nil)
|
987
|
+
if wait_until_resolved! timeout
|
988
|
+
internal_state.value
|
989
|
+
else
|
990
|
+
timeout_value
|
991
|
+
end
|
949
992
|
end
|
950
993
|
|
951
994
|
# Allows rejected Future to be risen with `raise` method.
|
995
|
+
# If the reason is not an exception `Runtime.new(reason)` is returned.
|
996
|
+
#
|
952
997
|
# @example
|
953
998
|
# raise Promises.rejected_future(StandardError.new("boom"))
|
954
|
-
#
|
999
|
+
# raise Promises.rejected_future("or just boom")
|
1000
|
+
# @raise [Concurrent::Error] when raising not rejected future
|
955
1001
|
# @return [Exception]
|
956
1002
|
def exception(*args)
|
957
1003
|
raise Concurrent::Error, 'it is not rejected' unless rejected?
|
1004
|
+
raise ArgumentError unless args.size <= 1
|
958
1005
|
reason = Array(internal_state.reason).flatten.compact
|
959
1006
|
if reason.size > 1
|
960
1007
|
ex = Concurrent::MultipleErrors.new reason
|
961
1008
|
ex.set_backtrace(caller)
|
962
1009
|
ex
|
963
1010
|
else
|
964
|
-
ex = reason[0].
|
1011
|
+
ex = if reason[0].respond_to? :exception
|
1012
|
+
reason[0].exception(*args)
|
1013
|
+
else
|
1014
|
+
RuntimeError.new(reason[0]).exception(*args)
|
1015
|
+
end
|
965
1016
|
ex.set_backtrace Array(ex.backtrace) + caller
|
966
1017
|
ex
|
967
1018
|
end
|
@@ -1133,14 +1184,20 @@ module Concurrent
|
|
1133
1184
|
# will become reason of the returned future.
|
1134
1185
|
#
|
1135
1186
|
# @return [Future]
|
1187
|
+
# @param [#call(value)] run_test
|
1188
|
+
# an object which when called returns either Future to keep running with
|
1189
|
+
# or nil, then the run completes with the value.
|
1190
|
+
# The run_test can be used to extract the Future from deeper structure,
|
1191
|
+
# or to distinguish Future which is a resulting value from a future
|
1192
|
+
# which is suppose to continue running.
|
1136
1193
|
# @example
|
1137
1194
|
# body = lambda do |v|
|
1138
1195
|
# v += 1
|
1139
1196
|
# v < 5 ? Promises.future(v, &body) : v
|
1140
1197
|
# end
|
1141
1198
|
# Promises.future(0, &body).run.value! # => 5
|
1142
|
-
def run
|
1143
|
-
RunFuturePromise.new_blocked_by1(self, @DefaultExecutor).future
|
1199
|
+
def run(run_test = method(:run_test))
|
1200
|
+
RunFuturePromise.new_blocked_by1(self, @DefaultExecutor, run_test).future
|
1144
1201
|
end
|
1145
1202
|
|
1146
1203
|
# @!visibility private
|
@@ -1163,13 +1220,34 @@ module Concurrent
|
|
1163
1220
|
self
|
1164
1221
|
end
|
1165
1222
|
|
1223
|
+
# @return [String] Short string representation.
|
1224
|
+
def to_s
|
1225
|
+
if resolved?
|
1226
|
+
format '%s with %s>', super[0..-2], (fulfilled? ? value : reason).inspect
|
1227
|
+
else
|
1228
|
+
super
|
1229
|
+
end
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
alias_method :inspect, :to_s
|
1233
|
+
|
1166
1234
|
private
|
1167
1235
|
|
1236
|
+
def run_test(v)
|
1237
|
+
v if v.is_a?(Future)
|
1238
|
+
end
|
1239
|
+
|
1168
1240
|
def rejected_resolution(raise_on_reassign, state)
|
1169
1241
|
if raise_on_reassign
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1242
|
+
if internal_state == RESERVED
|
1243
|
+
raise Concurrent::MultipleAssignmentError.new(
|
1244
|
+
"Future can be resolved only once. It is already reserved.")
|
1245
|
+
else
|
1246
|
+
raise Concurrent::MultipleAssignmentError.new(
|
1247
|
+
"Future can be resolved only once. It's #{result}, trying to set #{state.result}.",
|
1248
|
+
current_result: result,
|
1249
|
+
new_result: state.result)
|
1250
|
+
end
|
1173
1251
|
end
|
1174
1252
|
return false
|
1175
1253
|
end
|
@@ -1206,15 +1284,15 @@ module Concurrent
|
|
1206
1284
|
|
1207
1285
|
end
|
1208
1286
|
|
1209
|
-
# Marker module of Future, Event resolved manually
|
1287
|
+
# Marker module of Future, Event resolved manually.
|
1210
1288
|
module Resolvable
|
1289
|
+
include InternalStates
|
1211
1290
|
end
|
1212
1291
|
|
1213
1292
|
# A Event which can be resolved by user.
|
1214
1293
|
class ResolvableEvent < Event
|
1215
1294
|
include Resolvable
|
1216
1295
|
|
1217
|
-
|
1218
1296
|
# @!macro raise_on_reassign
|
1219
1297
|
# @raise [MultipleAssignmentError] when already resolved and raise_on_reassign is true.
|
1220
1298
|
|
@@ -1227,8 +1305,13 @@ module Concurrent
|
|
1227
1305
|
# Makes the event resolved, which triggers all dependent futures.
|
1228
1306
|
#
|
1229
1307
|
# @!macro promise.param.raise_on_reassign
|
1230
|
-
|
1231
|
-
|
1308
|
+
# @!macro promise.param.reserved
|
1309
|
+
# @param [true, false] reserved
|
1310
|
+
# Set to true if the resolvable is {#reserve}d by you,
|
1311
|
+
# marks resolution of reserved resolvable events and futures explicitly.
|
1312
|
+
# Advanced feature, ignore unless you use {Resolvable#reserve} from edge.
|
1313
|
+
def resolve(raise_on_reassign = true, reserved = false)
|
1314
|
+
resolve_with RESOLVED, raise_on_reassign, reserved
|
1232
1315
|
end
|
1233
1316
|
|
1234
1317
|
# Creates new event wrapping receiver, effectively hiding the resolve method.
|
@@ -1237,6 +1320,23 @@ module Concurrent
|
|
1237
1320
|
def with_hidden_resolvable
|
1238
1321
|
@with_hidden_resolvable ||= EventWrapperPromise.new_blocked_by1(self, @DefaultExecutor).event
|
1239
1322
|
end
|
1323
|
+
|
1324
|
+
# Behaves as {AbstractEventFuture#wait} but has one additional optional argument
|
1325
|
+
# resolve_on_timeout.
|
1326
|
+
#
|
1327
|
+
# @param [true, false] resolve_on_timeout
|
1328
|
+
# If it times out and the argument is true it will also resolve the event.
|
1329
|
+
# @return [self, true, false]
|
1330
|
+
# @see AbstractEventFuture#wait
|
1331
|
+
def wait(timeout = nil, resolve_on_timeout = false)
|
1332
|
+
super(timeout) or if resolve_on_timeout
|
1333
|
+
# if it fails to resolve it was resolved in the meantime
|
1334
|
+
# so return true as if there was no timeout
|
1335
|
+
!resolve(false)
|
1336
|
+
else
|
1337
|
+
false
|
1338
|
+
end
|
1339
|
+
end
|
1240
1340
|
end
|
1241
1341
|
|
1242
1342
|
# A Future which can be resolved by user.
|
@@ -1246,29 +1346,38 @@ module Concurrent
|
|
1246
1346
|
# Makes the future resolved with result of triplet `fulfilled?`, `value`, `reason`,
|
1247
1347
|
# which triggers all dependent futures.
|
1248
1348
|
#
|
1349
|
+
# @param [true, false] fulfilled
|
1350
|
+
# @param [Object] value
|
1351
|
+
# @param [Object] reason
|
1249
1352
|
# @!macro promise.param.raise_on_reassign
|
1250
|
-
|
1251
|
-
|
1353
|
+
# @!macro promise.param.reserved
|
1354
|
+
def resolve(fulfilled = true, value = nil, reason = nil, raise_on_reassign = true, reserved = false)
|
1355
|
+
resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason), raise_on_reassign, reserved)
|
1252
1356
|
end
|
1253
1357
|
|
1254
1358
|
# Makes the future fulfilled with `value`,
|
1255
1359
|
# which triggers all dependent futures.
|
1256
1360
|
#
|
1361
|
+
# @param [Object] value
|
1257
1362
|
# @!macro promise.param.raise_on_reassign
|
1258
|
-
|
1259
|
-
|
1363
|
+
# @!macro promise.param.reserved
|
1364
|
+
def fulfill(value, raise_on_reassign = true, reserved = false)
|
1365
|
+
resolve_with Fulfilled.new(value), raise_on_reassign, reserved
|
1260
1366
|
end
|
1261
1367
|
|
1262
1368
|
# Makes the future rejected with `reason`,
|
1263
1369
|
# which triggers all dependent futures.
|
1264
1370
|
#
|
1371
|
+
# @param [Object] reason
|
1265
1372
|
# @!macro promise.param.raise_on_reassign
|
1266
|
-
|
1267
|
-
|
1373
|
+
# @!macro promise.param.reserved
|
1374
|
+
def reject(reason, raise_on_reassign = true, reserved = false)
|
1375
|
+
resolve_with Rejected.new(reason), raise_on_reassign, reserved
|
1268
1376
|
end
|
1269
1377
|
|
1270
1378
|
# Evaluates the block and sets its result as future's value fulfilling, if the block raises
|
1271
1379
|
# an exception the future rejects with it.
|
1380
|
+
#
|
1272
1381
|
# @yield [*args] to the block.
|
1273
1382
|
# @yieldreturn [Object] value
|
1274
1383
|
# @return [self]
|
@@ -1278,6 +1387,7 @@ module Concurrent
|
|
1278
1387
|
|
1279
1388
|
# Evaluates the block and sets its result as future's value fulfilling, if the block raises
|
1280
1389
|
# an exception the future rejects with it.
|
1390
|
+
#
|
1281
1391
|
# @yield [*args] to the block.
|
1282
1392
|
# @yieldreturn [Object] value
|
1283
1393
|
# @return [self]
|
@@ -1286,6 +1396,135 @@ module Concurrent
|
|
1286
1396
|
promise.evaluate_to(*args, block).wait!
|
1287
1397
|
end
|
1288
1398
|
|
1399
|
+
# @!macro promises.resolvable.resolve_on_timeout
|
1400
|
+
# @param [::Array(true, Object, nil), ::Array(false, nil, Exception), nil] resolve_on_timeout
|
1401
|
+
# If it times out and the argument is not nil it will also resolve the future
|
1402
|
+
# to the provided resolution.
|
1403
|
+
|
1404
|
+
# Behaves as {AbstractEventFuture#wait} but has one additional optional argument
|
1405
|
+
# resolve_on_timeout.
|
1406
|
+
#
|
1407
|
+
# @!macro promises.resolvable.resolve_on_timeout
|
1408
|
+
# @return [self, true, false]
|
1409
|
+
# @see AbstractEventFuture#wait
|
1410
|
+
def wait(timeout = nil, resolve_on_timeout = nil)
|
1411
|
+
super(timeout) or if resolve_on_timeout
|
1412
|
+
# if it fails to resolve it was resolved in the meantime
|
1413
|
+
# so return true as if there was no timeout
|
1414
|
+
!resolve(*resolve_on_timeout, false)
|
1415
|
+
else
|
1416
|
+
false
|
1417
|
+
end
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
# Behaves as {Future#wait!} but has one additional optional argument
|
1421
|
+
# resolve_on_timeout.
|
1422
|
+
#
|
1423
|
+
# @!macro promises.resolvable.resolve_on_timeout
|
1424
|
+
# @return [self, true, false]
|
1425
|
+
# @raise [Exception] {#reason} on rejection
|
1426
|
+
# @see Future#wait!
|
1427
|
+
def wait!(timeout = nil, resolve_on_timeout = nil)
|
1428
|
+
super(timeout) or if resolve_on_timeout
|
1429
|
+
if resolve(*resolve_on_timeout, false)
|
1430
|
+
false
|
1431
|
+
else
|
1432
|
+
# if it fails to resolve it was resolved in the meantime
|
1433
|
+
# so return true as if there was no timeout
|
1434
|
+
raise self if rejected?
|
1435
|
+
true
|
1436
|
+
end
|
1437
|
+
else
|
1438
|
+
false
|
1439
|
+
end
|
1440
|
+
end
|
1441
|
+
|
1442
|
+
# Behaves as {Future#value} but has one additional optional argument
|
1443
|
+
# resolve_on_timeout.
|
1444
|
+
#
|
1445
|
+
# @!macro promises.resolvable.resolve_on_timeout
|
1446
|
+
# @return [Object, timeout_value, nil]
|
1447
|
+
# @see Future#value
|
1448
|
+
def value(timeout = nil, timeout_value = nil, resolve_on_timeout = nil)
|
1449
|
+
if wait_until_resolved timeout
|
1450
|
+
internal_state.value
|
1451
|
+
else
|
1452
|
+
if resolve_on_timeout
|
1453
|
+
unless resolve(*resolve_on_timeout, false)
|
1454
|
+
# if it fails to resolve it was resolved in the meantime
|
1455
|
+
# so return value as if there was no timeout
|
1456
|
+
return internal_state.value
|
1457
|
+
end
|
1458
|
+
end
|
1459
|
+
timeout_value
|
1460
|
+
end
|
1461
|
+
end
|
1462
|
+
|
1463
|
+
# Behaves as {Future#value!} but has one additional optional argument
|
1464
|
+
# resolve_on_timeout.
|
1465
|
+
#
|
1466
|
+
# @!macro promises.resolvable.resolve_on_timeout
|
1467
|
+
# @return [Object, timeout_value, nil]
|
1468
|
+
# @raise [Exception] {#reason} on rejection
|
1469
|
+
# @see Future#value!
|
1470
|
+
def value!(timeout = nil, timeout_value = nil, resolve_on_timeout = nil)
|
1471
|
+
if wait_until_resolved! timeout
|
1472
|
+
internal_state.value
|
1473
|
+
else
|
1474
|
+
if resolve_on_timeout
|
1475
|
+
unless resolve(*resolve_on_timeout, false)
|
1476
|
+
# if it fails to resolve it was resolved in the meantime
|
1477
|
+
# so return value as if there was no timeout
|
1478
|
+
raise self if rejected?
|
1479
|
+
return internal_state.value
|
1480
|
+
end
|
1481
|
+
end
|
1482
|
+
timeout_value
|
1483
|
+
end
|
1484
|
+
end
|
1485
|
+
|
1486
|
+
# Behaves as {Future#reason} but has one additional optional argument
|
1487
|
+
# resolve_on_timeout.
|
1488
|
+
#
|
1489
|
+
# @!macro promises.resolvable.resolve_on_timeout
|
1490
|
+
# @return [Exception, timeout_value, nil]
|
1491
|
+
# @see Future#reason
|
1492
|
+
def reason(timeout = nil, timeout_value = nil, resolve_on_timeout = nil)
|
1493
|
+
if wait_until_resolved timeout
|
1494
|
+
internal_state.reason
|
1495
|
+
else
|
1496
|
+
if resolve_on_timeout
|
1497
|
+
unless resolve(*resolve_on_timeout, false)
|
1498
|
+
# if it fails to resolve it was resolved in the meantime
|
1499
|
+
# so return value as if there was no timeout
|
1500
|
+
return internal_state.reason
|
1501
|
+
end
|
1502
|
+
end
|
1503
|
+
timeout_value
|
1504
|
+
end
|
1505
|
+
end
|
1506
|
+
|
1507
|
+
# Behaves as {Future#result} but has one additional optional argument
|
1508
|
+
# resolve_on_timeout.
|
1509
|
+
#
|
1510
|
+
# @!macro promises.resolvable.resolve_on_timeout
|
1511
|
+
# @return [::Array(Boolean, Object, Exception), nil]
|
1512
|
+
# @see Future#result
|
1513
|
+
def result(timeout = nil, resolve_on_timeout = nil)
|
1514
|
+
if wait_until_resolved timeout
|
1515
|
+
internal_state.result
|
1516
|
+
else
|
1517
|
+
if resolve_on_timeout
|
1518
|
+
unless resolve(*resolve_on_timeout, false)
|
1519
|
+
# if it fails to resolve it was resolved in the meantime
|
1520
|
+
# so return value as if there was no timeout
|
1521
|
+
internal_state.result
|
1522
|
+
end
|
1523
|
+
end
|
1524
|
+
# otherwise returns nil
|
1525
|
+
end
|
1526
|
+
end
|
1527
|
+
|
1289
1528
|
# Creates new future wrapping receiver, effectively hiding the resolve method and similar.
|
1290
1529
|
#
|
1291
1530
|
# @return [Future]
|
@@ -1358,14 +1597,6 @@ module Concurrent
|
|
1358
1597
|
super ResolvableFuture.new(self, default_executor)
|
1359
1598
|
end
|
1360
1599
|
|
1361
|
-
def fulfill(value, raise_on_reassign)
|
1362
|
-
resolve_with Fulfilled.new(value), raise_on_reassign
|
1363
|
-
end
|
1364
|
-
|
1365
|
-
def reject(reason, raise_on_reassign)
|
1366
|
-
resolve_with Rejected.new(reason), raise_on_reassign
|
1367
|
-
end
|
1368
|
-
|
1369
1600
|
public :evaluate_to
|
1370
1601
|
end
|
1371
1602
|
|
@@ -1615,7 +1846,7 @@ module Concurrent
|
|
1615
1846
|
|
1616
1847
|
value = internal_state.value
|
1617
1848
|
case value
|
1618
|
-
when
|
1849
|
+
when AbstractEventFuture
|
1619
1850
|
add_delayed_of value
|
1620
1851
|
value.add_callback_notify_blocked self, nil
|
1621
1852
|
countdown
|
@@ -1651,12 +1882,10 @@ module Concurrent
|
|
1651
1882
|
|
1652
1883
|
value = internal_state.value
|
1653
1884
|
case value
|
1654
|
-
when
|
1885
|
+
when AbstractEventFuture
|
1655
1886
|
add_delayed_of value
|
1656
1887
|
value.add_callback_notify_blocked self, nil
|
1657
1888
|
countdown
|
1658
|
-
when Event
|
1659
|
-
evaluate_to(lambda { raise TypeError, 'cannot flatten to Event' })
|
1660
1889
|
else
|
1661
1890
|
evaluate_to(lambda { raise TypeError, "returned value #{value.inspect} is not a Future" })
|
1662
1891
|
end
|
@@ -1670,8 +1899,9 @@ module Concurrent
|
|
1670
1899
|
|
1671
1900
|
private
|
1672
1901
|
|
1673
|
-
def initialize(delayed, blockers_count, default_executor)
|
1902
|
+
def initialize(delayed, blockers_count, default_executor, run_test)
|
1674
1903
|
super delayed, 1, Future.new(self, default_executor)
|
1904
|
+
@RunTest = run_test
|
1675
1905
|
end
|
1676
1906
|
|
1677
1907
|
def process_on_blocker_resolution(future, index)
|
@@ -1682,11 +1912,12 @@ module Concurrent
|
|
1682
1912
|
return 0
|
1683
1913
|
end
|
1684
1914
|
|
1685
|
-
value
|
1686
|
-
|
1687
|
-
|
1688
|
-
|
1689
|
-
|
1915
|
+
value = internal_state.value
|
1916
|
+
continuation_future = @RunTest.call value
|
1917
|
+
|
1918
|
+
if continuation_future
|
1919
|
+
add_delayed_of continuation_future
|
1920
|
+
continuation_future.add_callback_notify_blocked self, nil
|
1690
1921
|
else
|
1691
1922
|
resolve_with internal_state
|
1692
1923
|
end
|
@@ -1805,12 +2036,12 @@ module Concurrent
|
|
1805
2036
|
class AbstractAnyPromise < BlockedPromise
|
1806
2037
|
end
|
1807
2038
|
|
1808
|
-
class
|
2039
|
+
class AnyResolvedEventPromise < AbstractAnyPromise
|
1809
2040
|
|
1810
2041
|
private
|
1811
2042
|
|
1812
2043
|
def initialize(delayed, blockers_count, default_executor)
|
1813
|
-
super delayed, blockers_count,
|
2044
|
+
super delayed, blockers_count, Event.new(self, default_executor)
|
1814
2045
|
end
|
1815
2046
|
|
1816
2047
|
def resolvable?(countdown, future, index)
|
@@ -1818,16 +2049,16 @@ module Concurrent
|
|
1818
2049
|
end
|
1819
2050
|
|
1820
2051
|
def on_resolvable(resolved_future, index)
|
1821
|
-
resolve_with
|
2052
|
+
resolve_with RESOLVED, false
|
1822
2053
|
end
|
1823
2054
|
end
|
1824
2055
|
|
1825
|
-
class
|
2056
|
+
class AnyResolvedFuturePromise < AbstractAnyPromise
|
1826
2057
|
|
1827
2058
|
private
|
1828
2059
|
|
1829
2060
|
def initialize(delayed, blockers_count, default_executor)
|
1830
|
-
super delayed, blockers_count,
|
2061
|
+
super delayed, blockers_count, Future.new(self, default_executor)
|
1831
2062
|
end
|
1832
2063
|
|
1833
2064
|
def resolvable?(countdown, future, index)
|
@@ -1835,7 +2066,7 @@ module Concurrent
|
|
1835
2066
|
end
|
1836
2067
|
|
1837
2068
|
def on_resolvable(resolved_future, index)
|
1838
|
-
resolve_with
|
2069
|
+
resolve_with resolved_future.internal_state, false
|
1839
2070
|
end
|
1840
2071
|
end
|
1841
2072
|
|