dynflow 0.7.9 → 0.8.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 +2 -0
- data/.travis.yml +16 -1
- data/Gemfile +13 -1
- data/doc/pages/source/_drafts/2015-03-01-new-documentation.markdown +10 -0
- data/doc/pages/source/_includes/menu.html +1 -0
- data/doc/pages/source/_includes/menu_right.html +1 -1
- data/doc/pages/source/_sass/_bootstrap-variables.sass +1 -0
- data/doc/pages/source/_sass/_style.scss +4 -0
- data/doc/pages/source/blog/index.html +12 -0
- data/doc/pages/source/documentation/index.md +330 -5
- data/dynflow.gemspec +3 -1
- data/examples/example_helper.rb +18 -11
- data/examples/orchestrate_evented.rb +2 -1
- data/examples/remote_executor.rb +53 -20
- data/lib/dynflow.rb +16 -6
- data/lib/dynflow/action/suspended.rb +1 -1
- data/lib/dynflow/action/with_sub_plans.rb +3 -6
- data/lib/dynflow/actor.rb +56 -0
- data/lib/dynflow/clock.rb +43 -38
- data/lib/dynflow/config.rb +107 -0
- data/lib/dynflow/connectors.rb +7 -0
- data/lib/dynflow/connectors/abstract.rb +41 -0
- data/lib/dynflow/connectors/database.rb +175 -0
- data/lib/dynflow/connectors/direct.rb +71 -0
- data/lib/dynflow/coordinator.rb +280 -0
- data/lib/dynflow/coordinator_adapters.rb +8 -0
- data/lib/dynflow/coordinator_adapters/abstract.rb +28 -0
- data/lib/dynflow/coordinator_adapters/sequel.rb +29 -0
- data/lib/dynflow/dispatcher.rb +58 -0
- data/lib/dynflow/dispatcher/abstract.rb +14 -0
- data/lib/dynflow/dispatcher/client_dispatcher.rb +139 -0
- data/lib/dynflow/dispatcher/executor_dispatcher.rb +86 -0
- data/lib/dynflow/errors.rb +7 -1
- data/lib/dynflow/execution_history.rb +46 -0
- data/lib/dynflow/execution_plan.rb +19 -15
- data/lib/dynflow/executors.rb +0 -1
- data/lib/dynflow/executors/abstract.rb +5 -10
- data/lib/dynflow/executors/parallel.rb +16 -13
- data/lib/dynflow/executors/parallel/core.rb +76 -78
- data/lib/dynflow/executors/parallel/execution_plan_manager.rb +4 -5
- data/lib/dynflow/executors/parallel/pool.rb +22 -52
- data/lib/dynflow/executors/parallel/running_steps_manager.rb +9 -2
- data/lib/dynflow/executors/parallel/worker.rb +5 -10
- data/lib/dynflow/persistence.rb +14 -0
- data/lib/dynflow/persistence_adapters/abstract.rb +14 -3
- data/lib/dynflow/persistence_adapters/sequel.rb +142 -38
- data/lib/dynflow/persistence_adapters/sequel_migrations/004_coordinator_records.rb +14 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/005_envelopes.rb +14 -0
- data/lib/dynflow/round_robin.rb +37 -0
- data/lib/dynflow/serializable.rb +1 -2
- data/lib/dynflow/serializer.rb +46 -0
- data/lib/dynflow/testing/dummy_executor.rb +2 -2
- data/lib/dynflow/testing/dummy_world.rb +1 -1
- data/lib/dynflow/transaction_adapters/abstract.rb +0 -5
- data/lib/dynflow/transaction_adapters/active_record.rb +0 -10
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web.rb +26 -0
- data/lib/dynflow/web/console.rb +108 -0
- data/lib/dynflow/web/console_helpers.rb +158 -0
- data/lib/dynflow/web/filtering_helpers.rb +85 -0
- data/lib/dynflow/web/world_helpers.rb +9 -0
- data/lib/dynflow/web_console.rb +3 -310
- data/lib/dynflow/world.rb +188 -119
- data/test/abnormal_states_recovery_test.rb +152 -0
- data/test/action_test.rb +2 -3
- data/test/clock_test.rb +1 -5
- data/test/coordinator_test.rb +152 -0
- data/test/dispatcher_test.rb +146 -0
- data/test/execution_plan_test.rb +2 -1
- data/test/executor_test.rb +534 -612
- data/test/middleware_test.rb +4 -4
- data/test/persistence_test.rb +17 -0
- data/test/prepare_travis_env.sh +35 -0
- data/test/rescue_test.rb +5 -3
- data/test/round_robin_test.rb +28 -0
- data/test/support/code_workflow_example.rb +0 -73
- data/test/support/dummy_example.rb +130 -0
- data/test/support/test_execution_log.rb +41 -0
- data/test/test_helper.rb +222 -116
- data/test/testing_test.rb +10 -10
- data/test/web_console_test.rb +3 -3
- data/test/world_test.rb +23 -0
- data/web/assets/images/logo-square.png +0 -0
- data/web/assets/stylesheets/application.css +9 -0
- data/web/assets/vendor/bootstrap/config.json +429 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-theme.css +479 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-theme.min.css +10 -0
- data/web/assets/vendor/bootstrap/css/bootstrap.css +5377 -4980
- data/web/assets/vendor/bootstrap/css/bootstrap.min.css +9 -8
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
- data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/web/assets/vendor/bootstrap/js/bootstrap.js +1674 -1645
- data/web/assets/vendor/bootstrap/js/bootstrap.min.js +11 -5
- data/web/views/execution_history.erb +17 -0
- data/web/views/index.erb +4 -6
- data/web/views/layout.erb +44 -8
- data/web/views/show.erb +4 -5
- data/web/views/worlds.erb +26 -0
- metadata +116 -23
- checksums.yaml +0 -15
- data/lib/dynflow/daemon.rb +0 -30
- data/lib/dynflow/executors/remote_via_socket.rb +0 -43
- data/lib/dynflow/executors/remote_via_socket/core.rb +0 -184
- data/lib/dynflow/future.rb +0 -173
- data/lib/dynflow/listeners.rb +0 -7
- data/lib/dynflow/listeners/abstract.rb +0 -17
- data/lib/dynflow/listeners/serialization.rb +0 -77
- data/lib/dynflow/listeners/socket.rb +0 -117
- data/lib/dynflow/micro_actor.rb +0 -102
- data/lib/dynflow/simple_world.rb +0 -19
- data/test/remote_via_socket_test.rb +0 -170
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.css +0 -1109
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.min.css +0 -9
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings.png +0 -0
checksums.yaml
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
!binary "U0hBMQ==":
|
|
3
|
-
metadata.gz: !binary |-
|
|
4
|
-
OTk4YWEzYzI4ZGUxYmEzYmRiZmYwZGUwN2I3ZjgzY2NlNmNjMDQ2OA==
|
|
5
|
-
data.tar.gz: !binary |-
|
|
6
|
-
OTFjZmY1OGE4MDI3MmIwY2YyYTIwYjZiOWI1YjljMTNiN2JhN2FhMA==
|
|
7
|
-
SHA512:
|
|
8
|
-
metadata.gz: !binary |-
|
|
9
|
-
ZGRlZGI5Njc4ZDA4ZmFkNzMxMTdkMGM0M2E5Y2ZlNjljYjUxNzc5MDY0ZWY5
|
|
10
|
-
MWJmNWQzYjZjOGVhMTM2ODRiZjZlM2JkMTZhY2MwMzUwNDM4M2FlN2VmZjA3
|
|
11
|
-
ODIwODIzYjdkZGE2NGI1NGYzNzEzODQ4OTdjOWQyMTc0ZTI5YmQ=
|
|
12
|
-
data.tar.gz: !binary |-
|
|
13
|
-
OTllZWM0M2U2M2ZiN2I1MjA0MTQzM2ZlYzdiNzUxMzZjMTg2Mzg5ODRiOTcw
|
|
14
|
-
MWQwNTdmMjYxN2Q3ZWFlOGRmOTVmZjJhNjIyYTNlMjZiNWI4NTc2ZWNlODg4
|
|
15
|
-
MTUyNmMwNGY5MWFiZjc4MTU5YWRjMmEwYTllNzcwM2QzMWMxNDE=
|
data/lib/dynflow/daemon.rb
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
module Dynflow
|
|
2
|
-
class Daemon
|
|
3
|
-
include Algebrick::TypeCheck
|
|
4
|
-
|
|
5
|
-
def initialize(listener, world, lock_file = nil)
|
|
6
|
-
@listener = Type! listener, Listeners::Abstract
|
|
7
|
-
@world = Type! world, World
|
|
8
|
-
@lock_file = Type! lock_file, String, NilClass
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def run
|
|
12
|
-
with_lock_file do
|
|
13
|
-
terminated = Future.new
|
|
14
|
-
trap('SIGINT') { @world.terminate terminated }
|
|
15
|
-
terminated.wait
|
|
16
|
-
@listener.terminate.wait
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def with_lock_file(&block)
|
|
21
|
-
if @lock_file
|
|
22
|
-
raise "Lockfile #{@lock_file} is already present." if File.exist?(@lock_file)
|
|
23
|
-
File.write(@lock_file, "Locked at #{Time.now} by process #{$$}\n")
|
|
24
|
-
end
|
|
25
|
-
block.call
|
|
26
|
-
ensure
|
|
27
|
-
File.delete(@lock_file) if @lock_file
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
require 'multi_json'
|
|
2
|
-
require 'socket'
|
|
3
|
-
|
|
4
|
-
module Dynflow
|
|
5
|
-
module Executors
|
|
6
|
-
class RemoteViaSocket < Abstract
|
|
7
|
-
require 'dynflow/executors/remote_via_socket/core'
|
|
8
|
-
|
|
9
|
-
include Listeners::Serialization
|
|
10
|
-
include Algebrick::Matching
|
|
11
|
-
|
|
12
|
-
def initialize(world, socket_path)
|
|
13
|
-
super world
|
|
14
|
-
@core = Core.new world, socket_path
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def execute(execution_plan_id, finished = Future.new)
|
|
18
|
-
@core.ask(Core::Execution[execution_plan_id, finished]).value!.value!
|
|
19
|
-
finished
|
|
20
|
-
rescue => e
|
|
21
|
-
finished.fail e unless finished.ready?
|
|
22
|
-
raise e
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def event(execution_plan_id, step_id, event, future = Future)
|
|
26
|
-
@core.ask(Core::Event[execution_plan_id, step_id, event, future]).value!
|
|
27
|
-
future
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def terminate(future = Future.new)
|
|
31
|
-
@core.ask(MicroActor::Terminate, future)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def initialized
|
|
35
|
-
@core.initialized
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def connected?
|
|
39
|
-
@core.ask(Core::Connect).value!
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
module Dynflow
|
|
2
|
-
module Executors
|
|
3
|
-
class RemoteViaSocket < Abstract
|
|
4
|
-
class Core < MicroActor
|
|
5
|
-
include Listeners::Serialization
|
|
6
|
-
|
|
7
|
-
Message = Algebrick.type do
|
|
8
|
-
Job = Algebrick.type do
|
|
9
|
-
variants Event = Executors::Abstract::Event,
|
|
10
|
-
Execution = Executors::Abstract::Execution
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
variants Closed = atom,
|
|
14
|
-
Received = type { fields message: Protocol::Response },
|
|
15
|
-
Connect = atom,
|
|
16
|
-
Job
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
TrackedJob = Algebrick.type do
|
|
20
|
-
fields! id: Integer, job: Protocol::Job, accepted: Future, finished: Future
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
module TrackedJob
|
|
24
|
-
def accept!
|
|
25
|
-
accepted.resolve true
|
|
26
|
-
self
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def reject!(error)
|
|
30
|
-
accepted.fail error
|
|
31
|
-
finished.fail error
|
|
32
|
-
self
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def success!(world)
|
|
36
|
-
raise unless accepted.ready?
|
|
37
|
-
finished.resolve(
|
|
38
|
-
match job,
|
|
39
|
-
(on Core::Protocol::Execution.(execution_plan_id: ~any) do |uuid|
|
|
40
|
-
world.persistence.load_execution_plan(uuid)
|
|
41
|
-
end),
|
|
42
|
-
(on Core::Protocol::Event do
|
|
43
|
-
true
|
|
44
|
-
end))
|
|
45
|
-
self
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def fail!(error)
|
|
49
|
-
if accepted.ready?
|
|
50
|
-
finished.fail error
|
|
51
|
-
else
|
|
52
|
-
reject! error
|
|
53
|
-
end
|
|
54
|
-
self
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def initialize(world, socket_path)
|
|
59
|
-
super(world.logger, world, socket_path)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
private
|
|
63
|
-
|
|
64
|
-
def delayed_initialize(world, socket_path)
|
|
65
|
-
@socket_path = Type! socket_path, String
|
|
66
|
-
@world = Type! world, World
|
|
67
|
-
@socket = nil
|
|
68
|
-
@last_id = 0
|
|
69
|
-
@tracked_jobs = {}
|
|
70
|
-
connect
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def termination
|
|
74
|
-
terminate! if disconnect
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def on_message(message)
|
|
78
|
-
Type! message, Message
|
|
79
|
-
match message,
|
|
80
|
-
(on ~Job do |job|
|
|
81
|
-
raise 'terminating' if terminating?
|
|
82
|
-
job, future =
|
|
83
|
-
match job,
|
|
84
|
-
(on ~Execution do |(execution_plan_uuid, future)|
|
|
85
|
-
[Protocol::Execution[execution_plan_uuid], future]
|
|
86
|
-
end),
|
|
87
|
-
(on ~Event do |(execution_plan_id, step_id, event, future)|
|
|
88
|
-
[Protocol::Event[execution_plan_id, step_id, event], future]
|
|
89
|
-
end)
|
|
90
|
-
id, accepted = add_tracked_job future, job
|
|
91
|
-
success = connect && begin
|
|
92
|
-
send_message @socket, Protocol::Do[id, job]
|
|
93
|
-
true
|
|
94
|
-
rescue IOError => error
|
|
95
|
-
logger.warn error
|
|
96
|
-
false
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
unless success
|
|
100
|
-
@tracked_jobs[id].reject!(
|
|
101
|
-
Dynflow::Error.new(
|
|
102
|
-
"Cannot do #{message}, no connection to a Listener"))
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
return accepted
|
|
106
|
-
end),
|
|
107
|
-
|
|
108
|
-
(on Received.(~Protocol::Accepted) do |(id)|
|
|
109
|
-
@tracked_jobs[id].accept!
|
|
110
|
-
end),
|
|
111
|
-
|
|
112
|
-
(on Received.(~Protocol::Failed) do |(id, error)|
|
|
113
|
-
@tracked_jobs.delete(id).reject! Dynflow::Error.new(error)
|
|
114
|
-
end),
|
|
115
|
-
|
|
116
|
-
(on Received.(~Protocol::Done) do |(id)|
|
|
117
|
-
@tracked_jobs.delete(id).success! @world
|
|
118
|
-
end),
|
|
119
|
-
|
|
120
|
-
(on Closed do
|
|
121
|
-
@socket = nil
|
|
122
|
-
logger.info 'Disconnected from server.'
|
|
123
|
-
@tracked_jobs.each do |_, c|
|
|
124
|
-
c.fail! 'Connection to a Listener lost.'
|
|
125
|
-
end
|
|
126
|
-
@tracked_jobs.clear
|
|
127
|
-
terminate! if terminating?
|
|
128
|
-
end),
|
|
129
|
-
|
|
130
|
-
(on Connect do
|
|
131
|
-
connect
|
|
132
|
-
end)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def add_tracked_job(finished, job)
|
|
136
|
-
@tracked_jobs[id = (@last_id += 1)] = TrackedJob[id, job, accepted = Future.new, finished]
|
|
137
|
-
return id, accepted
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def connect
|
|
141
|
-
return true if @socket
|
|
142
|
-
@socket = UNIXSocket.new @socket_path
|
|
143
|
-
logger.info 'Connected to server.'
|
|
144
|
-
read_socket_until_closed
|
|
145
|
-
true
|
|
146
|
-
rescue SystemCallError, IOError => error
|
|
147
|
-
logger.warn error
|
|
148
|
-
false
|
|
149
|
-
rescue => error
|
|
150
|
-
logger.fatal error
|
|
151
|
-
raise error
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def disconnect
|
|
155
|
-
return true unless @socket
|
|
156
|
-
|
|
157
|
-
@socket.close
|
|
158
|
-
false
|
|
159
|
-
rescue Errno::ENOTCONN
|
|
160
|
-
true
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def read_socket_until_closed
|
|
164
|
-
Thread.new do
|
|
165
|
-
catch(:stop_reading) do
|
|
166
|
-
loop { read_socket }
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def read_socket
|
|
172
|
-
match message = receive_message(@socket),
|
|
173
|
-
Protocol::Message >-> { self << Received[message] },
|
|
174
|
-
NilClass.to_m >-> do
|
|
175
|
-
self << Closed
|
|
176
|
-
throw :stop_reading
|
|
177
|
-
end
|
|
178
|
-
rescue => error
|
|
179
|
-
logger.fatal error
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
end
|
data/lib/dynflow/future.rb
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
module Dynflow
|
|
2
|
-
class Future
|
|
3
|
-
Error = Class.new Dynflow::Error
|
|
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
|