dynflow 0.1.0 → 0.2.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.
- data/.gitignore +6 -0
- data/.travis.yml +9 -0
- data/Gemfile +0 -10
- data/MIT-LICENSE +1 -1
- data/README.md +99 -37
- data/Rakefile +2 -6
- data/doc/images/logo.png +0 -0
- data/dynflow.gemspec +10 -1
- data/examples/generate_work_for_daemon.rb +24 -0
- data/examples/orchestrate.rb +121 -0
- data/examples/run_daemon.rb +17 -0
- data/examples/web_console.rb +29 -0
- data/lib/dynflow.rb +27 -6
- data/lib/dynflow/action.rb +185 -77
- data/lib/dynflow/action/cancellable_polling.rb +18 -0
- data/lib/dynflow/action/finalize_phase.rb +18 -0
- data/lib/dynflow/action/flow_phase.rb +44 -0
- data/lib/dynflow/action/format.rb +46 -0
- data/lib/dynflow/action/missing.rb +26 -0
- data/lib/dynflow/action/plan_phase.rb +85 -0
- data/lib/dynflow/action/polling.rb +49 -0
- data/lib/dynflow/action/presenter.rb +51 -0
- data/lib/dynflow/action/progress.rb +62 -0
- data/lib/dynflow/action/run_phase.rb +43 -0
- data/lib/dynflow/action/suspended.rb +21 -0
- data/lib/dynflow/clock.rb +133 -0
- data/lib/dynflow/daemon.rb +29 -0
- data/lib/dynflow/execution_plan.rb +285 -33
- data/lib/dynflow/execution_plan/dependency_graph.rb +29 -0
- data/lib/dynflow/execution_plan/output_reference.rb +52 -0
- data/lib/dynflow/execution_plan/steps.rb +12 -0
- data/lib/dynflow/execution_plan/steps/abstract.rb +121 -0
- data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +52 -0
- data/lib/dynflow/execution_plan/steps/error.rb +33 -0
- data/lib/dynflow/execution_plan/steps/finalize_step.rb +23 -0
- data/lib/dynflow/execution_plan/steps/plan_step.rb +81 -0
- data/lib/dynflow/execution_plan/steps/run_step.rb +21 -0
- data/lib/dynflow/executors.rb +9 -0
- data/lib/dynflow/executors/abstract.rb +32 -0
- data/lib/dynflow/executors/parallel.rb +88 -0
- data/lib/dynflow/executors/parallel/core.rb +119 -0
- data/lib/dynflow/executors/parallel/execution_plan_manager.rb +120 -0
- data/lib/dynflow/executors/parallel/flow_manager.rb +48 -0
- data/lib/dynflow/executors/parallel/pool.rb +102 -0
- data/lib/dynflow/executors/parallel/running_steps_manager.rb +63 -0
- data/lib/dynflow/executors/parallel/sequence_cursor.rb +97 -0
- data/lib/dynflow/executors/parallel/sequential_manager.rb +81 -0
- data/lib/dynflow/executors/parallel/work_queue.rb +44 -0
- data/lib/dynflow/executors/parallel/worker.rb +30 -0
- data/lib/dynflow/executors/remote_via_socket.rb +38 -0
- data/lib/dynflow/executors/remote_via_socket/core.rb +150 -0
- data/lib/dynflow/flows.rb +13 -0
- data/lib/dynflow/flows/abstract.rb +36 -0
- data/lib/dynflow/flows/abstract_composed.rb +104 -0
- data/lib/dynflow/flows/atom.rb +36 -0
- data/lib/dynflow/flows/concurrence.rb +28 -0
- data/lib/dynflow/flows/sequence.rb +13 -0
- data/lib/dynflow/future.rb +173 -0
- data/lib/dynflow/listeners.rb +7 -0
- data/lib/dynflow/listeners/abstract.rb +13 -0
- data/lib/dynflow/listeners/serialization.rb +41 -0
- data/lib/dynflow/listeners/socket.rb +88 -0
- data/lib/dynflow/logger_adapters.rb +8 -0
- data/lib/dynflow/logger_adapters/abstract.rb +30 -0
- data/lib/dynflow/logger_adapters/delegator.rb +13 -0
- data/lib/dynflow/logger_adapters/formatters.rb +8 -0
- data/lib/dynflow/logger_adapters/formatters/abstract.rb +33 -0
- data/lib/dynflow/logger_adapters/formatters/exception.rb +15 -0
- data/lib/dynflow/logger_adapters/simple.rb +59 -0
- data/lib/dynflow/micro_actor.rb +102 -0
- data/lib/dynflow/persistence.rb +53 -0
- data/lib/dynflow/persistence_adapters.rb +6 -0
- data/lib/dynflow/persistence_adapters/abstract.rb +56 -0
- data/lib/dynflow/persistence_adapters/sequel.rb +160 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/001_initial.rb +52 -0
- data/lib/dynflow/serializable.rb +66 -0
- data/lib/dynflow/simple_world.rb +18 -0
- data/lib/dynflow/stateful.rb +40 -0
- data/lib/dynflow/testing.rb +32 -0
- data/lib/dynflow/testing/assertions.rb +64 -0
- data/lib/dynflow/testing/dummy_execution_plan.rb +40 -0
- data/lib/dynflow/testing/dummy_executor.rb +29 -0
- data/lib/dynflow/testing/dummy_planned_action.rb +18 -0
- data/lib/dynflow/testing/dummy_step.rb +19 -0
- data/lib/dynflow/testing/dummy_world.rb +33 -0
- data/lib/dynflow/testing/factories.rb +83 -0
- data/lib/dynflow/testing/managed_clock.rb +23 -0
- data/lib/dynflow/testing/mimic.rb +38 -0
- data/lib/dynflow/transaction_adapters.rb +9 -0
- data/lib/dynflow/transaction_adapters/abstract.rb +26 -0
- data/lib/dynflow/transaction_adapters/active_record.rb +27 -0
- data/lib/dynflow/transaction_adapters/none.rb +12 -0
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web_console.rb +277 -0
- data/lib/dynflow/world.rb +168 -0
- data/test/action_test.rb +89 -11
- data/test/clock_test.rb +59 -0
- data/test/code_workflow_example.rb +382 -0
- data/test/execution_plan_test.rb +195 -64
- data/test/executor_test.rb +692 -0
- data/test/persistance_adapters_test.rb +173 -0
- data/test/test_helper.rb +316 -1
- data/test/testing_test.rb +148 -0
- data/test/web_console_test.rb +38 -0
- data/web/assets/javascripts/application.js +25 -0
- data/web/assets/stylesheets/application.css +101 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.css +1109 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.min.css +9 -0
- data/web/assets/vendor/bootstrap/css/bootstrap.css +6167 -0
- data/web/assets/vendor/bootstrap/css/bootstrap.min.css +9 -0
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings.png +0 -0
- data/web/assets/vendor/bootstrap/js/bootstrap.js +2280 -0
- data/web/assets/vendor/bootstrap/js/bootstrap.min.js +6 -0
- data/web/assets/vendor/google-code-prettify/lang-basic.js +3 -0
- data/web/assets/vendor/google-code-prettify/prettify.css +1 -0
- data/web/assets/vendor/google-code-prettify/prettify.js +30 -0
- data/web/assets/vendor/google-code-prettify/run_prettify.js +34 -0
- data/web/assets/vendor/jquery/jquery.js +9807 -0
- data/web/views/flow.erb +19 -0
- data/web/views/flow_step.erb +31 -0
- data/web/views/index.erb +39 -0
- data/web/views/layout.erb +20 -0
- data/web/views/plan_step.erb +11 -0
- data/web/views/show.erb +54 -0
- metadata +250 -11
- data/examples/events.rb +0 -71
- data/examples/workflow.rb +0 -140
- data/lib/dynflow/bus.rb +0 -168
- data/lib/dynflow/dispatcher.rb +0 -36
- data/lib/dynflow/logger.rb +0 -34
- data/lib/dynflow/step.rb +0 -234
- data/test/bus_test.rb +0 -150
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'forwardable'
|
|
2
|
+
|
|
3
|
+
module Dynflow
|
|
4
|
+
module Flows
|
|
5
|
+
|
|
6
|
+
require 'dynflow/flows/abstract'
|
|
7
|
+
require 'dynflow/flows/atom'
|
|
8
|
+
require 'dynflow/flows/abstract_composed'
|
|
9
|
+
require 'dynflow/flows/concurrence'
|
|
10
|
+
require 'dynflow/flows/sequence'
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Flows
|
|
3
|
+
|
|
4
|
+
class Abstract < Serializable
|
|
5
|
+
include Algebrick::TypeCheck
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
raise 'cannot instantiate Flows::Abstract'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_hash
|
|
12
|
+
{ :class => self.class.name }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def empty?
|
|
16
|
+
self.size == 0
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def size
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def includes_step?(step_id)
|
|
24
|
+
self.all_step_ids.any? { |s| s == step_id }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def all_step_ids
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def flatten!
|
|
32
|
+
raise NotImplementedError
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Flows
|
|
3
|
+
class AbstractComposed < Abstract
|
|
4
|
+
|
|
5
|
+
attr_reader :flows
|
|
6
|
+
|
|
7
|
+
def initialize(flows)
|
|
8
|
+
Type! flows, Array
|
|
9
|
+
flows.all? { |f| Type! f, Abstract }
|
|
10
|
+
@flows = flows
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_hash
|
|
14
|
+
super.merge recursive_to_hash(:flows => flows)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def <<(v)
|
|
18
|
+
@flows << v
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def [](*args)
|
|
23
|
+
@flows[*args]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def []=(*args)
|
|
27
|
+
@flows.[]=(*args)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def size
|
|
31
|
+
@flows.size
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
alias_method :sub_flows, :flows
|
|
35
|
+
|
|
36
|
+
# @return [Array<Integer>] all step_ids recursively in the flow
|
|
37
|
+
def all_step_ids
|
|
38
|
+
flows.map(&:all_step_ids).flatten
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def add_and_resolve(dependency_graph, new_flow)
|
|
42
|
+
return if new_flow.empty?
|
|
43
|
+
|
|
44
|
+
satisfying_flows = find_satisfying_sub_flows(dependency_graph, new_flow)
|
|
45
|
+
add_to_sequence(satisfying_flows, new_flow)
|
|
46
|
+
flatten!
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def flatten!
|
|
50
|
+
self.sub_flows.to_enum.with_index.reverse_each do |flow, i|
|
|
51
|
+
if flow.class == self.class
|
|
52
|
+
expand_steps(i)
|
|
53
|
+
elsif flow.is_a?(AbstractComposed) && flow.sub_flows.size == 1
|
|
54
|
+
self.sub_flows[i] = flow.sub_flows.first
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
self.sub_flows.map(&:flatten!)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
protected
|
|
62
|
+
|
|
63
|
+
def self.new_from_hash(hash)
|
|
64
|
+
check_class_matching hash
|
|
65
|
+
new(hash[:flows].map { |flow_hash| from_hash(flow_hash) })
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# adds the +new_flow+ in a way that it's in sequence with
|
|
69
|
+
# the +satisfying_flows+
|
|
70
|
+
def add_to_sequence(satisfying_flows, new_flow)
|
|
71
|
+
raise NotImplementedError
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def find_satisfying_sub_flows(dependency_graph, new_flow)
|
|
77
|
+
satisfying_flows = Set.new
|
|
78
|
+
new_flow.all_step_ids.each do |step_id|
|
|
79
|
+
dependency_graph.required_step_ids(step_id).each do |required_step_id|
|
|
80
|
+
satisfying_flow = sub_flows.find do |flow|
|
|
81
|
+
flow.includes_step?(required_step_id)
|
|
82
|
+
end
|
|
83
|
+
if satisfying_flow
|
|
84
|
+
satisfying_flows << satisfying_flow
|
|
85
|
+
dependency_graph.mark_satisfied(step_id, required_step_id)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
return satisfying_flows.to_a
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def expand_steps(index)
|
|
94
|
+
expanded_step = self.sub_flows[index]
|
|
95
|
+
self.sub_flows.delete_at(index)
|
|
96
|
+
expanded_step.sub_flows.each do |flow|
|
|
97
|
+
self.sub_flows.insert(index, flow)
|
|
98
|
+
index += 1
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Flows
|
|
3
|
+
class Atom < Abstract
|
|
4
|
+
|
|
5
|
+
attr_reader :step_id
|
|
6
|
+
|
|
7
|
+
def to_hash
|
|
8
|
+
super.merge(:step_id => step_id)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(step_id)
|
|
12
|
+
@step_id = Type! step_id, Integer
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def size
|
|
16
|
+
1
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def all_step_ids
|
|
20
|
+
[step_id]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def flatten!
|
|
24
|
+
# nothing to do
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
|
|
29
|
+
def self.new_from_hash(hash)
|
|
30
|
+
check_class_matching hash
|
|
31
|
+
new(hash[:step_id])
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Flows
|
|
3
|
+
class Concurrence < AbstractComposed
|
|
4
|
+
|
|
5
|
+
protected
|
|
6
|
+
|
|
7
|
+
def add_to_sequence(satisfying_flows, dependent_flow)
|
|
8
|
+
if satisfying_flows.empty?
|
|
9
|
+
self.sub_flows << dependent_flow
|
|
10
|
+
return
|
|
11
|
+
end
|
|
12
|
+
extracted_flow = extract_flows(satisfying_flows)
|
|
13
|
+
sequence = Sequence.new([extracted_flow])
|
|
14
|
+
|
|
15
|
+
self.sub_flows << sequence
|
|
16
|
+
sequence << dependent_flow
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def extract_flows(extracted_sub_flows)
|
|
20
|
+
extracted_sub_flows.each do |sub_flow|
|
|
21
|
+
self.sub_flows.delete(sub_flow)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
return Concurrence.new(extracted_sub_flows)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Flows
|
|
3
|
+
class Sequence < AbstractComposed
|
|
4
|
+
|
|
5
|
+
protected
|
|
6
|
+
|
|
7
|
+
def add_to_sequence(satisfying_flows, dependent_flow)
|
|
8
|
+
# the flows are already in sequence, we don't need to do anything extra
|
|
9
|
+
self << dependent_flow
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
class Future
|
|
3
|
+
Error = Class.new StandardError
|
|
4
|
+
FutureAlreadySet = Class.new Error
|
|
5
|
+
FutureFailed = Class.new Error
|
|
6
|
+
TimeOut = Class.new Error
|
|
7
|
+
|
|
8
|
+
# `#future` will become resolved to `true` when ``#countdown!`` is called `count` times
|
|
9
|
+
class CountDownLatch
|
|
10
|
+
attr_reader :future
|
|
11
|
+
|
|
12
|
+
def initialize(count, future = Future.new)
|
|
13
|
+
raise ArgumentError if count < 0
|
|
14
|
+
@count = count
|
|
15
|
+
@lock = Mutex.new
|
|
16
|
+
@future = future
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def countdown!
|
|
20
|
+
@lock.synchronize do
|
|
21
|
+
@count -= 1 if @count > 0
|
|
22
|
+
@future.resolve true if @count == 0 && !@future.ready?
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def count
|
|
27
|
+
@lock.synchronize { @count }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
include Algebrick::TypeCheck
|
|
32
|
+
extend Algebrick::TypeCheck
|
|
33
|
+
|
|
34
|
+
def self.join(futures, result = Future.new)
|
|
35
|
+
countdown = CountDownLatch.new(futures.size, result)
|
|
36
|
+
futures.each do |future|
|
|
37
|
+
Type! future, Future
|
|
38
|
+
future.do_then { |_| countdown.countdown! }
|
|
39
|
+
end
|
|
40
|
+
result
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def initialize(&task)
|
|
44
|
+
@lock = Mutex.new
|
|
45
|
+
@value = nil
|
|
46
|
+
@resolved = false
|
|
47
|
+
@failed = false
|
|
48
|
+
@waiting = []
|
|
49
|
+
@tasks = []
|
|
50
|
+
do_then &task if task
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def value(timeout = nil)
|
|
54
|
+
wait timeout
|
|
55
|
+
@lock.synchronize { @value }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def value!
|
|
59
|
+
value.tap { raise value if failed? }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def resolve(result)
|
|
63
|
+
set result, false
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def fail(exception)
|
|
67
|
+
Type! exception, Exception, String
|
|
68
|
+
if exception.is_a? String
|
|
69
|
+
exception = FutureFailed.new(exception).tap { |e| e.set_backtrace caller }
|
|
70
|
+
end
|
|
71
|
+
set exception, true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def evaluate_to(&block)
|
|
75
|
+
resolve block.call
|
|
76
|
+
rescue => error
|
|
77
|
+
self.fail error
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def evaluate_to!(&block)
|
|
81
|
+
evaluate_to &block
|
|
82
|
+
raise value if self.failed?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def do_then(&task)
|
|
86
|
+
call_task = @lock.synchronize do
|
|
87
|
+
@tasks << task unless _ready?
|
|
88
|
+
@resolved
|
|
89
|
+
end
|
|
90
|
+
task.call value if call_task
|
|
91
|
+
self
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def set(value, failed)
|
|
95
|
+
@lock.synchronize do
|
|
96
|
+
raise FutureAlreadySet, "future already set to #{@value} cannot use #{value}" if _ready?
|
|
97
|
+
if failed
|
|
98
|
+
@failed = true
|
|
99
|
+
else
|
|
100
|
+
@resolved = true
|
|
101
|
+
end
|
|
102
|
+
@value = value
|
|
103
|
+
while (thread = @waiting.pop)
|
|
104
|
+
begin
|
|
105
|
+
thread.wakeup
|
|
106
|
+
rescue ThreadError
|
|
107
|
+
retry
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
!failed
|
|
111
|
+
end
|
|
112
|
+
@tasks.each { |t| t.call value }
|
|
113
|
+
self
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def wait(timeout = nil)
|
|
117
|
+
@lock.synchronize do
|
|
118
|
+
unless _ready?
|
|
119
|
+
@waiting << Thread.current
|
|
120
|
+
clock.ping self, timeout, Thread.current, :expired if timeout
|
|
121
|
+
@lock.sleep
|
|
122
|
+
raise TimeOut unless _ready?
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
self
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def ready?
|
|
129
|
+
@lock.synchronize { _ready? }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def resolved?
|
|
133
|
+
@lock.synchronize { @resolved }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def failed?
|
|
137
|
+
@lock.synchronize { @failed }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def tangle(future)
|
|
141
|
+
do_then { |v| future.set v, failed? }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# @api private
|
|
145
|
+
def expired(thread)
|
|
146
|
+
@lock.synchronize do
|
|
147
|
+
thread.wakeup if @waiting.delete(thread)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def _ready?
|
|
154
|
+
@resolved || @failed
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
@clock_barrier = Mutex.new
|
|
158
|
+
|
|
159
|
+
# @api private
|
|
160
|
+
def self.clock
|
|
161
|
+
@clock_barrier.synchronize do
|
|
162
|
+
# TODO remove global state and use world.clock, needs to be terminated in right order
|
|
163
|
+
@clock ||= Clock.new(::Logger.new($stderr)).tap do |clock|
|
|
164
|
+
at_exit { clock.ask(Clock::Terminate).wait }
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def clock
|
|
170
|
+
self.class.clock
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Listeners
|
|
3
|
+
module Serialization
|
|
4
|
+
SocketMessage = Algebrick.type do
|
|
5
|
+
Execute = type { fields request_id: Integer, execution_plan_uuid: String }
|
|
6
|
+
Confirmation = type do
|
|
7
|
+
variants Accepted = type { fields request_id: Integer },
|
|
8
|
+
Failed = type { fields request_id: Integer, error: String }
|
|
9
|
+
end
|
|
10
|
+
Done = type { fields request_id: Integer, execution_plan_uuid: String }
|
|
11
|
+
|
|
12
|
+
variants Execute, Confirmation, Done
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def dump(obj)
|
|
16
|
+
MultiJson.dump(obj.to_hash)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def load(str)
|
|
20
|
+
SocketMessage.from_hash MultiJson.load(str)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def send_message(io, message, barrier = nil)
|
|
24
|
+
barrier.lock if barrier
|
|
25
|
+
io.puts dump(message)
|
|
26
|
+
ensure
|
|
27
|
+
barrier.unlock if barrier
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def receive_message(io)
|
|
31
|
+
if (message = io.gets)
|
|
32
|
+
load(message)
|
|
33
|
+
else
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
rescue IOError
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|