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.
@@ -15,8 +15,9 @@ module Concurrent
15
15
  # @!macro internal_implementation_note
16
16
  HashImplementation = case
17
17
  when Concurrent.on_cruby?
18
- # Because MRI never runs code in parallel, the existing
19
- # non-thread-safe structures should usually work fine.
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
-
@@ -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
- # @!macro promises.future-on1
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.future-on2
100
- # @!macro promises.param.default_executor
101
- # @!macro promises.param.args
102
- # @yield [*args] to the task.
103
- # @!macro promise.param.task-future
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 create(nil, default_executor = self.default_executor)
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 create(a_future, default_executor = self.default_executor)
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 create(an_event, default_executor = self.default_executor)
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 create(exception, default_executor = self.default_executor)
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 create(value, default_executor = self.default_executor)
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
- # @!macro promises.future-on1
189
- # The task will be evaluated only after the future is touched, see {AbstractEventFuture#touch}
192
+ # Creates new event or future which is resolved only after it is touched,
193
+ # see {Concurrent::AbstractEventFuture#touch}.
190
194
  #
191
- # @!macro promises.future-on2
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.chain(*args, &task)
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
- # @!macro promises.future-on1
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.future-on2
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.chain(*args, &task)
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
- # @private
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
- private_constant :State
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
- private_constant :Pending
359
+ # @!visibility private
360
+ class Reserved < Pending
361
+ end
339
362
 
340
- # @private
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
- private_constant :ResolvedWithResult
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
- private_constant :Fulfilled
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
- private_constant :FulfilledArray
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
- private_constant :Rejected
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
- private_constant :PartiallyRejected
471
-
472
- PENDING = Pending.new
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
- private(*attr_atomic(:internal_state) - [:internal_state])
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 [Future, true, false] self implies timeout was not used, true implies timeout was used
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 [Exception] reason
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 [Exception] reason
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 [Exception] reason
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
- # @return [Object, nil] the value of the Future when fulfilled, nil on timeout or rejection.
911
- def value(timeout = nil)
912
- internal_state.value if wait_until_resolved timeout
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
- # @return [Exception, nil] nil on timeout or fulfillment.
922
- def reason(timeout = nil)
923
- internal_state.reason if wait_until_resolved timeout
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, Exception), nil] triplet of fulfilled?, value, reason, or nil
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, nil on timeout.
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
- internal_state.value if wait_until_resolved! timeout
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
- # @raise [StandardError] when raising not rejected future
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].clone.exception(*args)
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
- raise Concurrent::MultipleAssignmentError.new(
1171
- "Future can be resolved only once. It's #{result}, trying to set #{state.result}.",
1172
- current_result: result, new_result: state.result)
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 by user.
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
- def resolve(raise_on_reassign = true)
1231
- resolve_with RESOLVED, raise_on_reassign
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
- def resolve(fulfilled = true, value = nil, reason = nil, raise_on_reassign = true)
1251
- resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason), raise_on_reassign)
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
- def fulfill(value, raise_on_reassign = true)
1259
- promise.fulfill(value, raise_on_reassign)
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
- def reject(reason, raise_on_reassign = true)
1267
- promise.reject(reason, raise_on_reassign)
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 Future, Event
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 Future
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 = internal_state.value
1686
- case value
1687
- when Future
1688
- add_delayed_of value
1689
- value.add_callback_notify_blocked self, nil
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 AnyResolvedFuturePromise < AbstractAnyPromise
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, Future.new(self, default_executor)
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 resolved_future.internal_state, false
2052
+ resolve_with RESOLVED, false
1822
2053
  end
1823
2054
  end
1824
2055
 
1825
- class AnyResolvedEventPromise < AbstractAnyPromise
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, Event.new(self, default_executor)
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 RESOLVED, false
2069
+ resolve_with resolved_future.internal_state, false
1839
2070
  end
1840
2071
  end
1841
2072