aws-flow-core 1.0.0

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.
@@ -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