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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 21429b1dcafe35b79201be08fd02442171fc68fb
4
- data.tar.gz: 2de92c445f74fbf670cb605e760b9a4c413597c4
3
+ metadata.gz: b3b0a6f42dd53529fb35370f6d9d6b1957aa576c
4
+ data.tar.gz: b960e25b2834254aaa00685cf38e41fc3a481e9f
5
5
  SHA512:
6
- metadata.gz: cb45d38d9f966ff005121fec47adc053349675ee7d018662e7b49c67a67fdd3c3bb603cca559d3b39c4243f62ff099905c72b320b2ada6b29d8d5fc00541df49
7
- data.tar.gz: 2e1c9c51ec89bb08e32ced3883cf5fa8cf472bdf0a98616af1966e7929c2c018ee832b747262fff13831295aa381c67cf883f207f60c9dfa29355c504803563d
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::Utils::AbstractWorker
16
- # def work(message)
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 |balancer, index|
22
- # Worker.spawn name: "worker-#{index}", supervise: true, args: [balancer]
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
@@ -93,15 +93,7 @@ module Concurrent
93
93
  #
94
94
  # @param [Object] initial the initial value
95
95
  #
96
- # @!macro [attach] executor_and_deref_options
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(*args, &task)
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
- ImmediatePromise.new(default_executor, *args).future.then(&task)
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(*args, &task)
52
- delay_on :io, *args, &task
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, *args, &task)
64
- schedule_on :io, intended_time, *args, &task
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
- AllPromise.new(futures, :io).future
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 timeout
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
- # TODO take block optionally
223
- # Zip with futures producing new Future
224
- # @return [Future]
225
- def zip(*futures)
226
- AllPromise.new([self, *futures], @DefaultExecutor).future
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 [Future]
245
+ # @return [Event]
233
246
  def delay
234
- zip(Delay.new(@DefaultExecutor).future)
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 [Future]
239
- def schedule(intended_time)
240
- chain { ScheduledPromise.new(@DefaultExecutor, intended_time).event.zip(self) }.flat
241
- end
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
- self.zip(Concurrent.select(*channels))
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 [Future]
279
+ # @return [Event]
263
280
  def with_default_executor(executor)
264
- AllPromise.new([self], executor).future
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 complete(raise_on_reassign = true)
282
- if complete_state
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('multiple assignment') if raise_on_reassign
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
- break if completed?
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?(state)
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! timeout
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!(timeout)
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.exception(*args)
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
- FlattingPromise.new(self, level, @DefaultExecutor).future
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 } # FIXME it's blocking for now
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 complete(success, value, reason, raise_on_reassign = true)
597
- if (new_state = complete_state success, value, reason)
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 new_state
716
+ call_callbacks state
601
717
  else
602
- raise reason || Concurrent::MultipleAssignmentError.new('multiple assignment') if raise_on_reassign
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
- self
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
- super raise_on_reassign
808
+ complete_with COMPLETED, raise_on_reassign
701
809
  end
702
810
 
703
811
  def hide_completable
704
- Concurrent.zip(self)
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
- super success, value, reason, raise_on_reassign
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
- Concurrent.zip(self)
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 complete(*args)
797
- @Future.complete(*args)
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
- complete true, block.call(*args), nil
908
+ complete_with Future::Success.new(block.call(*args))
803
909
  rescue => error
804
- complete false, nil, error
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
- complete(true, value, nil)
933
+ complete_with Future::Success.new(value)
832
934
  end
833
935
 
834
936
  def try_success(value)
835
- complete(true, value, nil, false)
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
- complete(false, nil, reason)
946
+ complete_with Future::Failed.new(reason)
845
947
  end
846
948
 
847
949
  def try_fail(reason = StandardError.new)
848
- !!complete(false, nil, reason, false)
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, &block)
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
- complete false, nil, done_future.reason
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.reason, @Task) { |reason, task| evaluate_to reason, task }
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
- complete true, done_future.value, nil
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 ImmediatePromise < InnerPromise
998
- def initialize(default_executor, *args)
999
- super(if args.empty?
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 FlattingPromise < BlockedPromise
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
- complete *done_future.result
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 AllPromise < BlockedPromise
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
- complete
1268
+ complete_with Event::COMPLETED
1096
1269
  else
1097
- complete(true, values.size == 1 ? values.first : values, nil)
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
- # TODO what about other reasons?
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
- complete *done_future.result, false
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
- if @Args.empty?
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, *args)
1139
- @Args = args
1140
- super(if args.empty?
1141
- Event.new(self, default_executor)
1142
- else
1143
- Future.new(self, default_executor)
1144
- end)
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, *args)
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
- use_event = args.empty?
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, *args) do |*args|
1182
- if use_event
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.pre2
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-08 00:00:00.000000000 Z
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.pre2
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.pre2
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.7
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.