aws-flow 1.0.5 → 1.0.6
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.
- data/lib/aws/decider.rb +0 -9
- data/lib/aws/decider/async_decider.rb +13 -12
- data/lib/aws/decider/decider.rb +2 -2
- data/lib/aws/decider/generic_client.rb +4 -0
- data/lib/aws/decider/task_poller.rb +5 -2
- data/lib/aws/decider/version.rb +1 -1
- data/lib/aws/decider/workflow_client.rb +17 -3
- data/lib/aws/flow.rb +29 -0
- data/lib/aws/flow/async_backtrace.rb +134 -0
- data/lib/aws/flow/async_scope.rb +195 -0
- data/lib/aws/flow/begin_rescue_ensure.rb +386 -0
- data/lib/aws/flow/fiber.rb +77 -0
- data/lib/aws/flow/flow_utils.rb +50 -0
- data/lib/aws/flow/future.rb +115 -0
- data/lib/aws/flow/implementation.rb +151 -0
- data/lib/aws/flow/simple_dfa.rb +85 -0
- data/lib/aws/flow/tasks.rb +405 -0
- data/test/aws/async_backtrace_spec.rb +41 -0
- data/test/aws/async_scope_spec.rb +118 -0
- data/test/aws/begin_rescue_ensure_spec.rb +665 -0
- data/test/aws/decider_spec.rb +155 -35
- data/test/aws/external_task_spec.rb +197 -0
- data/test/aws/factories.rb +30 -1
- data/test/aws/fiber_condition_variable_spec.rb +163 -0
- data/test/aws/fiber_spec.rb +78 -0
- data/test/aws/flow_spec.rb +255 -0
- data/test/aws/future_spec.rb +210 -0
- data/test/aws/integration_spec.rb +83 -39
- data/test/aws/rubyflow.rb +22 -0
- data/test/aws/simple_dfa_spec.rb +63 -0
- data/test/aws/spec_helper.rb +11 -5
- metadata +23 -3
data/lib/aws/decider.rb
CHANGED
@@ -13,12 +13,6 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
#++
|
15
15
|
|
16
|
-
# @!visibility private
|
17
|
-
def require_all(path)
|
18
|
-
glob = File.join(path, "*.rb")
|
19
|
-
Dir[glob].each { |f| require f}
|
20
|
-
Dir[glob].map { |f| File.basename(f) }
|
21
|
-
end
|
22
16
|
|
23
17
|
require 'aws/flow'
|
24
18
|
include AWS::Flow::Core
|
@@ -27,9 +21,6 @@ require 'aws-sdk'
|
|
27
21
|
# Setting the user-agent as ruby-flow for all calls to the service
|
28
22
|
AWS.config(:user_agent_prefix => "ruby-flow")
|
29
23
|
|
30
|
-
#LOAD_PATH << File.dirname(File.expand_path(__FILE__))
|
31
|
-
|
32
|
-
|
33
24
|
require "aws/decider/utilities"
|
34
25
|
require "aws/decider/worker"
|
35
26
|
require 'aws/decider/generic_client'
|
@@ -132,7 +132,7 @@ module AWS
|
|
132
132
|
# @!visibility private
|
133
133
|
def get_next_state_machine_which_will_schedule(list)
|
134
134
|
return if list.empty?
|
135
|
-
ele = list.
|
135
|
+
ele = list.shift
|
136
136
|
ele = list.shift until (list.empty? || ele.get_decision != nil)
|
137
137
|
ele
|
138
138
|
end
|
@@ -159,18 +159,19 @@ module AWS
|
|
159
159
|
end
|
160
160
|
decision_state_machine.consume(:handle_decision_task_started_event)
|
161
161
|
decision_state_machine = next_decision_state_machine
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
162
|
+
end
|
163
|
+
if (next_decision_state_machine != nil &&
|
164
|
+
count < DecisionHelper.maximum_decisions_per_completion)
|
165
|
+
next_decision_state_machine.consume(:handle_decision_task_started_event)
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
169
|
-
|
170
|
-
def
|
171
|
-
|
172
|
-
|
173
|
-
|
169
|
+
|
170
|
+
def [](key)
|
171
|
+
return @decision_map[key]
|
172
|
+
end
|
173
|
+
def []=(key, val)
|
174
|
+
return @decision_map[key] = val
|
174
175
|
end
|
175
176
|
|
176
177
|
# Returns the activity ID for a scheduled activity
|
@@ -336,7 +337,7 @@ module AWS
|
|
336
337
|
#
|
337
338
|
def complete_workflow
|
338
339
|
return unless @completed && ! @unhandled_decision
|
339
|
-
decision_id =
|
340
|
+
decision_id = [:SELF, nil]
|
340
341
|
if @failure
|
341
342
|
@decision_helper[decision_id] = make_fail_decision(decision_id, @failure)
|
342
343
|
elsif @cancel_requested
|
@@ -459,7 +460,7 @@ module AWS
|
|
459
460
|
|
460
461
|
def handle_closing_failure
|
461
462
|
@unhandled_decision = true
|
462
|
-
@decision_helper[:SELF, nil].consume(:handle_initiation_failed_event)
|
463
|
+
@decision_helper[[:SELF, nil]].consume(:handle_initiation_failed_event)
|
463
464
|
end
|
464
465
|
|
465
466
|
# Handler for the `:CompleteWorkflowExecutionFailed` event.
|
data/lib/aws/decider/decider.rb
CHANGED
@@ -198,7 +198,7 @@ module AWS
|
|
198
198
|
:consume_symbol => :handle_completion_event,
|
199
199
|
:decision_helper_scheduled => :scheduled_signals,
|
200
200
|
:handle_open_request => lambda do |event, open_request|
|
201
|
-
workflow_execution = AWS::
|
201
|
+
workflow_execution = AWS::Flow::MinimalWorkflowExecution.new("",event.attributes.workflow_id, event.attributes.run_id)
|
202
202
|
failure = SignalExternalWorkflowException(event.id, workflow_execution, event.attributes.cause)
|
203
203
|
open_request.completion_handle.fail(failure)
|
204
204
|
end
|
@@ -215,7 +215,7 @@ module AWS
|
|
215
215
|
:consume_symbol => :handle_initiation_failed_event,
|
216
216
|
:decision_helper_scheduled => :scheduled_external_workflows,
|
217
217
|
:handle_open_request => lambda do |event, open_request|
|
218
|
-
workflow_execution = AWS::
|
218
|
+
workflow_execution = AWS::Flow::MinimalWorkflowExecution.new("",event.attributes.workflow_id, nil)
|
219
219
|
workflow_type = event.attributes.workflow_type
|
220
220
|
cause = event.attributes.cause
|
221
221
|
failure = StartChildWorkflowFailedException.new(event.id, workflow_execution, workflow_type, cause)
|
@@ -80,12 +80,16 @@ module AWS
|
|
80
80
|
elsif block.arity == 0
|
81
81
|
modified_options = Proc.new do
|
82
82
|
result = block.call
|
83
|
+
# We need to copy the hash to make sure that we don't mutate it
|
84
|
+
result = result.dup
|
83
85
|
result[:return_on_start] = true
|
84
86
|
result
|
85
87
|
end
|
86
88
|
# Otherwise, it will expect an options object passed in, and will do things on that object. So make our new Proc do that, and add an option
|
87
89
|
else modified_options = Proc.new do |x|
|
88
90
|
result = block.call(x)
|
91
|
+
# We need to copy the hash to make sure that we don't mutate it
|
92
|
+
result = result.dup
|
89
93
|
result.return_on_start = true
|
90
94
|
result
|
91
95
|
end
|
@@ -99,7 +99,10 @@ module AWS
|
|
99
99
|
raise "This activity worker was told to work on activity type #{activity_type.name}, but this activity worker only knows how to work on #{@activity_definition_map.keys.map(&:name).join' '}" unless activity_implementation
|
100
100
|
output = activity_implementation.execute(task.input, context)
|
101
101
|
@logger.debug "Responding on task_token #{task.task_token} for task #{task}"
|
102
|
-
if
|
102
|
+
if output.length > 32768
|
103
|
+
output = output.slice(0..32767)
|
104
|
+
respond_activity_task_failed_with_retry(task.task_token, "You cannot send a response with a result greater thank 32768. Please reduce the response size. The first part of the output is included in the details field.", output )
|
105
|
+
elsif ! activity_implementation.execution_options.manual_completion
|
103
106
|
@service.respond_activity_task_completed(:task_token => task.task_token, :result => output)
|
104
107
|
end
|
105
108
|
rescue ActivityFailureException => e
|
@@ -133,7 +136,7 @@ module AWS
|
|
133
136
|
end
|
134
137
|
|
135
138
|
def process_single_task(task)
|
136
|
-
@service = AWS::SimpleWorkflow.new.client.with_http_handler(AWS::Core::Http::NetHttpHandler.new(
|
139
|
+
@service = AWS::SimpleWorkflow.new.client.with_http_handler(AWS::Core::Http::NetHttpHandler.new(AWS.config.to_h))
|
137
140
|
begin
|
138
141
|
begin
|
139
142
|
execute(task)
|
data/lib/aws/decider/version.rb
CHANGED
@@ -16,11 +16,24 @@
|
|
16
16
|
module AWS
|
17
17
|
module Flow
|
18
18
|
|
19
|
+
class MinimalDomain
|
20
|
+
attr_accessor :name
|
21
|
+
def initialize(domain); @domain = domain; end
|
22
|
+
end
|
23
|
+
|
24
|
+
class MinimalWorkflowExecution
|
25
|
+
attr_accessor :workflow_id, :run_id, :domain
|
26
|
+
def initialize(domain, workflow_id, run_id)
|
27
|
+
@domain = domain
|
28
|
+
@workflow_id = workflow_id
|
29
|
+
@run_id = run_id
|
30
|
+
end
|
31
|
+
end
|
19
32
|
|
20
33
|
# A future provided by a [WorkflowExecution](http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/WorkflowExecution.html).
|
21
34
|
#
|
22
35
|
# @!attribute _workflow_execution
|
23
|
-
#
|
36
|
+
# A MinimalWorkflowExecution
|
24
37
|
# instance that this future belongs to.
|
25
38
|
#
|
26
39
|
# @!attribute return_value
|
@@ -35,7 +48,7 @@ module AWS
|
|
35
48
|
# @param workflow_execution
|
36
49
|
#
|
37
50
|
def initialize(workflow_execution)
|
38
|
-
@_workflow_execution = workflow_execution
|
51
|
+
@_workflow_execution = workflow_execution.dup
|
39
52
|
@return_value = Future.new
|
40
53
|
end
|
41
54
|
|
@@ -208,7 +221,8 @@ module AWS
|
|
208
221
|
get_decision_context
|
209
222
|
options = Utilities::interpret_block_for_options(StartWorkflowOptions, block)
|
210
223
|
workflow_id_future, run_id_future = Future.new, Future.new
|
211
|
-
|
224
|
+
minimal_domain = MinimalDomain.new(@domain.name.to_s)
|
225
|
+
output = WorkflowFuture.new(AWS::Flow::MinimalWorkflowExecution.new(minimal_domain, workflow_id_future, run_id_future))
|
212
226
|
options = Utilities::merge_all_options(@options, options)
|
213
227
|
new_options = StartWorkflowOptions.new(options)
|
214
228
|
open_request = OpenRequestInfo.new
|
data/lib/aws/flow.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
# A copy of the License is located at
|
7
|
+
#
|
8
|
+
# http://aws.amazon.com/apache2.0
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is distributed
|
11
|
+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
12
|
+
# express or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
##
|
15
|
+
|
16
|
+
|
17
|
+
# Everything depends on fiber, so we have to require that before anything else
|
18
|
+
require 'aws/flow/fiber'
|
19
|
+
|
20
|
+
# Require everything else
|
21
|
+
require 'aws/flow/async_backtrace.rb'
|
22
|
+
require 'aws/flow/async_scope.rb'
|
23
|
+
require 'aws/flow/begin_rescue_ensure.rb'
|
24
|
+
require 'aws/flow/flow_utils.rb'
|
25
|
+
require 'aws/flow/future.rb'
|
26
|
+
require 'aws/flow/implementation.rb'
|
27
|
+
require 'aws/flow/simple_dfa.rb'
|
28
|
+
require 'aws/flow/tasks.rb'
|
29
|
+
$RUBY_FLOW_FILES = ["async_backtrace.rb","tasks.rb","simple_dfa.rb","implementation.rb","future.rb","flow_utils.rb","begin_rescue_ensure.rb","async_scope.rb"]
|
@@ -0,0 +1,134 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
# A copy of the License is located at
|
7
|
+
#
|
8
|
+
# http://aws.amazon.com/apache2.0
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is distributed
|
11
|
+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
12
|
+
# express or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
##
|
15
|
+
|
16
|
+
# This file contains AsyncBacktrace, which takes care of decorating and properly filtering backtraces
|
17
|
+
|
18
|
+
module AWS
|
19
|
+
module Flow
|
20
|
+
module Core
|
21
|
+
# @!visibility private
|
22
|
+
class AsyncBacktrace
|
23
|
+
|
24
|
+
# @!visibility private
|
25
|
+
def initialize(parent, backtrace)
|
26
|
+
@backtrace = AsyncBacktrace.filter(backtrace)
|
27
|
+
@parent = parent
|
28
|
+
end
|
29
|
+
|
30
|
+
# @!visibility private
|
31
|
+
def backtrace
|
32
|
+
if @parent
|
33
|
+
AsyncBacktrace.merge(@backtrace, @parent.backtrace)
|
34
|
+
else
|
35
|
+
@backtrace
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# @!visibility private
|
40
|
+
class << self
|
41
|
+
|
42
|
+
# @!visibility private
|
43
|
+
def create(parent, frames_to_skip)
|
44
|
+
|
45
|
+
unless @disable_async_backtrace
|
46
|
+
b = AsyncBacktrace.caller(frames_to_skip)
|
47
|
+
AsyncBacktrace.new(parent, b)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @!visibility private
|
52
|
+
def create_from_exception(parent, exception)
|
53
|
+
unless @disable_async_backtrace
|
54
|
+
AsyncBacktrace.new(parent, exception.backtrace);
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Remove all framework related frames after application frames. Keep framework frames before application
|
59
|
+
# frames.
|
60
|
+
#
|
61
|
+
# @todo
|
62
|
+
# The correct implementation should not have framework frames before application frames as it is expected to
|
63
|
+
# call Kernel.caller with the correct number. But in cases when due to changes this number is not correct
|
64
|
+
# the frames are kept to not create confusion.
|
65
|
+
#
|
66
|
+
def filter(backtrace)
|
67
|
+
if @disable_filtering
|
68
|
+
backtrace
|
69
|
+
else
|
70
|
+
do_filter(backtrace)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# @!visibility private
|
75
|
+
def merge(*backtraces)
|
76
|
+
result = []
|
77
|
+
backtraces.each do | b |
|
78
|
+
if b
|
79
|
+
result << "------ continuation ------" if result.size > 0
|
80
|
+
result += b
|
81
|
+
end
|
82
|
+
end
|
83
|
+
result
|
84
|
+
end
|
85
|
+
|
86
|
+
# @!visibility private
|
87
|
+
def disable_filtering
|
88
|
+
@disable_filtering = true
|
89
|
+
end
|
90
|
+
|
91
|
+
# @!visibility private
|
92
|
+
def enable_filtering
|
93
|
+
@disable_filtering = false
|
94
|
+
end
|
95
|
+
|
96
|
+
# @!visibility private
|
97
|
+
def disable
|
98
|
+
@disable_async_backtrace = true
|
99
|
+
end
|
100
|
+
|
101
|
+
# @!visibility private
|
102
|
+
def enable
|
103
|
+
@disable_async_backtrace = false
|
104
|
+
end
|
105
|
+
|
106
|
+
# @!visibility private
|
107
|
+
def caller(skip)
|
108
|
+
random_var = Kernel.caller 0
|
109
|
+
this_stuff = 1.upto(6).map { |x| Kernel.caller(x) }
|
110
|
+
other_var = Kernel.caller skip
|
111
|
+
Kernel.caller(@disable_filtering ? 0 : skip)
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
# @!visibility private
|
117
|
+
def do_filter(backtrace)
|
118
|
+
return nil unless backtrace
|
119
|
+
# keep asynchrony frames at the top of the backtrace only
|
120
|
+
# then cut all frames starting from asynchrony frame
|
121
|
+
skip_asynchrony_frames = false
|
122
|
+
@backtrace = backtrace.take_while do |frame|
|
123
|
+
if ! $RUBY_FLOW_FILES.select {|file| Regexp.new(file) =~ frame}.empty?
|
124
|
+
!skip_asynchrony_frames
|
125
|
+
else
|
126
|
+
skip_asynchrony_frames = true
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
# A copy of the License is located at
|
7
|
+
#
|
8
|
+
# http://aws.amazon.com/apache2.0
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is distributed
|
11
|
+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
12
|
+
# express or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
##
|
15
|
+
|
16
|
+
# This module contains the Root of the heirarchy for calls into flow, the AsyncScope
|
17
|
+
|
18
|
+
module AWS
|
19
|
+
module Flow
|
20
|
+
module Core
|
21
|
+
|
22
|
+
def gate_by_version(version, method, &block)
|
23
|
+
if RUBY_VERSION.send(method, version)
|
24
|
+
block.call
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @!visibility private
|
29
|
+
class AsyncScope
|
30
|
+
attr_accessor :stackTrace, :root, :failure, :root_context
|
31
|
+
|
32
|
+
def is_complete?
|
33
|
+
@root_context.complete
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_closest_containing_scope
|
37
|
+
@root_error_handler
|
38
|
+
end
|
39
|
+
|
40
|
+
def cancel(error); @root_error_handler.cancel(error); end
|
41
|
+
|
42
|
+
def initialize(&block)
|
43
|
+
@root_context = RootAsyncScope.new
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
# 1 for the function that skips frames
|
48
|
+
# 1 for the create function
|
49
|
+
# 1 for the the initialize of the backtrace
|
50
|
+
|
51
|
+
# "./lib/aws/rubyflow/asyncBacktrace.rb:75:in `caller'"
|
52
|
+
# "./lib/aws/rubyflow/asyncBacktrace.rb:21:in `create'"
|
53
|
+
# "./lib/aws/rubyflow/asyncScope.rb:18:in `initialize'"
|
54
|
+
|
55
|
+
@root_context.backtrace = AsyncBacktrace.create(nil, 3)
|
56
|
+
@root_error_handler = BeginRescueEnsure.new(:parent => @root_context)
|
57
|
+
begin
|
58
|
+
@root_error_handler.begin lambda { block.call if ! block.nil? }
|
59
|
+
@root_error_handler.rescue(Exception, lambda { |e| raise e })
|
60
|
+
end
|
61
|
+
@root_context << @root_error_handler
|
62
|
+
end
|
63
|
+
|
64
|
+
# Collects all the heirs of a task for use in async_stack_dump
|
65
|
+
def get_heirs
|
66
|
+
@root_error_handler.get_heirs
|
67
|
+
end
|
68
|
+
|
69
|
+
# Execute all queued tasks. If execution of those tasks results in addition of new tasks to the queue, execute
|
70
|
+
# them as well.
|
71
|
+
#
|
72
|
+
# Unless there are external dependencies or bugs in the tasks to be executed, a single call to this method
|
73
|
+
# performs the complete asynchronous execution.
|
74
|
+
#
|
75
|
+
# @note In the presence of external dependencies, it is expected that {AsyncScope#eventLoop} is called every
|
76
|
+
# time after a change in the state in a dependency can unblock asynchronous execution.
|
77
|
+
#
|
78
|
+
def eventLoop
|
79
|
+
#TODO Figure out when to raise Done raise "Done" if ! @root_task.alive?
|
80
|
+
raise IllegalStateException, "Already complete" if is_complete?
|
81
|
+
@root_context.eventLoop
|
82
|
+
# TODO Does this need to be taken care of? It's supposed to protect
|
83
|
+
# against people having errors that are classes, so like, passing
|
84
|
+
# Exception into cancel. We might want to just catch that at the
|
85
|
+
# entry point
|
86
|
+
if @root_context.failure
|
87
|
+
if @root_context.failure.respond_to? :message
|
88
|
+
failure_message = @root_context.failure.message + "\n" +
|
89
|
+
@root_context.failure.backtrace.join("\n")
|
90
|
+
raise @root_context.failure, failure_message
|
91
|
+
else
|
92
|
+
raise @root_context.failure
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
return is_complete?
|
97
|
+
end
|
98
|
+
|
99
|
+
def <<(task)
|
100
|
+
@root_context << task
|
101
|
+
task.parent = @root_context
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# @!visibility private
|
106
|
+
class RootAsyncScope < FlowFiber
|
107
|
+
|
108
|
+
attr_accessor :backtrace, :failure, :executor, :complete
|
109
|
+
|
110
|
+
def initialize(options = {}, &block)
|
111
|
+
@parent = options[:parent_context]
|
112
|
+
@daemon = options[:daemon]
|
113
|
+
@context = @parent
|
114
|
+
@executor = AsyncEventLoop.new
|
115
|
+
@task_queue = []
|
116
|
+
@complete = false
|
117
|
+
@task_queue << Task.new(context, &block) if block
|
118
|
+
end
|
119
|
+
|
120
|
+
# The only thing that should be removed from the RootAsyncScope is the
|
121
|
+
# root BeginRescueEnsure, so upon removal we are complete
|
122
|
+
def remove(task)
|
123
|
+
@complete = true
|
124
|
+
end
|
125
|
+
|
126
|
+
# As with remove, the only thing that is under RootAsyncScope should be
|
127
|
+
# the root BeginRescueEnsure, so upon failure we will be complete. Also
|
128
|
+
# sets failure variable for later raising.
|
129
|
+
def fail(task, error)
|
130
|
+
@failure = error
|
131
|
+
@complete = true
|
132
|
+
end
|
133
|
+
|
134
|
+
def <<(this_task)
|
135
|
+
@executor << this_task
|
136
|
+
end
|
137
|
+
|
138
|
+
# Reture self, a RootAsyncScope is the closest containing scope
|
139
|
+
def get_closest_containing_scope
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
# Call out to the AsyncEventLoop
|
144
|
+
def eventLoop
|
145
|
+
@executor.executeQueuedTasks
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
private
|
150
|
+
DELEGATED_METHODS = [:push, :<<, :enq, :empty?, :length, :size, :delete, :shift]
|
151
|
+
|
152
|
+
def method_missing(method_name, *args)
|
153
|
+
if DELEGATED_METHODS.include? method_name
|
154
|
+
@executor.send(method_name, *args)
|
155
|
+
else
|
156
|
+
super
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# @!visibility private
|
162
|
+
class AsyncEventLoop
|
163
|
+
|
164
|
+
def initialize
|
165
|
+
@tasks = []
|
166
|
+
end
|
167
|
+
|
168
|
+
def remove(task)
|
169
|
+
@tasks.delete(task)
|
170
|
+
end
|
171
|
+
# TODO Make sure that it's okay to fail from the AsyncEventLoop, and that
|
172
|
+
# this is the correct behavior
|
173
|
+
def fail(task, error)
|
174
|
+
raise error
|
175
|
+
end
|
176
|
+
def <<(task)
|
177
|
+
@tasks << task
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
# TODO should this be synchronized somehow?
|
183
|
+
|
184
|
+
# Actually executes the eventLoop
|
185
|
+
def executeQueuedTasks
|
186
|
+
until @tasks.empty?
|
187
|
+
task = @tasks.shift
|
188
|
+
task.resume if task.alive?
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|