aws-flow-core 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,77 @@
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 our implementation of fibers for 1.8
17
+ module AWS
18
+ module Flow
19
+ module Core
20
+ require 'fiber'
21
+ class FlowFiber < Fiber
22
+ def initialize(*args)
23
+ ObjectSpace.define_finalizer(self, self.class.finalize(self.object_id))
24
+ super(args)
25
+ end
26
+ class << self
27
+ attr_accessor :local_variables
28
+ end
29
+ @local_variables = Hash.new {|hash, key| hash[key] = {}}
30
+ def self.finalize(obj_id)
31
+ proc { FlowFiber.local_variables.delete(obj_id) }
32
+ end
33
+ def self.[](index)
34
+ self.local_variables[index]
35
+ end
36
+ def self.[]=(key, value)
37
+ self.local_variables[key] = value
38
+ end
39
+
40
+ # Will unset all the values for ancestors of this fiber, assuming that
41
+ # 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
43
+ def self.unset(current_fiber, key)
44
+ current_value = FlowFiber[current_fiber.object_id][key]
45
+ parent = FlowFiber[current_fiber.object_id][:parent]
46
+ ancestor_fibers = []
47
+ while parent != nil
48
+ ancestor_fibers << parent
49
+ parent = FlowFiber[parent.object_id][:parent]
50
+ end
51
+ ancestor_fibers.each do |fiber|
52
+ FlowFiber[fiber.object_id].delete(key) if FlowFiber[fiber.object_id][key] == current_value
53
+ end
54
+ FlowFiber[current_fiber.object_id].delete(key)
55
+ end
56
+
57
+ def initialize
58
+ # Child fibers should inherit their parents FiberLocals
59
+ FlowFiber[Fiber.current.object_id].each_pair do |key, val|
60
+ FlowFiber[self.object_id][key] = val
61
+ end
62
+ FlowFiber[self.object_id][:parent] = Fiber.current
63
+ super
64
+ end
65
+
66
+ def [](key)
67
+ FlowFiber[self.object_id][key]
68
+ end
69
+ def []=(key, value)
70
+ FlowFiber[self.object_id][key] = value
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,50 @@
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 simply contains definitions and functions useful to the overall running of flow
17
+ module AWS
18
+ module Flow
19
+ module Core
20
+ class IllegalStateException < Exception; end
21
+ class CancellationException < Exception
22
+ attr_accessor :reason, :details
23
+ def initialize(reason = nil, details = nil)
24
+ @reason = reason
25
+ @details = details
26
+ end
27
+ end
28
+
29
+ def make_backtrace(parent_backtrace)
30
+ # 1 frame for the function that actually removes the stack traces
31
+ # 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 BRE or External Task
35
+ # 1 frame for the new call into the BRE or ET
36
+ # 1 frame for the AsyncScope initialize that the BRE/ET has to be in
37
+
38
+ # "./lib/aws/rubyflow/asyncBacktrace.rb:75:in `caller'"
39
+ # "./lib/aws/rubyflow/asyncBacktrace.rb:21:in `create'"
40
+ # "./lib/aws/rubyflow/flow.rb:16:in `make_backtrace'"
41
+ # "./lib/aws/rubyflow/flow.rb:103:in `initialize'"
42
+ # "./lib/aws/rubyflow/asyncScope.rb:17:in `new'"
43
+ # "./lib/aws/rubyflow/asyncScope.rb:17:in `initialize'"
44
+
45
+ frames_to_skip = 7
46
+ backtrace = AsyncBacktrace.create(parent_backtrace, frames_to_skip)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,109 @@
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 our Future implementation, which allows us to have asynchronous, blocking promises
17
+
18
+ module AWS
19
+ module Flow
20
+ module Core
21
+ class AlreadySetException < Exception; end
22
+
23
+ # A Future represents the result of an asynchronous computation. Methods are
24
+ # provided to check if the computation is complete(Future#set), to wait for
25
+ # its completion(Future#wait), and to retrieve the result of the
26
+ # computation(Future#get). The result can only be retrieved using method get
27
+ # when the computation has completed, blocking if necessary until it is
28
+ # ready. This is okay, however, because it will block that Fiber, and
29
+ # another Fiber will start executing
30
+ class Future
31
+
32
+ # Sets the value of the future, and notifies all of the Fibers that tried
33
+ # to get when this future wasn't ready.
34
+ def set(result=nil)
35
+ raise AlreadySetException if @set
36
+ @set = true
37
+ @result = result
38
+ @conditional.broadcast if @conditional
39
+ @listeners.each { |b| b.call(self) } if @listeners
40
+ self
41
+ end
42
+
43
+ # Blocks if Future is not set
44
+ # raises CancellationError when task is cancelled
45
+ def get
46
+ until @set
47
+ @conditional ||= FiberConditionVariable.new
48
+ @conditional.wait
49
+ end
50
+ @result
51
+ end
52
+
53
+ def set?
54
+ @set
55
+ end
56
+
57
+ def unset
58
+ @set = nil
59
+ @result = nil
60
+ end
61
+
62
+ # Add a callback, block, which will fire when the future is set
63
+ def on_set(&block)
64
+ @listeners ||= []
65
+ # TODO probably want to use lambda here
66
+ @listeners << block
67
+ end
68
+ end
69
+
70
+ # Based on the ruby core source:
71
+ # https://github.com/ruby/ruby/blob/trunk/lib/thread.rb
72
+ class FiberConditionVariable
73
+ #
74
+ # Creates a new ConditionVariable
75
+ #
76
+ def initialize
77
+ @waiters = []
78
+ end
79
+
80
+
81
+ # Have the current fiber wait on this condition variable, and wake up when
82
+ # the FiberConditionVariable is signalled/broadcaster
83
+ def wait
84
+ fiber = ::Fiber.current
85
+ @waiters << fiber
86
+ Fiber.yield
87
+ self
88
+ end
89
+
90
+ #
91
+ # Wakes up the first fiber in line waiting for this lock.
92
+ #
93
+ def signal
94
+ t = @waiters.shift
95
+ t.schedule if t && t.alive?
96
+ self
97
+ end
98
+
99
+ #
100
+ # Wakes up all fibers waiting for this lock.
101
+ #
102
+ def broadcast
103
+ signal until @waiters.empty?
104
+ self
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,151 @@
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 the externally visible parts of flow that are expected to be used by customers of flow
17
+ module AWS
18
+ module Flow
19
+ module Core
20
+ class NoContextException < Exception; end
21
+
22
+ # @param block
23
+ # The block of code to be executed when the task is run.
24
+ #
25
+ # @raise [NoContextException]
26
+ # If the current fiber does not respond to {#__context__}.
27
+ #
28
+ # @return [Future]
29
+ # The tasks result, which is a {Future}.
30
+ #
31
+ def task(future = nil, &block)
32
+ fiber = ::Fiber.current
33
+ raise NoContextException unless fiber.respond_to? :__context__
34
+ context = fiber.__context__
35
+ t = Task.new(nil, &block)
36
+ task_context = TaskContext.new(:parent => context.get_closest_containing_scope, :task => t)
37
+ context << t
38
+ t.result
39
+ end
40
+
41
+ #
42
+ # @param block
43
+ # The block of code to be executed when the daemon task is run.
44
+ #
45
+ # @return [Future]
46
+ # The tasks result, which is a {Future}.
47
+ #
48
+ # @raise [NoContextException]
49
+ # If the current fiber does not respond to {#__context__}.
50
+ #
51
+ def daemon_task(&block)
52
+ fiber = ::Fiber.current
53
+ raise NoContextException unless fiber.respond_to? :__context__
54
+ context = fiber.__context__
55
+ t = DaemonTask.new(nil, &block)
56
+ task_context = TaskContext.new(:parent => context.get_closest_containing_scope, :task => t)
57
+ context << t
58
+ t.result
59
+ end
60
+
61
+ #
62
+ # @param block
63
+ # The block of code to be executed when the external task is run.
64
+ #
65
+ # @return [nil]
66
+ #
67
+ # @raise [NoContextException]
68
+ # If the current fiber does not respond to {#__context__}.
69
+ #
70
+ def external_task(&block)
71
+ fiber = ::Fiber.current
72
+ raise NoContextException unless fiber.respond_to? :__context__
73
+ context = fiber.__context__
74
+ t = ExternalTask.new(:parent => context.get_closest_containing_scope, &block)
75
+ task_context = TaskContext.new(:parent => context.get_closest_containing_scope, :task => t)
76
+ context << t
77
+ nil
78
+ end
79
+
80
+
81
+ #
82
+ #
83
+ # * *Args* :
84
+ # - block -> a block, which is passed in a BeginRescueEnsureWrapper, and which will define the BeginRescueEnsure#begin, BeginRescueEnsure#rescue, and BeginRescueEnsure#ensure methods
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__
89
+ #
90
+ def error_handler(&block)
91
+ fiber = ::Fiber.current
92
+ raise NoContextException unless fiber.respond_to? :__context__
93
+ context = fiber.__context__
94
+ begin_rescue_ensure = BeginRescueEnsure.new(:parent => context.get_closest_containing_scope)
95
+ bge = BeginRescueEnsureWrapper.new(block, begin_rescue_ensure)
96
+ context << bge
97
+ context << begin_rescue_ensure
98
+ begin_rescue_ensure
99
+ end
100
+
101
+ # @param block
102
+ # A code block, which is passed within a {BeginRescueEnsureWrapper}, and which must define the
103
+ # {BeginRescueEnsure#begin}, {BeginRescueEnsure#rescue}, and {BeginRescueEnsure#ensure} methods.
104
+ # @!visibility private
105
+ def _error_handler(&block)
106
+ error_handler(&block).result
107
+ end
108
+
109
+
110
+ def wait_for_function(function, *futures)
111
+ conditional = FiberConditionVariable.new
112
+ futures.flatten!
113
+ return nil if futures.empty?
114
+ result = futures.select(&:set?)
115
+ return futures.find(&:set?)if function.call(result, futures)
116
+ futures.each do |f|
117
+ f.on_set do |set_one|
118
+ result << set_one
119
+ conditional.broadcast if function.call(result, futures)
120
+ end
121
+ end
122
+ conditional.wait
123
+ result
124
+ end
125
+
126
+ # Blocks until *any* of the arguments are set.
127
+ #
128
+ # @param [Array] futures
129
+ # A list of futures to wait for. The function will return when at least one of these is set.
130
+ #
131
+ # @return [Array]
132
+ # A list of the set futures, in the order of being set.
133
+ #
134
+ def wait_for_any(*futures)
135
+ wait_for_function(lambda {|result, future_list| result.length >= 1 }, futures)
136
+ end
137
+
138
+ # Blocks until *all* of the arguments are set.
139
+ #
140
+ # @param [Array<Future>] futures
141
+ # A list of futures to wait for. The function will return only when all of them are set.
142
+ #
143
+ # @return [Array]
144
+ # A list of the set futures, in the order of being set.
145
+ #
146
+ def wait_for_all(*futures)
147
+ wait_for_function(lambda {|result, future_list| result.size == future_list.size}, futures)
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,85 @@
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
+ module AWS
17
+ module Flow
18
+ module Core
19
+
20
+ # Contains a Data Flow Analysis (DFA)-like framework, where transition functions can perform arbitrary computation
21
+ # before moving to the next state
22
+ module SimpleDFA
23
+ attr_accessor :transitions, :symbols, :states, :start_state
24
+
25
+ # Creates a new SimpleDFA instance.
26
+ #
27
+ # @param start_state
28
+ # The state with which to start the framework.
29
+ #
30
+ def init(start_state)
31
+ include InstanceMethods
32
+ @start_state = start_state
33
+ @symbols = []
34
+ @states = []
35
+ @transitions = {}
36
+ @states << start_state
37
+ end
38
+
39
+ # @return the start state
40
+ # The start state that was provided when this instance was created.
41
+ #
42
+ def get_start_state
43
+ @start_state
44
+ end
45
+
46
+ # @return [Array]
47
+ # The list of all transitions that were added with {#add_transition}.
48
+ #
49
+ def get_transitions
50
+ @transitions
51
+ end
52
+
53
+ def define_general(state, &block)
54
+ @symbols.each do |symbol|
55
+ if @transitions[[state, symbol]].nil?
56
+ @transitions[[state, symbol]] = block
57
+ end
58
+ end
59
+ end
60
+
61
+ def add_transition(state, symbol, &block)
62
+ @symbols << symbol unless @symbols.include? symbol
63
+ @states << state unless @states.include? state
64
+ @transitions[[state, symbol]] = block
65
+ end
66
+
67
+ def uncovered_transitions
68
+ @states.product(@symbols) - @transitions.keys
69
+ end
70
+
71
+ module InstanceMethods
72
+ attr_accessor :current_state
73
+
74
+ def consume(symbol)
75
+ @current_state ||= self.class.get_start_state
76
+ func_to_call = self.class.get_transitions[[@current_state, symbol]]
77
+ raise "This is not a legal transition" unless func_to_call
78
+ func_to_call.call(self)
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+ end