1
1
require 'concurrent/synchronization'
2
2
require 'concurrent/atomic/atomic_boolean'
3
3
require 'concurrent/atomic/atomic_fixnum'
4
- require 'concurrent/lock_free_stack'
4
+ require 'concurrent/edge/ lock_free_stack'
5
5
require 'concurrent/errors'
6
6
7
7
module Concurrent
@@ -781,7 +781,7 @@ class Event < AbstractEventFuture
781
781
#
782
782
# @return [Future, Event]
783
783
def zip ( other )
784
- if other . is ?( Future )
784
+ if other . is_a ?( Future )
785
785
ZipFutureEventPromise . new ( other , self , @DefaultExecutor ) . future
786
786
else
787
787
ZipEventEventPromise . new ( self , other , @DefaultExecutor ) . event
@@ -825,7 +825,20 @@ def schedule(intended_time)
825
825
end . flat_event
826
826
end
827
827
828
- # TODO (pitr-ch 12-Jun-2016): add to_event, to_future
828
+ # Converts event to a future. The future is fulfilled when the event is resolved, the future may never fail.
829
+ #
830
+ # @return [Future]
831
+ def to_future
832
+ future = Promises . resolvable_future
833
+ ensure
834
+ chain_resolvable ( future )
835
+ end
836
+
837
+ # Returns self, since this is event
838
+ # @return [Event]
839
+ def to_event
840
+ self
841
+ end
829
842
830
843
# @!macro promises.method.with_default_executor
831
844
# @return [Event]
@@ -1111,6 +1124,21 @@ def apply(args, block)
1111
1124
internal_state . apply args , block
1112
1125
end
1113
1126
1127
+ # Converts future to event which is resolved when future is resolved by fulfillment or rejection.
1128
+ #
1129
+ # @return [Event]
1130
+ def to_event
1131
+ event = Promises . resolvable_event
1132
+ ensure
1133
+ chain_resolvable ( event )
1134
+ end
1135
+
1136
+ # Returns self, since this is a future
1137
+ # @return [Future]
1138
+ def to_future
1139
+ self
1140
+ end
1141
+
1114
1142
private
1115
1143
1116
1144
def rejected_resolution ( raise_on_reassign , state )
@@ -1195,7 +1223,8 @@ class ResolvableFuture < Future
1195
1223
# which triggers all dependent futures.
1196
1224
#
1197
1225
# @!macro promise.param.raise_on_reassign
1198
- def resolve ( fulfilled , value , reason , raise_on_reassign = true )
1226
+ def resolve ( fulfilled = true , value = nil , reason = nil , raise_on_reassign = true )
1227
+ # TODO (pitr-ch 25-Sep-2016): should the defaults be kept to match event resolve api?
1199
1228
resolve_with ( fulfilled ? Fulfilled . new ( value ) : Rejected . new ( reason ) , raise_on_reassign )
1200
1229
end
1201
1230
@@ -1288,8 +1317,8 @@ def resolve_with(new_state, raise_on_reassign = true)
1288
1317
# @return [Future]
1289
1318
def evaluate_to ( *args , block )
1290
1319
resolve_with Fulfilled . new ( block . call ( *args ) )
1291
- # TODO (pitr-ch 30-Jul-2016): figure out what should be rescued, there is an issue about it
1292
1320
rescue Exception => error
1321
+ # TODO (pitr-ch 30-Jul-2016): figure out what should be rescued, there is an issue about it
1293
1322
resolve_with Rejected . new ( error )
1294
1323
end
1295
1324
end
@@ -1358,7 +1387,7 @@ def on_resolution(future)
1358
1387
1359
1388
# @!visibility private
1360
1389
def touch
1361
- # TODO (pitr-ch 13-Jun-2016): on construction pass down references of delays to be touched, avoids extra casses
1390
+ # TODO (pitr-ch 13-Jun-2016): on construction pass down references of delays to be touched, avoids extra CASses
1362
1391
blocked_by . each ( &:touch )
1363
1392
end
1364
1393
@@ -1506,6 +1535,11 @@ def clear_blocked_by!
1506
1535
nil
1507
1536
end
1508
1537
1538
+ def blocked_by_add ( future )
1539
+ @BlockedBy . push future
1540
+ future . touch if self . future . touched?
1541
+ end
1542
+
1509
1543
def resolvable? ( countdown , future )
1510
1544
!@Future . internal_state . resolved? && super ( countdown , future )
1511
1545
end
@@ -1532,7 +1566,7 @@ def process_on_resolution(future)
1532
1566
value = internal_state . value
1533
1567
case value
1534
1568
when Future , Event
1535
- @BlockedBy . push value
1569
+ blocked_by_add value
1536
1570
value . add_callback :callback_notify_blocked , self
1537
1571
@Countdown . value
1538
1572
else
@@ -1566,7 +1600,7 @@ def process_on_resolution(future)
1566
1600
value = internal_state . value
1567
1601
case value
1568
1602
when Future
1569
- @BlockedBy . push value
1603
+ blocked_by_add value
1570
1604
value . add_callback :callback_notify_blocked , self
1571
1605
@Countdown . value
1572
1606
when Event
@@ -1599,7 +1633,8 @@ def process_on_resolution(future)
1599
1633
value = internal_state . value
1600
1634
case value
1601
1635
when Future
1602
- # @BlockedBy.push value
1636
+ # FIXME (pitr-ch 08-Dec-2016): will accumulate the completed futures
1637
+ blocked_by_add value
1603
1638
value . add_callback :callback_notify_blocked , self
1604
1639
else
1605
1640
resolve_with internal_state
@@ -1871,7 +1906,7 @@ module FactoryMethods
1871
1906
# only proof of concept
1872
1907
# @return [Future]
1873
1908
def select ( *channels )
1874
- # TODO (pitr-ch 26-Mar-2016): redo , has to be non-blocking
1909
+ # TODO (pitr-ch 26-Mar-2016): re-do , has to be non-blocking
1875
1910
future do
1876
1911
# noinspection RubyArgCount
1877
1912
Channel . select do |s |
@@ -1924,12 +1959,14 @@ def each_body(value, &block)
1924
1959
end
1925
1960
end
1926
1961
1962
+ # TODO example: parallel jobs, cancell them all when one fails, clean-up in zip
1927
1963
# inspired by https://msdn.microsoft.com/en-us/library/dd537607(v=vs.110).aspx
1928
1964
class Cancellation < Synchronization ::Object
1929
1965
safe_initialization!
1930
1966
1931
1967
def self . create ( future_or_event = Promises . resolvable_event , *resolve_args )
1932
- [ ( i = new ( future_or_event , *resolve_args ) ) , i . token ]
1968
+ cancellation = new ( future_or_event , *resolve_args )
1969
+ [ cancellation , cancellation . token ]
1933
1970
end
1934
1971
1935
1972
private_class_method :new
@@ -1960,20 +1997,18 @@ def initialize(cancel)
1960
1997
@Cancel = cancel
1961
1998
end
1962
1999
1963
- def event
1964
- @Cancel
2000
+ def to_event
2001
+ @Cancel . to_event
1965
2002
end
1966
2003
1967
- alias_method :future , :event
2004
+ def to_future
2005
+ @Cancel . to_future
2006
+ end
1968
2007
1969
2008
def on_cancellation ( *args , &block )
1970
2009
@Cancel . on_resolution *args , &block
1971
2010
end
1972
2011
1973
- def then ( *args , &block )
1974
- @Cancel . chain *args , &block
1975
- end
1976
-
1977
2012
def canceled?
1978
2013
@Cancel . resolved?
1979
2014
end
@@ -1985,13 +2020,14 @@ def loop_until_canceled(&block)
1985
2020
result
1986
2021
end
1987
2022
1988
- def raise_if_canceled
1989
- raise CancelledOperationError if canceled?
2023
+ def raise_if_canceled ( error = CancelledOperationError )
2024
+ raise error if canceled?
1990
2025
self
1991
2026
end
1992
2027
1993
- def join ( *tokens )
1994
- Token . new Promises . any_event ( @Cancel , *tokens . map ( &:event ) )
2028
+ def join ( *tokens , &block )
2029
+ block ||= -> tokens { Promises . any_event ( *tokens . map ( &:to_event ) ) }
2030
+ self . class . new block . call ( [ @Cancel , *tokens ] )
1995
2031
end
1996
2032
1997
2033
end
@@ -2002,7 +2038,7 @@ def join(*tokens)
2002
2038
# TODO (pitr-ch 27-Mar-2016): examples (scheduled to be cancelled in 10 sec)
2003
2039
end
2004
2040
2005
- class Throttle < Synchronization ::Object
2041
+ class Promises :: Throttle < Synchronization ::Object
2006
2042
2007
2043
safe_initialization!
2008
2044
private *attr_atomic ( :can_run )
@@ -2015,16 +2051,23 @@ def initialize(max)
2015
2051
end
2016
2052
2017
2053
def limit ( future = nil , &block )
2018
- # TODO (pitr-ch 11-Jun-2016): triggers should allocate resources when they are to be required
2019
- trigger = future ? future & get_event : get_event
2020
-
2021
- if block_given?
2022
- block . call ( trigger ) . on_resolution! { done }
2054
+ if future
2055
+ # future.chain { block.call(new_trigger & future).on_resolution! { done } }.flat
2056
+ block . call ( new_trigger & future ) . on_resolution! { done }
2023
2057
else
2024
- get_event
2058
+ if block_given?
2059
+ block . call ( new_trigger ) . on_resolution! { done }
2060
+ else
2061
+ new_trigger
2062
+ end
2025
2063
end
2026
2064
end
2027
2065
2066
+ # TODO (pitr-ch 10-Oct-2016): maybe just then?
2067
+ def then_limit ( &block )
2068
+ limit { |trigger | trigger . then &block }
2069
+ end
2070
+
2028
2071
def done
2029
2072
while true
2030
2073
current_can_run = can_run
@@ -2037,7 +2080,7 @@ def done
2037
2080
2038
2081
private
2039
2082
2040
- def get_event
2083
+ def new_trigger
2041
2084
while true
2042
2085
current_can_run = can_run
2043
2086
if compare_and_set_can_run current_can_run , current_can_run - 1
@@ -2059,5 +2102,18 @@ def throttle(throttle, &throttled_future)
2059
2102
throttle . limit ( self , &throttled_future )
2060
2103
end
2061
2104
2105
+ def then_throttle ( throttle , &block )
2106
+ throttle ( throttle ) { |trigger | trigger . then &block }
2107
+ end
2108
+
2109
+ end
2110
+
2111
+ module Promises ::FactoryMethods
2112
+
2113
+ # @!visibility private
2114
+
2115
+ def throttle ( count )
2116
+ Promises ::Throttle . new count
2117
+ end
2062
2118
end
2063
2119
end
0 commit comments