Skip to content

Commit 3a93a8f

Browse files
Reopen controller event queue for each run
Assisted-By: devx/3236e566-7538-432e-a30a-2bdf37265ed4
1 parent b184f2e commit 3a93a8f

2 files changed

Lines changed: 71 additions & 6 deletions

File tree

lib/async/container/controller.rb

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def initialize(notify: Notify.open!, container_class: Container, graceful_stop:
6464
@container = nil
6565
@events = ::Thread::Queue.new
6666
@signals = Async::Signals::Handlers.new
67+
@running = false
6768

6869
# Serializes lifecycle transitions such as start, restart and reload. `Container#stop` (which can also take time) is performed outside this guard, so that live container events are not blocked by the stop operation (e.g. restarting).
6970
@guard = ::Thread::Mutex.new
@@ -117,7 +118,7 @@ def trap(signal, &block)
117118
event = SignalEvent.new(signal, block).freeze
118119

119120
@signals.trap(signal) do
120-
@events << event
121+
enqueue_event(event)
121122
end
122123
else
123124
@signals.ignore(signal)
@@ -272,12 +273,44 @@ def reload
272273
end
273274
end
274275

275-
private def wait_for_container
276+
private def enqueue_event(event)
277+
@events << event
278+
rescue ::ClosedQueueError
279+
# The controller run loop has already stopped.
280+
end
281+
282+
private def open_event_queue
283+
@guard.synchronize do
284+
if @running
285+
raise RuntimeError, "Controller is already running."
286+
end
287+
288+
@running = true
289+
@events = ::Thread::Queue.new
290+
end
291+
end
292+
293+
private def close_event_queue(events)
294+
events.close
295+
end
296+
297+
private def finish_event_queue(events)
298+
events.close
299+
300+
@guard.synchronize do
301+
if @events.equal?(events)
302+
@running = false
303+
@events = ::Thread::Queue.new
304+
end
305+
end
306+
end
307+
308+
private def wait_for_container(events)
276309
while true
277310
container = @guard.synchronize{@container}
278311

279312
if container.nil?
280-
@events.close
313+
close_event_queue(events)
281314
return
282315
end
283316

@@ -287,7 +320,7 @@ def reload
287320
# If this is still the active container, it completed naturally. Clear it and close the event queue so the controller run loop can finish. If it was replaced by a restart, keep waiting for the new active container.
288321
if @container.equal?(container)
289322
@container = nil
290-
@events.close
323+
close_event_queue(events)
291324
return
292325
end
293326
end
@@ -298,14 +331,15 @@ def reload
298331
# @parameter signals [#install] The signal backend to use while running the controller.
299332
def run(signals: Async::Signals.default)
300333
@notify&.status!("Initializing controller...")
334+
events = open_event_queue
301335

302336
signals.install(@signals) do
303337
Sync do |task|
304338
self.start
305339

306-
waiter = task.async{wait_for_container}
340+
task.async{wait_for_container(events)}
307341

308-
while event = @events.pop
342+
while event = events.pop
309343
event.call
310344
end
311345
rescue Async::Cancel
@@ -318,6 +352,8 @@ def run(signals: Async::Signals.default)
318352
end
319353
rescue Interrupt
320354
self.stop(false)
355+
ensure
356+
finish_event_queue(events) if events
321357
end
322358
end
323359
end

test/async/container/controller.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,35 @@ def controller.setup(container)
187187
end
188188
end
189189

190+
with "#run" do
191+
it "can run the same controller more than once" do
192+
input, output = IO.pipe
193+
194+
controller.instance_variable_set(:@output, output)
195+
196+
def controller.setup(container)
197+
container.spawn do |instance|
198+
instance.ready!
199+
200+
sleep(0.01)
201+
202+
@output.puts("done")
203+
@output.flush
204+
end
205+
end
206+
207+
2.times do
208+
controller.run(signals: Async::Signals::Ignore)
209+
210+
expect(IO.select([input], nil, nil, 1)).not.to be_nil
211+
expect(input.gets).to be == "done\n"
212+
end
213+
ensure
214+
input&.close
215+
output&.close
216+
end
217+
end
218+
190219
with "graceful controller" do
191220
include_context Async::Container::AController, "graceful"
192221

0 commit comments

Comments
 (0)