dynflow 1.1.6 → 1.2.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/dynflow.gemspec +2 -2
- data/examples/clock_benchmark.rb +35 -0
- data/examples/memory_limit_watcher.rb +1 -1
- data/lib/dynflow/action.rb +2 -2
- data/lib/dynflow/action/suspended.rb +1 -1
- data/lib/dynflow/action/with_sub_plans.rb +1 -1
- data/lib/dynflow/actor.rb +2 -2
- data/lib/dynflow/actors/execution_plan_cleaner.rb +1 -1
- data/lib/dynflow/clock.rb +11 -8
- data/lib/dynflow/delayed_executors/abstract.rb +1 -1
- data/lib/dynflow/delayed_plan.rb +1 -1
- data/lib/dynflow/director.rb +4 -4
- data/lib/dynflow/director/execution_plan_manager.rb +2 -2
- data/lib/dynflow/director/running_steps_manager.rb +4 -4
- data/lib/dynflow/dispatcher/client_dispatcher.rb +13 -12
- data/lib/dynflow/dispatcher/executor_dispatcher.rb +5 -5
- data/lib/dynflow/execution_plan.rb +1 -1
- data/lib/dynflow/execution_plan/steps/plan_step.rb +4 -2
- data/lib/dynflow/executors/abstract.rb +6 -6
- data/lib/dynflow/executors/parallel.rb +6 -6
- data/lib/dynflow/executors/parallel/core.rb +1 -1
- data/lib/dynflow/rails/daemon.rb +1 -1
- data/lib/dynflow/testing/dummy_executor.rb +2 -2
- data/lib/dynflow/testing/dummy_world.rb +1 -1
- data/lib/dynflow/testing/in_thread_executor.rb +5 -5
- data/lib/dynflow/testing/in_thread_world.rb +6 -6
- data/lib/dynflow/throttle_limiter.rb +5 -5
- data/lib/dynflow/utils.rb +3 -140
- data/lib/dynflow/utils/indifferent_hash.rb +143 -0
- data/lib/dynflow/utils/priority_queue.rb +64 -0
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/world.rb +22 -22
- data/lib/dynflow/world/invalidation.rb +1 -1
- data/test/batch_sub_tasks_test.rb +4 -4
- data/test/concurrency_control_test.rb +6 -6
- data/test/daemon_test.rb +2 -2
- data/test/dispatcher_test.rb +6 -6
- data/test/execution_plan_test.rb +11 -0
- data/test/executor_test.rb +1 -1
- data/test/support/dummy_example.rb +1 -1
- data/test/test_helper.rb +17 -17
- data/test/utils_test.rb +56 -0
- data/test/world_test.rb +2 -2
- metadata +14 -9
@@ -0,0 +1,64 @@
|
|
1
|
+
module Dynflow
|
2
|
+
module Utils
|
3
|
+
# Heavily inspired by rubyworks/pqueue
|
4
|
+
class PriorityQueue
|
5
|
+
def initialize(&block) # :yields: a, b
|
6
|
+
@backing_store = []
|
7
|
+
@comparator = block || :<=>.to_proc
|
8
|
+
end
|
9
|
+
|
10
|
+
def size
|
11
|
+
@backing_store.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def top
|
15
|
+
@backing_store.last
|
16
|
+
end
|
17
|
+
|
18
|
+
def push(element)
|
19
|
+
@backing_store << element
|
20
|
+
reposition_element(@backing_store.size - 1)
|
21
|
+
end
|
22
|
+
|
23
|
+
def pop
|
24
|
+
@backing_store.pop
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_a
|
28
|
+
@backing_store
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# The element at index k will be repositioned to its proper place.
|
34
|
+
def reposition_element(index)
|
35
|
+
return if size <= 1
|
36
|
+
|
37
|
+
element = @backing_store.delete_at(index)
|
38
|
+
index = binary_index(@backing_store, element)
|
39
|
+
|
40
|
+
@backing_store.insert(index, element)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Find index where a new element should be inserted using binary search
|
44
|
+
def binary_index(array, target)
|
45
|
+
upper = array.size - 1
|
46
|
+
lower = 0
|
47
|
+
|
48
|
+
while upper >= lower
|
49
|
+
center = lower + (upper - lower) / 2
|
50
|
+
|
51
|
+
case @comparator.call(target, array[center])
|
52
|
+
when 0
|
53
|
+
return center
|
54
|
+
when 1
|
55
|
+
lower = center + 1
|
56
|
+
when -1
|
57
|
+
upper = center - 1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
lower
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/dynflow/version.rb
CHANGED
data/lib/dynflow/world.rb
CHANGED
@@ -21,8 +21,8 @@ module Dynflow
|
|
21
21
|
Dynflow::Telemetry.register_metrics!
|
22
22
|
|
23
23
|
@id = SecureRandom.uuid
|
24
|
-
@clock = spawn_and_wait(Clock, 'clock')
|
25
24
|
@logger_adapter = @config.logger_adapter
|
25
|
+
@clock = spawn_and_wait(Clock, 'clock', logger)
|
26
26
|
@config.validate
|
27
27
|
@transaction_adapter = @config.transaction_adapter
|
28
28
|
@persistence = Persistence.new(self, @config.persistence_adapter,
|
@@ -41,7 +41,7 @@ module Dynflow
|
|
41
41
|
@auto_validity_check = @config.auto_validity_check
|
42
42
|
@validity_check_timeout = @config.validity_check_timeout
|
43
43
|
@throttle_limiter = @config.throttle_limiter
|
44
|
-
@terminated = Concurrent.
|
44
|
+
@terminated = Concurrent::Promises.resolvable_event
|
45
45
|
@termination_timeout = @config.termination_timeout
|
46
46
|
calculate_subscription_index
|
47
47
|
|
@@ -132,7 +132,7 @@ module Dynflow
|
|
132
132
|
PlaningFailed = type { fields! execution_plan_id: String, error: Exception }
|
133
133
|
# Returned by #trigger when planning is successful, #future will resolve after
|
134
134
|
# ExecutionPlan is executed.
|
135
|
-
Triggered = type { fields! execution_plan_id: String, future: Concurrent::
|
135
|
+
Triggered = type { fields! execution_plan_id: String, future: Concurrent::Promises::ResolvableFuture }
|
136
136
|
|
137
137
|
Scheduled = type { fields! execution_plan_id: String }
|
138
138
|
|
@@ -176,7 +176,7 @@ module Dynflow
|
|
176
176
|
planned = execution_plan.state == :planned
|
177
177
|
|
178
178
|
if planned
|
179
|
-
done = execute(execution_plan.id, Concurrent.
|
179
|
+
done = execute(execution_plan.id, Concurrent::Promises.resolvable_future)
|
180
180
|
Triggered[execution_plan.id, done]
|
181
181
|
else
|
182
182
|
PlaningFailed[execution_plan.id, execution_plan.errors.first.exception]
|
@@ -208,41 +208,41 @@ module Dynflow
|
|
208
208
|
end
|
209
209
|
end
|
210
210
|
|
211
|
-
# @return [Concurrent::
|
211
|
+
# @return [Concurrent::Promises::ResolvableFuture] containing execution_plan when finished
|
212
212
|
# raises when ExecutionPlan is not accepted for execution
|
213
|
-
def execute(execution_plan_id, done = Concurrent.
|
213
|
+
def execute(execution_plan_id, done = Concurrent::Promises.resolvable_future)
|
214
214
|
publish_request(Dispatcher::Execution[execution_plan_id], done, true)
|
215
215
|
end
|
216
216
|
|
217
|
-
def event(execution_plan_id, step_id, event, done = Concurrent.
|
217
|
+
def event(execution_plan_id, step_id, event, done = Concurrent::Promises.resolvable_future)
|
218
218
|
publish_request(Dispatcher::Event[execution_plan_id, step_id, event], done, false)
|
219
219
|
end
|
220
220
|
|
221
|
-
def ping(world_id, timeout, done = Concurrent.
|
221
|
+
def ping(world_id, timeout, done = Concurrent::Promises.resolvable_future)
|
222
222
|
publish_request(Dispatcher::Ping[world_id, true], done, false, timeout)
|
223
223
|
end
|
224
224
|
|
225
|
-
def ping_without_cache(world_id, timeout, done = Concurrent.
|
225
|
+
def ping_without_cache(world_id, timeout, done = Concurrent::Promises.resolvable_future)
|
226
226
|
publish_request(Dispatcher::Ping[world_id, false], done, false, timeout)
|
227
227
|
end
|
228
228
|
|
229
|
-
def get_execution_status(world_id, execution_plan_id, timeout, done = Concurrent.
|
229
|
+
def get_execution_status(world_id, execution_plan_id, timeout, done = Concurrent::Promises.resolvable_future)
|
230
230
|
publish_request(Dispatcher::Status[world_id, execution_plan_id], done, false, timeout)
|
231
231
|
end
|
232
232
|
|
233
233
|
def publish_request(request, done, wait_for_accepted, timeout = nil)
|
234
|
-
accepted = Concurrent.
|
234
|
+
accepted = Concurrent::Promises.resolvable_future
|
235
235
|
accepted.rescue do |reason|
|
236
|
-
done.
|
236
|
+
done.reject reason if reason
|
237
237
|
end
|
238
238
|
client_dispatcher.ask([:publish_request, done, request, timeout], accepted)
|
239
239
|
accepted.wait if wait_for_accepted
|
240
240
|
done
|
241
241
|
rescue => e
|
242
|
-
accepted.
|
242
|
+
accepted.reject e
|
243
243
|
end
|
244
244
|
|
245
|
-
def terminate(future = Concurrent.
|
245
|
+
def terminate(future = Concurrent::Promises.resolvable_future)
|
246
246
|
start_termination.tangle(future)
|
247
247
|
future
|
248
248
|
end
|
@@ -285,7 +285,7 @@ module Dynflow
|
|
285
285
|
def start_termination
|
286
286
|
@termination_barrier.synchronize do
|
287
287
|
return @terminating if @terminating
|
288
|
-
termination_future ||= Concurrent.future do
|
288
|
+
termination_future ||= Concurrent::Promises.future do
|
289
289
|
begin
|
290
290
|
run_before_termination_hooks
|
291
291
|
|
@@ -304,13 +304,13 @@ module Dynflow
|
|
304
304
|
executor.terminate.wait(termination_timeout)
|
305
305
|
|
306
306
|
logger.info "start terminating executor dispatcher..."
|
307
|
-
executor_dispatcher_terminated = Concurrent.
|
307
|
+
executor_dispatcher_terminated = Concurrent::Promises.resolvable_future
|
308
308
|
executor_dispatcher.ask([:start_termination, executor_dispatcher_terminated])
|
309
309
|
executor_dispatcher_terminated.wait(termination_timeout)
|
310
310
|
end
|
311
311
|
|
312
312
|
logger.info "start terminating client dispatcher..."
|
313
|
-
client_dispatcher_terminated = Concurrent.
|
313
|
+
client_dispatcher_terminated = Concurrent::Promises.resolvable_future
|
314
314
|
client_dispatcher.ask([:start_termination, client_dispatcher_terminated])
|
315
315
|
client_dispatcher_terminated.wait(termination_timeout)
|
316
316
|
|
@@ -323,15 +323,15 @@ module Dynflow
|
|
323
323
|
end
|
324
324
|
|
325
325
|
coordinator.delete_world(registered_world)
|
326
|
-
@terminated.
|
326
|
+
@terminated.resolve
|
327
327
|
true
|
328
328
|
rescue => e
|
329
329
|
logger.fatal(e)
|
330
330
|
end
|
331
331
|
end
|
332
|
-
@terminating = Concurrent.future do
|
332
|
+
@terminating = Concurrent::Promises.future do
|
333
333
|
termination_future.wait(termination_timeout)
|
334
|
-
end.
|
334
|
+
end.on_resolution do
|
335
335
|
@terminated.complete
|
336
336
|
Thread.new { Kernel.exit } if @exit_on_terminate.true?
|
337
337
|
end
|
@@ -350,7 +350,7 @@ module Dynflow
|
|
350
350
|
|
351
351
|
def run_before_termination_hooks
|
352
352
|
until @before_termination_hooks.empty?
|
353
|
-
hook_run = Concurrent.future do
|
353
|
+
hook_run = Concurrent::Promises.future do
|
354
354
|
begin
|
355
355
|
@before_termination_hooks.pop.call
|
356
356
|
rescue => e
|
@@ -362,7 +362,7 @@ module Dynflow
|
|
362
362
|
end
|
363
363
|
|
364
364
|
def spawn_and_wait(klass, name, *args)
|
365
|
-
initialized = Concurrent.
|
365
|
+
initialized = Concurrent::Promises.resolvable_future
|
366
366
|
actor = klass.spawn(name: name, args: args, initialized: initialized)
|
367
367
|
initialized.wait
|
368
368
|
return actor
|
@@ -68,7 +68,7 @@ module Dynflow
|
|
68
68
|
FailureSimulator.wont_fail!
|
69
69
|
plan = world.plan(ParentAction, 20)
|
70
70
|
future = world.execute plan.id
|
71
|
-
wait_for { future.
|
71
|
+
wait_for { future.resolved? }
|
72
72
|
plan = world.persistence.load_execution_plan(plan.id)
|
73
73
|
action = plan.entry_action
|
74
74
|
|
@@ -79,7 +79,7 @@ module Dynflow
|
|
79
79
|
FailureSimulator.should_fail!
|
80
80
|
plan = world.plan(ParentAction, 20)
|
81
81
|
future = world.execute plan.id
|
82
|
-
wait_for { future.
|
82
|
+
wait_for { future.resolved? }
|
83
83
|
plan = world.persistence.load_execution_plan(plan.id)
|
84
84
|
action = plan.entry_action
|
85
85
|
action.output[:batch_count].must_equal 1
|
@@ -87,7 +87,7 @@ module Dynflow
|
|
87
87
|
|
88
88
|
FailureSimulator.wont_fail!
|
89
89
|
future = world.execute plan.id
|
90
|
-
wait_for { future.
|
90
|
+
wait_for { future.resolved? }
|
91
91
|
action = future.value.entry_action
|
92
92
|
future.value.state.must_equal :stopped
|
93
93
|
action.output[:batch_count].must_equal (action.total_count / action.batch_size) + 1
|
@@ -98,7 +98,7 @@ module Dynflow
|
|
98
98
|
it 'is controlled only by total_count and output[:planned_count]' do
|
99
99
|
plan = world.plan(ParentAction, 10)
|
100
100
|
future = world.execute plan.id
|
101
|
-
wait_for { future.
|
101
|
+
wait_for { future.resolved? }
|
102
102
|
plan = world.persistence.load_execution_plan(plan.id)
|
103
103
|
action = plan.entry_action
|
104
104
|
action.send(:can_spawn_next_batch?).must_equal false
|
@@ -124,7 +124,7 @@ module Dynflow
|
|
124
124
|
total = 10
|
125
125
|
plan = world.plan(ParentAction, 10)
|
126
126
|
future = world.execute plan.id
|
127
|
-
wait_for { future.
|
127
|
+
wait_for { future.resolved? }
|
128
128
|
plan.sub_plans.all? { |sub| successful? sub }
|
129
129
|
world.throttle_limiter.core.ask!(:running).must_equal [0]
|
130
130
|
end
|
@@ -134,7 +134,7 @@ module Dynflow
|
|
134
134
|
level = 4
|
135
135
|
plan = world.plan(ParentAction, total, level)
|
136
136
|
future = world.execute plan.id
|
137
|
-
wait_for { future.
|
137
|
+
wait_for { future.resolved? }
|
138
138
|
world.throttle_limiter.core.ask!(:running).max.must_be :<=, level
|
139
139
|
end
|
140
140
|
|
@@ -145,7 +145,7 @@ module Dynflow
|
|
145
145
|
triggered = world.execute(plan.id)
|
146
146
|
wait_for { plan.sub_plans_count == total }
|
147
147
|
world.event(plan.id, plan.steps.values.last.id, ::Dynflow::Action::Cancellable::Cancel)
|
148
|
-
wait_for { triggered.
|
148
|
+
wait_for { triggered.resolved? }
|
149
149
|
plan = world.persistence.load_execution_plan(plan.id)
|
150
150
|
plan.entry_action.output[:failed_count].must_equal total
|
151
151
|
world.throttle_limiter.core.ask!(:running).max.must_be :<=, 0
|
@@ -198,7 +198,7 @@ module Dynflow
|
|
198
198
|
world.throttle_limiter.observe(plan.id).dup.each do |triggered|
|
199
199
|
triggered.future.tap do |future|
|
200
200
|
klok.progress
|
201
|
-
wait_for { future.
|
201
|
+
wait_for { future.resolved? }
|
202
202
|
end
|
203
203
|
finished += 1
|
204
204
|
check_step(plan, total, finished)
|
@@ -217,7 +217,7 @@ module Dynflow
|
|
217
217
|
time_span = 10
|
218
218
|
plan = world.plan(ParentAction, total, level, time_span)
|
219
219
|
future = world.execute(plan.id)
|
220
|
-
wait_for { future.
|
220
|
+
wait_for { future.resolved? }
|
221
221
|
plan.sub_plans.all? { |sub| sub.result == :error }.must_equal true
|
222
222
|
end
|
223
223
|
|
@@ -234,7 +234,7 @@ module Dynflow
|
|
234
234
|
running.count.must_equal level
|
235
235
|
world.throttle_limiter.observe(plan.id).length.must_equal (total - 1)
|
236
236
|
4.times { klok.progress }
|
237
|
-
wait_for { future.
|
237
|
+
wait_for { future.resolved? }
|
238
238
|
finished, stopped = plan.sub_plans.partition { |sub| successful? sub }
|
239
239
|
finished.count.must_equal level
|
240
240
|
stopped.count.must_equal (total - level)
|
data/test/daemon_test.rb
CHANGED
@@ -18,7 +18,7 @@ class DaemonTest < ActiveSupport::TestCase
|
|
18
18
|
@dummy_world.stubs(:id => '123')
|
19
19
|
@dummy_world.stubs(:auto_execute)
|
20
20
|
@dummy_world.stubs(:perform_validity_checks => 0)
|
21
|
-
@event = Concurrent.
|
21
|
+
@event = Concurrent::Promises.resolvable_event
|
22
22
|
@dummy_world.stubs(:terminated).returns(@event)
|
23
23
|
@world_class.stubs(:new).returns(@dummy_world)
|
24
24
|
@dynflow = ::Dynflow::Rails.new(
|
@@ -38,7 +38,7 @@ class DaemonTest < ActiveSupport::TestCase
|
|
38
38
|
end
|
39
39
|
|
40
40
|
teardown do
|
41
|
-
@event.
|
41
|
+
@event.resolve
|
42
42
|
@event.wait
|
43
43
|
end
|
44
44
|
|
data/test/dispatcher_test.rb
CHANGED
@@ -40,7 +40,7 @@ module Dynflow
|
|
40
40
|
step = plan.steps.values.first
|
41
41
|
future = client_world.event(plan.id, step.id, 'finish')
|
42
42
|
future.wait
|
43
|
-
assert future.
|
43
|
+
assert future.rejected?
|
44
44
|
end
|
45
45
|
|
46
46
|
it 'succeeds when executor acts as client' do
|
@@ -73,27 +73,27 @@ module Dynflow
|
|
73
73
|
it 'succeeds when the world is available' do
|
74
74
|
ping_response = client_world.ping(executor_world.id, 0.5)
|
75
75
|
ping_response.wait
|
76
|
-
assert ping_response.
|
76
|
+
assert ping_response.fulfilled?
|
77
77
|
end
|
78
78
|
|
79
79
|
it 'succeeds when the world is available without cache' do
|
80
80
|
ping_response = client_world.ping_without_cache(executor_world.id, 0.5)
|
81
81
|
ping_response.wait
|
82
|
-
assert ping_response.
|
82
|
+
assert ping_response.fulfilled?
|
83
83
|
end
|
84
84
|
|
85
85
|
it 'time-outs when the world is not responding' do
|
86
86
|
executor_world.terminate.wait
|
87
87
|
ping_response = client_world.ping(executor_world.id, 0.5)
|
88
88
|
ping_response.wait
|
89
|
-
assert ping_response.
|
89
|
+
assert ping_response.rejected?
|
90
90
|
end
|
91
91
|
|
92
92
|
it 'time-outs when the world is not responding without cache' do
|
93
93
|
executor_world.terminate.wait
|
94
94
|
ping_response = client_world.ping_without_cache(executor_world.id, 0.5)
|
95
95
|
ping_response.wait
|
96
|
-
assert ping_response.
|
96
|
+
assert ping_response.rejected?
|
97
97
|
end
|
98
98
|
|
99
99
|
it 'caches the pings and pongs' do
|
@@ -121,7 +121,7 @@ module Dynflow
|
|
121
121
|
executor_world_2.terminate.wait
|
122
122
|
result = client_world.trigger(Support::DummyExample::Dummy)
|
123
123
|
result.finished.wait
|
124
|
-
assert result.finished.
|
124
|
+
assert result.finished.rejected?
|
125
125
|
assert_match(/No executor available/, result.finished.reason.message)
|
126
126
|
end
|
127
127
|
end
|
data/test/execution_plan_test.rb
CHANGED
@@ -141,6 +141,17 @@ module Dynflow
|
|
141
141
|
|
142
142
|
end
|
143
143
|
|
144
|
+
describe 'sub plans' do
|
145
|
+
let(:execution_plan) do
|
146
|
+
world.plan(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'does not have itself as a sub plan' do
|
150
|
+
assert execution_plan.actions.count >= 2
|
151
|
+
execution_plan.sub_plans.must_be :empty?
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
144
155
|
describe 'plan steps' do
|
145
156
|
let :execution_plan do
|
146
157
|
world.plan(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
data/test/executor_test.rb
CHANGED
@@ -671,7 +671,7 @@ module Dynflow
|
|
671
671
|
result = world.trigger(Support::DummyExample::Slow, 0.02)
|
672
672
|
result.must_be :planned?
|
673
673
|
result.finished.wait
|
674
|
-
assert result.finished.
|
674
|
+
assert result.finished.rejected?
|
675
675
|
result.finished.reason.must_be_kind_of Concurrent::Actor::ActorTerminated
|
676
676
|
end
|
677
677
|
|
@@ -143,7 +143,7 @@ module Support
|
|
143
143
|
world.clock.ping suspended_action, input[:timeout], "timeout"
|
144
144
|
end
|
145
145
|
|
146
|
-
sub_plan.finished.
|
146
|
+
sub_plan.finished.on_fulfillment! { suspended_action << 'finish' }
|
147
147
|
end
|
148
148
|
end),
|
149
149
|
(on 'finish' do
|
data/test/test_helper.rb
CHANGED
@@ -25,8 +25,8 @@ Concurrent.disable_at_exit_handlers!
|
|
25
25
|
class TestPause
|
26
26
|
|
27
27
|
def self.setup
|
28
|
-
@pause = Concurrent.
|
29
|
-
@ready = Concurrent.
|
28
|
+
@pause = Concurrent::Promises.resolvable_future
|
29
|
+
@ready = Concurrent::Promises.resolvable_future
|
30
30
|
end
|
31
31
|
|
32
32
|
def self.teardown
|
@@ -38,10 +38,10 @@ class TestPause
|
|
38
38
|
def self.pause
|
39
39
|
if !@pause
|
40
40
|
raise 'the TestPause class was not setup'
|
41
|
-
elsif @ready.
|
41
|
+
elsif @ready.resolved?
|
42
42
|
raise 'you can pause only once'
|
43
43
|
else
|
44
|
-
@ready.
|
44
|
+
@ready.fulfill(true)
|
45
45
|
@pause.wait
|
46
46
|
end
|
47
47
|
end
|
@@ -51,7 +51,7 @@ class TestPause
|
|
51
51
|
if @pause
|
52
52
|
@ready.wait # wait till we are paused
|
53
53
|
yield
|
54
|
-
@pause.
|
54
|
+
@pause.fulfill(true) # resume the run
|
55
55
|
else
|
56
56
|
raise 'the TestPause class was not setup'
|
57
57
|
end
|
@@ -249,17 +249,17 @@ events_test = -> do
|
|
249
249
|
event_creations = {}
|
250
250
|
non_ready_events = {}
|
251
251
|
|
252
|
-
Concurrent::
|
252
|
+
Concurrent::Promises::Event.singleton_class.send :define_method, :new do |*args, &block|
|
253
253
|
super(*args, &block).tap do |event|
|
254
254
|
event_creations[event.object_id] = caller(4)
|
255
255
|
end
|
256
256
|
end
|
257
257
|
|
258
|
-
[Concurrent::
|
259
|
-
|
260
|
-
future_class.send :define_method, :
|
258
|
+
[Concurrent::Promises::Event, Concurrent::Promises::ResolvableFuture].each do |future_class|
|
259
|
+
original_resolved_method = future_class.instance_method :resolve_with
|
260
|
+
future_class.send :define_method, :resolve_with do |*args|
|
261
261
|
begin
|
262
|
-
|
262
|
+
original_resolved_method.bind(self).call(*args)
|
263
263
|
ensure
|
264
264
|
event_creations.delete(self.object_id)
|
265
265
|
end
|
@@ -269,9 +269,9 @@ events_test = -> do
|
|
269
269
|
MiniTest.after_run do
|
270
270
|
Concurrent::Actor.root.ask!(:terminate!)
|
271
271
|
|
272
|
-
non_ready_events = ObjectSpace.each_object(Concurrent::
|
272
|
+
non_ready_events = ObjectSpace.each_object(Concurrent::Promises::Event).map do |event|
|
273
273
|
event.wait(1)
|
274
|
-
unless event.
|
274
|
+
unless event.resolved?
|
275
275
|
event.object_id
|
276
276
|
end
|
277
277
|
end.compact
|
@@ -294,9 +294,9 @@ events_test = -> do
|
|
294
294
|
|
295
295
|
# time out all futures by default
|
296
296
|
default_timeout = 8
|
297
|
-
wait_method = Concurrent::
|
297
|
+
wait_method = Concurrent::Promises::AbstractEventFuture.instance_method(:wait)
|
298
298
|
|
299
|
-
Concurrent::
|
299
|
+
Concurrent::Promises::AbstractEventFuture.class_eval do
|
300
300
|
define_method :wait do |timeout = nil|
|
301
301
|
wait_method.bind(self).call(timeout || default_timeout)
|
302
302
|
end
|
@@ -308,7 +308,7 @@ events_test.call
|
|
308
308
|
|
309
309
|
class ConcurrentRunTester
|
310
310
|
def initialize
|
311
|
-
@enter_future, @exit_future = Concurrent.
|
311
|
+
@enter_future, @exit_future = Concurrent::Promises.resolvable_future, Concurrent::Promises.resolvable_future
|
312
312
|
end
|
313
313
|
|
314
314
|
def while_executing(&block)
|
@@ -319,12 +319,12 @@ class ConcurrentRunTester
|
|
319
319
|
end
|
320
320
|
|
321
321
|
def pause
|
322
|
-
@enter_future.
|
322
|
+
@enter_future.fulfill(true)
|
323
323
|
@exit_future.wait(1)
|
324
324
|
end
|
325
325
|
|
326
326
|
def finish
|
327
|
-
@exit_future.
|
327
|
+
@exit_future.fulfill(true)
|
328
328
|
@thread.join
|
329
329
|
end
|
330
330
|
end
|