aws-flow 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|