dynflow 0.7.9 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|