aws-flow 1.0.8 → 1.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +1 -1
- data/Rakefile +18 -31
- data/aws-flow.gemspec +1 -1
- data/lib/aws/decider.rb +1 -2
- data/lib/aws/decider/activity.rb +99 -53
- data/lib/aws/decider/activity_definition.rb +43 -7
- data/lib/aws/decider/async_decider.rb +56 -57
- data/lib/aws/decider/async_retrying_executor.rb +4 -5
- data/lib/aws/decider/data_converter.rb +2 -2
- data/lib/aws/decider/decider.rb +46 -41
- data/lib/aws/decider/decision_context.rb +2 -2
- data/lib/aws/decider/exceptions.rb +6 -6
- data/lib/aws/decider/executor.rb +15 -11
- data/lib/aws/decider/flow_defaults.rb +54 -22
- data/lib/aws/decider/generic_client.rb +7 -7
- data/lib/aws/decider/history_helper.rb +0 -0
- data/lib/aws/decider/implementation.rb +5 -5
- data/lib/aws/decider/options.rb +285 -155
- data/lib/aws/decider/state_machines.rb +10 -10
- data/lib/aws/decider/task_handler.rb +5 -5
- data/lib/aws/decider/task_poller.rb +152 -15
- data/lib/aws/decider/utilities.rb +14 -14
- data/lib/aws/decider/version.rb +1 -1
- data/lib/aws/decider/worker.rb +21 -20
- data/lib/aws/decider/workflow_client.rb +78 -31
- data/lib/aws/decider/workflow_clock.rb +1 -1
- data/lib/aws/decider/workflow_definition.rb +5 -5
- data/lib/aws/decider/workflow_definition_factory.rb +1 -1
- data/lib/aws/decider/workflow_enabled.rb +1 -1
- data/lib/aws/flow/async_backtrace.rb +19 -18
- data/lib/aws/flow/async_scope.rb +32 -16
- data/lib/aws/flow/begin_rescue_ensure.rb +61 -56
- data/lib/aws/flow/fiber.rb +14 -6
- data/lib/aws/flow/flow_utils.rb +9 -6
- data/lib/aws/flow/future.rb +43 -18
- data/lib/aws/flow/implementation.rb +34 -18
- data/lib/aws/flow/simple_dfa.rb +12 -4
- data/lib/aws/flow/tasks.rb +120 -86
- data/{test/aws → spec/aws/integration}/integration_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/async_backtrace_spec.rb +1 -0
- data/{test/aws → spec/aws/unit}/async_scope_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/begin_rescue_ensure_spec.rb +90 -2
- data/{test/aws → spec/aws/unit}/decider_spec.rb +41 -53
- data/{test/aws → spec/aws/unit}/external_task_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/factories.rb +0 -0
- data/{test/aws → spec/aws/unit}/fiber_condition_variable_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/fiber_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/flow_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/future_spec.rb +0 -0
- data/{test/aws → spec/aws/unit}/preinclude_tests.rb +0 -0
- data/{test/aws → spec/aws/unit}/rubyflow.rb +0 -0
- data/{test/aws → spec/aws/unit}/simple_dfa_spec.rb +0 -0
- data/{test/aws → spec}/spec_helper.rb +0 -0
- metadata +30 -30
data/lib/aws/flow/fiber.rb
CHANGED
@@ -13,33 +13,40 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
##
|
15
15
|
|
16
|
-
# This file contains our implementation of fibers for 1.8
|
16
|
+
# This file contains our implementation of fibers for 1.8.
|
17
17
|
module AWS
|
18
18
|
module Flow
|
19
19
|
module Core
|
20
20
|
require 'fiber'
|
21
|
+
# @api private
|
21
22
|
class FlowFiber < Fiber
|
23
|
+
# @api private
|
22
24
|
def initialize(*args)
|
23
25
|
ObjectSpace.define_finalizer(self, self.class.finalize(self.object_id))
|
24
26
|
super(args)
|
25
27
|
end
|
28
|
+
# @api private
|
26
29
|
class << self
|
27
30
|
attr_accessor :local_variables
|
28
31
|
end
|
29
32
|
@local_variables = Hash.new {|hash, key| hash[key] = {}}
|
33
|
+
# @api private
|
30
34
|
def self.finalize(obj_id)
|
31
35
|
proc { FlowFiber.local_variables.delete(obj_id) }
|
32
36
|
end
|
37
|
+
# @api private
|
33
38
|
def self.[](index)
|
34
39
|
self.local_variables[index]
|
35
40
|
end
|
41
|
+
# @api private
|
36
42
|
def self.[]=(key, value)
|
37
43
|
self.local_variables[key] = value
|
38
44
|
end
|
39
45
|
|
40
|
-
#
|
46
|
+
# Unsets all the values for ancestors of this fiber, assuming that
|
41
47
|
# they have the same value for key. That is, they will unset upwards until
|
42
|
-
# the first time the value stored at key is changed
|
48
|
+
# the first time the value stored at key is changed.
|
49
|
+
# @api private
|
43
50
|
def self.unset(current_fiber, key)
|
44
51
|
current_value = FlowFiber[current_fiber.object_id][key]
|
45
52
|
parent = FlowFiber[current_fiber.object_id][:parent]
|
@@ -54,8 +61,9 @@ module AWS
|
|
54
61
|
FlowFiber[current_fiber.object_id].delete(key)
|
55
62
|
end
|
56
63
|
|
64
|
+
# @api private
|
57
65
|
def initialize
|
58
|
-
# Child fibers should inherit their parents FiberLocals
|
66
|
+
# Child fibers should inherit their parents FiberLocals.
|
59
67
|
FlowFiber[Fiber.current.object_id].each_pair do |key, val|
|
60
68
|
FlowFiber[self.object_id][key] = val
|
61
69
|
end
|
@@ -63,15 +71,15 @@ module AWS
|
|
63
71
|
super
|
64
72
|
end
|
65
73
|
|
74
|
+
# @api private
|
66
75
|
def [](key)
|
67
76
|
FlowFiber[self.object_id][key]
|
68
77
|
end
|
78
|
+
# @api private
|
69
79
|
def []=(key, value)
|
70
80
|
FlowFiber[self.object_id][key] = value
|
71
81
|
end
|
72
|
-
|
73
82
|
end
|
74
|
-
|
75
83
|
end
|
76
84
|
end
|
77
85
|
end
|
data/lib/aws/flow/flow_utils.rb
CHANGED
@@ -17,7 +17,9 @@
|
|
17
17
|
module AWS
|
18
18
|
module Flow
|
19
19
|
module Core
|
20
|
+
# @api private
|
20
21
|
class IllegalStateException < Exception; end
|
22
|
+
# @api private
|
21
23
|
class CancellationException < Exception
|
22
24
|
attr_accessor :reason, :details
|
23
25
|
def initialize(reason = nil, details = nil)
|
@@ -26,14 +28,15 @@ module AWS
|
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
31
|
+
# @api private
|
29
32
|
def make_backtrace(parent_backtrace)
|
30
|
-
# 1 frame for the function that actually removes the stack traces
|
33
|
+
# 1 frame for the function that actually removes the stack traces.
|
31
34
|
# 1 frame for the function that calls into the function that removes
|
32
|
-
# frames in AsyncBacktrace
|
33
|
-
# 1 frame for the call into this function
|
34
|
-
# 1 frame for the initialize call of the
|
35
|
-
# 1 frame for the new call into the
|
36
|
-
# 1 frame for the AsyncScope initialize that the
|
35
|
+
# frames in AsyncBacktrace.
|
36
|
+
# 1 frame for the call into this function.
|
37
|
+
# 1 frame for the initialize call of the BeginRescueEnsure or ExternalTask.
|
38
|
+
# 1 frame for the new call into the BeginRescueEnsure or ExternalTask.
|
39
|
+
# 1 frame for the AsyncScope initialize that the BeginRescueEnsure/ExternalTask has to be in.
|
37
40
|
|
38
41
|
# "./lib/aws/rubyflow/asyncBacktrace.rb:75:in `caller'"
|
39
42
|
# "./lib/aws/rubyflow/asyncBacktrace.rb:21:in `create'"
|
data/lib/aws/flow/future.rb
CHANGED
@@ -20,17 +20,23 @@ module AWS
|
|
20
20
|
module Core
|
21
21
|
class AlreadySetException < Exception; end
|
22
22
|
|
23
|
-
#
|
24
|
-
# provided to
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
23
|
+
# Represents the result of an asynchronous computation. Methods are
|
24
|
+
# provided to:
|
25
|
+
#
|
26
|
+
# * retrieve the result of the computation, once it is complete ({Future#get}).
|
27
|
+
# * check if the computation is complete ({Future#set?})
|
28
|
+
# * execute a block when computation is complete ({Future#on_set})
|
29
|
+
#
|
30
|
+
# The result of a Future can only be retrieved when the computation has
|
31
|
+
# completed. {Future#get} blocks execution, if necessary, until the
|
32
|
+
# Future is ready. This is okay: because it will block that fiber,
|
33
|
+
# another fiber will start executing.
|
34
|
+
#
|
30
35
|
class Future
|
31
36
|
|
32
|
-
# Sets the value of the
|
33
|
-
# to get when this future wasn't ready.
|
37
|
+
# Sets the value of the {Future}, and notifies all of the fibers that
|
38
|
+
# tried to call {#get} when this future wasn't ready.
|
39
|
+
# @api private
|
34
40
|
def set(result=nil)
|
35
41
|
raise AlreadySetException if @set
|
36
42
|
@set = true
|
@@ -40,14 +46,21 @@ module AWS
|
|
40
46
|
self
|
41
47
|
end
|
42
48
|
|
43
|
-
#
|
44
|
-
#
|
49
|
+
# Is the object is an AWS Flow future? AWS Flow futures *must* have a
|
50
|
+
# {#get} method.
|
51
|
+
# @api private
|
45
52
|
def is_flow_future?
|
46
53
|
true
|
47
54
|
end
|
48
55
|
|
49
|
-
# Blocks if
|
50
|
-
#
|
56
|
+
# Blocks if future is not set. Returns the result of the future once
|
57
|
+
# {#set} is true.
|
58
|
+
#
|
59
|
+
# @return
|
60
|
+
# The result of the future.
|
61
|
+
#
|
62
|
+
# @raise CancellationError
|
63
|
+
# when the task is cancelled.
|
51
64
|
def get
|
52
65
|
until @set
|
53
66
|
@conditional ||= FiberConditionVariable.new
|
@@ -56,16 +69,22 @@ module AWS
|
|
56
69
|
@result
|
57
70
|
end
|
58
71
|
|
72
|
+
# @return
|
73
|
+
# true if the {Future} has been set.
|
59
74
|
def set?
|
60
75
|
@set
|
61
76
|
end
|
62
77
|
|
78
|
+
# Unsets the future.
|
79
|
+
#
|
80
|
+
# @api private
|
63
81
|
def unset
|
64
82
|
@set = nil
|
65
83
|
@result = nil
|
66
84
|
end
|
67
85
|
|
68
|
-
#
|
86
|
+
# Adds a callback, passed in as a block, which will fire when the future
|
87
|
+
# is set.
|
69
88
|
def on_set(&block)
|
70
89
|
@listeners ||= []
|
71
90
|
# TODO probably want to use lambda here
|
@@ -73,19 +92,23 @@ module AWS
|
|
73
92
|
end
|
74
93
|
end
|
75
94
|
|
76
|
-
#
|
95
|
+
# Represents a fiber condition variable.
|
96
|
+
# Based on the Ruby core source:
|
77
97
|
# https://github.com/ruby/ruby/blob/trunk/lib/thread.rb
|
98
|
+
# @api private
|
78
99
|
class FiberConditionVariable
|
79
100
|
#
|
80
101
|
# Creates a new ConditionVariable
|
81
102
|
#
|
103
|
+
# @api private
|
82
104
|
def initialize
|
83
105
|
@waiters = []
|
84
106
|
end
|
85
107
|
|
86
|
-
|
87
|
-
#
|
88
|
-
#
|
108
|
+
# Have the current fiber wait on this condition variable, and wake up
|
109
|
+
# when the FiberConditionVariable is signaled/broadcasted.
|
110
|
+
#
|
111
|
+
# @api private
|
89
112
|
def wait
|
90
113
|
fiber = ::Fiber.current
|
91
114
|
@waiters << fiber
|
@@ -96,6 +119,7 @@ module AWS
|
|
96
119
|
#
|
97
120
|
# Wakes up the first fiber in line waiting for this lock.
|
98
121
|
#
|
122
|
+
# @api private
|
99
123
|
def signal
|
100
124
|
t = @waiters.shift
|
101
125
|
t.schedule if t && t.alive?
|
@@ -105,6 +129,7 @@ module AWS
|
|
105
129
|
#
|
106
130
|
# Wakes up all fibers waiting for this lock.
|
107
131
|
#
|
132
|
+
# @api private
|
108
133
|
def broadcast
|
109
134
|
signal until @waiters.empty?
|
110
135
|
self
|
@@ -13,17 +13,22 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
#++
|
15
15
|
|
16
|
-
# This file contains the externally visible parts of flow that are expected to be used by customers of flow
|
16
|
+
# This file contains the externally visible parts of flow that are expected to be used by customers of flow.
|
17
17
|
module AWS
|
18
18
|
module Flow
|
19
19
|
module Core
|
20
|
+
|
21
|
+
# A basic exception class with no context.
|
20
22
|
class NoContextException < Exception; end
|
21
23
|
|
24
|
+
# @param [Future] future
|
25
|
+
# Unused; defaults to **nil**.
|
26
|
+
#
|
22
27
|
# @param block
|
23
28
|
# The block of code to be executed when the task is run.
|
24
29
|
#
|
25
30
|
# @raise [NoContextException]
|
26
|
-
# If the current fiber does not respond to
|
31
|
+
# If the current fiber does not respond to `Fiber.__context__`.
|
27
32
|
#
|
28
33
|
# @return [Future]
|
29
34
|
# The tasks result, which is a {Future}.
|
@@ -46,7 +51,7 @@ module AWS
|
|
46
51
|
# The tasks result, which is a {Future}.
|
47
52
|
#
|
48
53
|
# @raise [NoContextException]
|
49
|
-
# If the current fiber does not respond to
|
54
|
+
# If the current fiber does not respond to `Fiber.__context__`.
|
50
55
|
#
|
51
56
|
def daemon_task(&block)
|
52
57
|
fiber = ::Fiber.current
|
@@ -65,7 +70,7 @@ module AWS
|
|
65
70
|
# @return [nil]
|
66
71
|
#
|
67
72
|
# @raise [NoContextException]
|
68
|
-
# If the current fiber does not respond to
|
73
|
+
# If the current fiber does not respond to `Fiber.__context__`.
|
69
74
|
#
|
70
75
|
def external_task(&block)
|
71
76
|
fiber = ::Fiber.current
|
@@ -77,15 +82,17 @@ module AWS
|
|
77
82
|
nil
|
78
83
|
end
|
79
84
|
|
80
|
-
|
85
|
+
# Creates a new error handler for asynchronous tasks.
|
86
|
+
#
|
87
|
+
# @param block
|
88
|
+
# A block that defines the {BeginRescueEnsure#begin}, {BeginRescueEnsure#rescue}, and {BeginRescueEnsure#ensure}
|
89
|
+
# methods.
|
81
90
|
#
|
91
|
+
# @return
|
92
|
+
# The result of the `begin` statement if there is no error; otherwise the value of the `return` statement.
|
82
93
|
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
# * *Returns* :
|
86
|
-
# - The result of the begin statement, if there is no error, otherwise the value of the return statement
|
87
|
-
# * *Raises* :
|
88
|
-
# - +NoContextException+ -> If the current fiber does not respond to #__context__
|
94
|
+
# @raise [NoContextException]
|
95
|
+
# If the current fiber does not respond to `Fiber.__context__`.
|
89
96
|
#
|
90
97
|
def error_handler(&block)
|
91
98
|
fiber = ::Fiber.current
|
@@ -98,15 +105,24 @@ module AWS
|
|
98
105
|
begin_rescue_ensure
|
99
106
|
end
|
100
107
|
|
101
|
-
# @
|
102
|
-
#
|
103
|
-
# {BeginRescueEnsure#begin}, {BeginRescueEnsure#rescue}, and {BeginRescueEnsure#ensure} methods.
|
104
|
-
# @!visibility private
|
108
|
+
# @api private
|
109
|
+
#
|
105
110
|
def _error_handler(&block)
|
106
111
|
error_handler(&block).result
|
107
112
|
end
|
108
113
|
|
109
114
|
|
115
|
+
# Waits for the passed-in function to complete, setting values for the provided futures when it does.
|
116
|
+
#
|
117
|
+
# @param function
|
118
|
+
# The function to wait for.
|
119
|
+
#
|
120
|
+
# @param [Array<Future>] futures
|
121
|
+
# A list of futures to provide values for when the function completes.
|
122
|
+
#
|
123
|
+
# @return [Array<Future>]
|
124
|
+
# A list of the set futures, in the order of being set.
|
125
|
+
#
|
110
126
|
def wait_for_function(function, *futures)
|
111
127
|
conditional = FiberConditionVariable.new
|
112
128
|
futures.flatten!
|
@@ -125,10 +141,10 @@ module AWS
|
|
125
141
|
|
126
142
|
# Blocks until *any* of the arguments are set.
|
127
143
|
#
|
128
|
-
# @param [Array] futures
|
144
|
+
# @param [Array<Future>] futures
|
129
145
|
# A list of futures to wait for. The function will return when at least one of these is set.
|
130
146
|
#
|
131
|
-
# @return [Array]
|
147
|
+
# @return [Array<Future>]
|
132
148
|
# A list of the set futures, in the order of being set.
|
133
149
|
#
|
134
150
|
def wait_for_any(*futures)
|
@@ -140,7 +156,7 @@ module AWS
|
|
140
156
|
# @param [Array<Future>] futures
|
141
157
|
# A list of futures to wait for. The function will return only when all of them are set.
|
142
158
|
#
|
143
|
-
# @return [Array]
|
159
|
+
# @return [Array<Future>]
|
144
160
|
# A list of the set futures, in the order of being set.
|
145
161
|
#
|
146
162
|
def wait_for_all(*futures)
|
data/lib/aws/flow/simple_dfa.rb
CHANGED
@@ -17,16 +17,18 @@ module AWS
|
|
17
17
|
module Flow
|
18
18
|
module Core
|
19
19
|
|
20
|
-
# Contains a
|
21
|
-
# before moving to the next state
|
20
|
+
# Contains a data flow analysis (DFA)-like framework, where transition functions can perform arbitrary computation
|
21
|
+
# before moving to the next state.
|
22
|
+
# @api private
|
22
23
|
module SimpleDFA
|
23
24
|
attr_accessor :transitions, :symbols, :states, :start_state
|
24
25
|
|
25
|
-
# Creates a new SimpleDFA instance.
|
26
|
+
# Creates a new `SimpleDFA` instance.
|
26
27
|
#
|
27
28
|
# @param start_state
|
28
29
|
# The state with which to start the framework.
|
29
30
|
#
|
31
|
+
# @api private
|
30
32
|
def init(start_state)
|
31
33
|
include InstanceMethods
|
32
34
|
@start_state = start_state
|
@@ -39,6 +41,7 @@ module AWS
|
|
39
41
|
# @return the start state
|
40
42
|
# The start state that was provided when this instance was created.
|
41
43
|
#
|
44
|
+
# @api private
|
42
45
|
def get_start_state
|
43
46
|
@start_state
|
44
47
|
end
|
@@ -46,10 +49,12 @@ module AWS
|
|
46
49
|
# @return [Array]
|
47
50
|
# The list of all transitions that were added with {#add_transition}.
|
48
51
|
#
|
52
|
+
# @api private
|
49
53
|
def get_transitions
|
50
54
|
@transitions
|
51
55
|
end
|
52
56
|
|
57
|
+
# @api private
|
53
58
|
def define_general(state, &block)
|
54
59
|
@symbols.each do |symbol|
|
55
60
|
if @transitions[[state, symbol]].nil?
|
@@ -58,19 +63,23 @@ module AWS
|
|
58
63
|
end
|
59
64
|
end
|
60
65
|
|
66
|
+
# @api private
|
61
67
|
def add_transition(state, symbol, &block)
|
62
68
|
@symbols << symbol unless @symbols.include? symbol
|
63
69
|
@states << state unless @states.include? state
|
64
70
|
@transitions[[state, symbol]] = block
|
65
71
|
end
|
66
72
|
|
73
|
+
# @api private
|
67
74
|
def uncovered_transitions
|
68
75
|
@states.product(@symbols) - @transitions.keys
|
69
76
|
end
|
70
77
|
|
78
|
+
# @api private
|
71
79
|
module InstanceMethods
|
72
80
|
attr_accessor :current_state
|
73
81
|
|
82
|
+
# @api private
|
74
83
|
def consume(symbol)
|
75
84
|
@current_state ||= self.class.get_start_state
|
76
85
|
func_to_call = self.class.get_transitions[[@current_state, symbol]]
|
@@ -79,7 +88,6 @@ module AWS
|
|
79
88
|
end
|
80
89
|
end
|
81
90
|
end
|
82
|
-
|
83
91
|
end
|
84
92
|
end
|
85
93
|
end
|
data/lib/aws/flow/tasks.rb
CHANGED
@@ -13,37 +13,48 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
##
|
15
15
|
|
16
|
-
# Contains all the task methods, which allow arbitrary blocks of code to be run asynchronously
|
16
|
+
# Contains all the task methods, which allow arbitrary blocks of code to be run asynchronously.
|
17
17
|
|
18
18
|
module AWS
|
19
19
|
module Flow
|
20
20
|
module Core
|
21
|
+
# @api private
|
21
22
|
class Task < FlowFiber
|
22
23
|
attr_reader :result, :block
|
23
24
|
attr_accessor :backtrace, :__context__, :parent
|
24
25
|
|
25
|
-
# Creates a new
|
26
|
+
# Creates a new task.
|
26
27
|
#
|
27
28
|
# @param __context__
|
28
|
-
# A
|
29
|
-
# find the __context__ which the new
|
29
|
+
# A task needs a reference to the __context__ that created it so that when the "task" macro is called it can
|
30
|
+
# find the __context__, which the new task should be added to.
|
30
31
|
#
|
31
32
|
# @param block
|
32
33
|
# A block of code that will be run by the task.
|
33
34
|
#
|
35
|
+
# @api private
|
34
36
|
def initialize(__context__, &block)
|
35
37
|
@__context__ = __context__
|
36
38
|
@result = Future.new
|
37
39
|
@block = block
|
38
40
|
|
39
41
|
# Is the task alive?
|
42
|
+
#
|
43
|
+
# @return Boolean
|
44
|
+
# true if the task is alive and has not been canceled.
|
45
|
+
#
|
46
|
+
# @api private
|
40
47
|
def alive?
|
41
48
|
super && !@cancelled
|
42
49
|
#!!@alive# && !@cancelled
|
43
50
|
end
|
44
51
|
|
52
|
+
# Retrieves the executor for this task.
|
53
|
+
#
|
45
54
|
# @return
|
46
55
|
# The executor for this task.
|
56
|
+
#
|
57
|
+
# @api private
|
47
58
|
def executor
|
48
59
|
@__context__.executor
|
49
60
|
end
|
@@ -51,7 +62,7 @@ module AWS
|
|
51
62
|
super() do
|
52
63
|
begin
|
53
64
|
# Not return because 1.9 will freak about local jump problems if you
|
54
|
-
# try to return, as this is inside a block
|
65
|
+
# try to return, as this is inside a block.
|
55
66
|
next if @cancelled
|
56
67
|
@result.set(lambda(&block).call)
|
57
68
|
next if @cancelled
|
@@ -68,32 +79,37 @@ module AWS
|
|
68
79
|
end
|
69
80
|
|
70
81
|
#
|
71
|
-
# Passes
|
82
|
+
# Passes all `get_heirs` calls to the class that is holding the context,
|
83
|
+
# to ensure uniform handling of `get_heirs`.
|
72
84
|
#
|
85
|
+
# @api private
|
73
86
|
def get_heirs
|
74
87
|
@__context__.get_heirs
|
75
88
|
end
|
76
89
|
|
90
|
+
# Will always be false. Provides a common API for BeginRescueEnsure to
|
91
|
+
# ensure they are maintaining their nonDaemonHeirsCount correctly.
|
77
92
|
#
|
78
|
-
#
|
79
|
-
#
|
93
|
+
# @api private
|
80
94
|
def is_daemon?
|
81
95
|
return false
|
82
96
|
end
|
83
97
|
|
84
|
-
# Used by Future#signal to schedule the task for re-evaluation.
|
98
|
+
# Used by {Future#signal} to schedule the task for re-evaluation.
|
85
99
|
#
|
86
100
|
# This will simply add the task back to the list of things to be run in the parent's event loop.
|
87
101
|
#
|
102
|
+
# @api private
|
88
103
|
def schedule
|
89
104
|
@__context__ << self
|
90
105
|
end
|
91
106
|
|
92
|
-
#
|
107
|
+
# Prevents the execution of this particular task, if possible.
|
93
108
|
#
|
94
109
|
# @param error
|
95
|
-
# The error that is the cause of the cancellation
|
110
|
+
# The error that is the cause of the cancellation.
|
96
111
|
#
|
112
|
+
# @api private
|
97
113
|
def cancel(error)
|
98
114
|
@cancelled = true
|
99
115
|
@__context__.remove(self)
|
@@ -102,9 +118,9 @@ module AWS
|
|
102
118
|
# Fails the given task with the specified error.
|
103
119
|
#
|
104
120
|
# @param error
|
105
|
-
# The error that is the cause of the
|
121
|
+
# The error that is the cause of the failure.
|
106
122
|
#
|
107
|
-
#
|
123
|
+
# @api private
|
108
124
|
def fail(this_task, error)
|
109
125
|
@__context__.fail(this_task, error)
|
110
126
|
end
|
@@ -112,73 +128,80 @@ module AWS
|
|
112
128
|
# raise error
|
113
129
|
# end
|
114
130
|
|
115
|
-
# Adds a task to this task's context
|
131
|
+
# Adds a task to this task's context.
|
116
132
|
#
|
117
133
|
# @param this_task
|
118
134
|
# The task to add.
|
119
135
|
#
|
136
|
+
# @api private
|
120
137
|
def <<(this_task)
|
121
138
|
@__context__.parent << this_task
|
122
139
|
end
|
123
140
|
|
124
|
-
# Removes a task from this task's context
|
141
|
+
# Removes a task from this task's context.
|
125
142
|
#
|
126
143
|
# @param this_task
|
127
144
|
# The task to remove.
|
128
145
|
#
|
146
|
+
# @api private
|
129
147
|
def remove(this_task)
|
130
148
|
@__context__.remove(this_task)
|
131
149
|
end
|
132
|
-
|
133
|
-
|
134
150
|
end
|
135
151
|
|
136
|
-
|
137
|
-
#
|
138
|
-
# begin/run/execute block cannot be closed
|
139
|
-
#
|
152
|
+
# Represents a task that might not execute. Similar to a regular task in
|
153
|
+
# all functions except that it is not assured a chance to execute. Whereas
|
154
|
+
# a begin/run/execute block cannot be closed while there are still
|
155
|
+
# `nonDaemonHeirs`, a DaemonTask can enter the closed state with daemon
|
156
|
+
# heirs, making them essentially unrunnable.
|
157
|
+
#
|
158
|
+
# @api private
|
140
159
|
class DaemonTask < Task
|
141
|
-
|
160
|
+
# @api private
|
142
161
|
def is_daemon?
|
143
162
|
return true
|
144
163
|
end
|
145
|
-
|
146
164
|
end
|
147
165
|
|
148
|
-
#
|
166
|
+
# Used to bridge asynchronous execution to external asynchronous APIs or
|
167
|
+
# events. It is passed a block, like so:
|
149
168
|
#
|
150
169
|
# external_task do |t|
|
151
170
|
# t.cancellation_handler { |h, cause| h.fail(cause) }
|
152
171
|
# t.initiate_task { |h| trace << :task_started; h.complete; }
|
153
172
|
# end
|
154
173
|
#
|
155
|
-
# The {ExternalTask#initiate_task} method is expected to call an external
|
156
|
-
# Completion or failure of the external
|
157
|
-
#
|
158
|
-
# initiate_task
|
174
|
+
# The {ExternalTask#initiate_task} method is expected to call an external
|
175
|
+
# API and return without blocking. Completion or failure of the external
|
176
|
+
# task is reported through ExternalTaskCompletionHandle, which is passed
|
177
|
+
# into the initiate_task and cancellation_handler blocks. The cancellation
|
178
|
+
# handler, defined in the same block as the initiate_task, is used to
|
179
|
+
# report the cancellation of the external task.
|
159
180
|
#
|
181
|
+
# @api private
|
160
182
|
class ExternalTask < FlowFiber
|
161
183
|
attr_reader :block
|
162
184
|
attr_accessor :cancelled, :inCancellationHandler, :parent, :backtrace, :__context__
|
163
185
|
|
164
186
|
|
165
|
-
# Will always be false, provides a common
|
166
|
-
# correctly
|
167
|
-
#
|
187
|
+
# Will always be false, provides a common API for BeginRescueEnsure's to
|
188
|
+
# ensure they are maintaining their nonDaemonHeirsCount correctly.
|
189
|
+
#
|
190
|
+
# @api private
|
168
191
|
def is_daemon?
|
169
192
|
false
|
170
193
|
end
|
171
194
|
|
172
195
|
#
|
173
|
-
#
|
174
|
-
#
|
196
|
+
# Passes the get_heirs calls to the context, to ensure uniform handling
|
197
|
+
# of get_heirs
|
175
198
|
#
|
176
|
-
#
|
199
|
+
# @api private
|
177
200
|
def get_heirs
|
178
201
|
@__context__.get_heirs
|
179
202
|
end
|
180
203
|
|
181
|
-
#
|
204
|
+
# @api private
|
182
205
|
def initialize(options = {}, &block)
|
183
206
|
@inCancellationHandler = false
|
184
207
|
@block = block
|
@@ -188,43 +211,47 @@ module AWS
|
|
188
211
|
block.call(self)
|
189
212
|
end
|
190
213
|
|
191
|
-
# This method is here because the way we create
|
192
|
-
# tricky
|
193
|
-
# the external_task function), then the parent will only be
|
194
|
-
# ExternalTask#initialize is called. We'd prefer to set it in
|
195
|
-
# however, the backtrace relies on the parent's backtrace,
|
196
|
-
# that. Instead, we use this method to lazily create
|
197
|
-
# called. The method itself simply sets the backtrace to
|
198
|
-
# make_backtrace of the parent's backtrace, if the backtrace is not
|
199
|
-
# set, and will otherwise simply return the backtrace
|
200
|
-
#
|
214
|
+
# This method is here because the way we create external tasks is a
|
215
|
+
# little tricky. If the parent isn't passed in on construction(as is the
|
216
|
+
# case with the external_task function), then the parent will only be
|
217
|
+
# set after ExternalTask#initialize is called. We'd prefer to set it in
|
218
|
+
# initialize, however, the backtrace relies on the parent's backtrace,
|
219
|
+
# and so we cannot do that. Instead, we use this method to lazily create
|
220
|
+
# it when it is called. The method itself simply sets the backtrace to
|
221
|
+
# the make_backtrace of the parent's backtrace, if the backtrace is not
|
222
|
+
# already set, and will otherwise simply return the backtrace
|
223
|
+
#
|
224
|
+
# @api private
|
201
225
|
def backtrace
|
202
226
|
@backtrace ||= make_backtrace(@parent.backtrace)
|
203
227
|
end
|
204
228
|
|
205
|
-
# Add a task
|
206
|
-
#
|
229
|
+
# Add a task that removes yourself, and pass it through the parents executor.
|
230
|
+
#
|
231
|
+
# @api private
|
207
232
|
def remove_from_parent
|
208
233
|
@__context__.executor << FlowFiber.new { @parent.remove(self) }
|
209
234
|
end
|
210
235
|
|
211
|
-
# Add a task
|
212
|
-
# the parents executor
|
213
|
-
#
|
236
|
+
# Add a task that fails yourself with the suppiled error, and pass it
|
237
|
+
# through the parents executor.
|
238
|
+
#
|
239
|
+
# @api private
|
214
240
|
def fail_to_parent(error)
|
215
241
|
@__context__.executor << FlowFiber.new { @parent.fail(self, error) }
|
216
242
|
end
|
217
243
|
|
218
244
|
|
219
|
-
# Part of the interface provided by
|
220
|
-
# reflect that an
|
221
|
-
# ExternalTaskCompletionHandle
|
222
|
-
#
|
245
|
+
# Part of the interface provided by fiber, has to overridden to properly
|
246
|
+
# reflect that an external tasks alive-ness relies on its
|
247
|
+
# ExternalTaskCompletionHandle.
|
248
|
+
#
|
249
|
+
# @api private
|
223
250
|
def alive?
|
224
251
|
! @handle.completed
|
225
252
|
end
|
226
253
|
|
227
|
-
#
|
254
|
+
# @api private
|
228
255
|
def cancel(cause)
|
229
256
|
return if @cancelled
|
230
257
|
return if @handle.failure != nil || @handle.completed
|
@@ -252,20 +279,23 @@ module AWS
|
|
252
279
|
end
|
253
280
|
end
|
254
281
|
|
255
|
-
# Store the cancellation handler block
|
256
|
-
#
|
282
|
+
# Store the passed-in cancellation handler block for later reference.
|
283
|
+
#
|
284
|
+
# @api private
|
257
285
|
def cancellation_handler(&block)
|
258
286
|
@cancellation_task = lambda { |cause| block.call(@handle, cause) }
|
259
287
|
end
|
260
288
|
|
261
|
-
# Store the
|
262
|
-
#
|
289
|
+
# Store the passed-in block for later.
|
290
|
+
#
|
291
|
+
# @api private
|
263
292
|
def initiate_task(&block)
|
264
293
|
@initiation_task = lambda { block.call(@handle) }
|
265
294
|
end
|
266
295
|
|
267
|
-
# From the interface provided by
|
268
|
-
#
|
296
|
+
# From the interface provided by fiber, will execute the external task.
|
297
|
+
#
|
298
|
+
# @api private
|
269
299
|
def resume
|
270
300
|
return if @cancelled
|
271
301
|
begin
|
@@ -279,27 +309,28 @@ module AWS
|
|
279
309
|
end
|
280
310
|
|
281
311
|
# Used to complete or fail an external task initiated through
|
282
|
-
# ExternalTask#initiate_task, and thus handles the logic of what to do
|
283
|
-
# external task is failed.
|
284
|
-
#
|
312
|
+
# ExternalTask#initiate_task, and thus handles the logic of what to do
|
313
|
+
# when the external task is failed.
|
314
|
+
#
|
315
|
+
# @api private
|
285
316
|
class ExternalTaskCompletionHandle
|
286
317
|
attr_accessor :completed, :failure, :external_task
|
287
318
|
|
288
|
-
#
|
319
|
+
# @api private
|
289
320
|
def initialize(external_task)
|
290
321
|
@external_task = external_task
|
291
322
|
end
|
292
323
|
|
293
|
-
#
|
294
|
-
#
|
324
|
+
# Merges the backtrace, sets the @failure, and then fails the task from
|
325
|
+
# the parent.
|
295
326
|
#
|
296
327
|
# @param error
|
297
|
-
# The exception to fail on
|
328
|
+
# The exception to fail on.
|
298
329
|
#
|
299
330
|
# @raise IllegalStateException
|
300
|
-
# Raises if failure hasn't been set, or if the task is already completed
|
331
|
+
# Raises if failure hasn't been set, or if the task is already completed.
|
301
332
|
#
|
302
|
-
#
|
333
|
+
# @api private
|
303
334
|
def fail(error)
|
304
335
|
if ! @failure.nil?
|
305
336
|
raise IllegalStateException, "Invalid ExternalTaskCompletionHandle"
|
@@ -320,12 +351,12 @@ module AWS
|
|
320
351
|
end
|
321
352
|
end
|
322
353
|
|
323
|
-
#
|
354
|
+
# Sets the task to complete, and removes it from its parent.
|
324
355
|
#
|
325
356
|
# @raise IllegalStateException
|
326
|
-
# If the failure hasn't been set, or if the task is already completed
|
357
|
+
# If the failure hasn't been set, or if the task is already completed.
|
327
358
|
#
|
328
|
-
#
|
359
|
+
# @api private
|
329
360
|
def complete
|
330
361
|
if ! failure.nil?
|
331
362
|
raise IllegalStateException, ""
|
@@ -339,15 +370,19 @@ module AWS
|
|
339
370
|
end
|
340
371
|
end
|
341
372
|
|
342
|
-
#
|
343
|
-
#
|
373
|
+
# Holds some metadata for tasks and stores the parent link for tasks. It
|
374
|
+
# separates some of the concerns between tasks and what they have to know
|
375
|
+
# to follow back up the chain.
|
344
376
|
#
|
345
|
-
#
|
346
|
-
#
|
377
|
+
# All the methods here will simply delegate calls, either up to the
|
378
|
+
# parent, or down to the task.
|
379
|
+
#
|
380
|
+
# @api private
|
347
381
|
class TaskContext
|
348
382
|
|
349
383
|
attr_accessor :daemon, :parent, :backtrace, :cancelled
|
350
384
|
|
385
|
+
# @api private
|
351
386
|
def initialize(options = {})
|
352
387
|
@parent = options[:parent]
|
353
388
|
@task = options[:task]
|
@@ -356,49 +391,48 @@ module AWS
|
|
356
391
|
@daemon = options[:daemon]
|
357
392
|
end
|
358
393
|
|
359
|
-
#
|
394
|
+
# @api private
|
360
395
|
def get_closest_containing_scope
|
361
396
|
@task
|
362
397
|
# @ parent
|
363
398
|
end
|
364
399
|
|
365
|
-
#
|
400
|
+
# @api private
|
366
401
|
def alive?
|
367
402
|
@task.alive?
|
368
403
|
end
|
369
404
|
|
370
|
-
#
|
405
|
+
# @api private
|
371
406
|
def executor
|
372
407
|
@parent.executor
|
373
408
|
end
|
374
409
|
|
375
|
-
#
|
410
|
+
# @api private
|
376
411
|
def get_heirs
|
377
412
|
str = "I am a #{@task.class}
|
378
413
|
and my block looks like #{@task.block}"
|
379
414
|
end
|
380
415
|
|
381
|
-
#
|
416
|
+
# @api private
|
382
417
|
def fail(this_task, error)
|
383
418
|
@parent.fail(this_task, error)
|
384
419
|
end
|
385
420
|
|
386
|
-
#
|
421
|
+
# @api private
|
387
422
|
def remove(thread)
|
388
423
|
@parent.remove(thread)
|
389
424
|
end
|
390
425
|
|
391
|
-
#
|
426
|
+
# @api private
|
392
427
|
def cancel(error_type)
|
393
428
|
@task.cancelled = true
|
394
429
|
@parent.cancel(self)
|
395
430
|
end
|
396
431
|
|
397
|
-
#
|
432
|
+
# @api private
|
398
433
|
def <<(task)
|
399
434
|
@parent << task
|
400
435
|
end
|
401
|
-
|
402
436
|
end
|
403
437
|
end
|
404
438
|
end
|