Skip to content

Commit

Permalink
Explicit specification of graceful shutdown behaviour.
Browse files Browse the repository at this point in the history
  • Loading branch information
ioquatix committed Mar 25, 2024
1 parent db97f98 commit 74b0c20
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 31 deletions.
25 changes: 0 additions & 25 deletions examples/async.rb

This file was deleted.

72 changes: 72 additions & 0 deletions examples/grace/server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2024, by Samuel Williams.

require '../../lib/async/container'
require 'async/io/host_endpoint'

Console.logger.debug!

module SignalWrapper
def self.trap(signal, &block)
signal = signal

original = Signal.trap(signal) do
::Signal.trap(signal, original)
block.call
end
end
end

class Controller < Async::Container::Controller
def initialize(...)
super

@endpoint = Async::IO::Endpoint.tcp("localhost", 8080)
@bound_endpoint = nil
end

def start
Console.debug(self) {"Binding to #{@endpoint}"}
@bound_endpoint = Sync{@endpoint.bound}

super
end

def setup(container)
container.run count: 2, restart: true do |instance|
SignalWrapper.trap(:INT) do
Console.debug(self) {"Closing bound instance..."}
@bound_endpoint.close
end

Sync do |task|
Console.info(self) {"Starting bound instance..."}

instance.ready!

@bound_endpoint.accept do |peer|
while true
peer.write("#{Time.now.to_s.rjust(32)}: Hello World\n")
sleep 1
end
end
end
end
end

def stop(graceful = true)
super

if @bound_endpoint
@bound_endpoint.close
@bound_endpoint = nil
end
end
end

controller = Controller.new

controller.run
14 changes: 8 additions & 6 deletions lib/async/container/controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Controller

# Initialize the controller.
# @parameter notify [Notify::Client] A client used for process readiness notifications.
def initialize(notify: Notify.open!, container_class: Container)
def initialize(notify: Notify.open!, container_class: Container, graceful: true)
@container = nil
@container_class = container_class

Expand All @@ -35,6 +35,8 @@ def initialize(notify: Notify.open!, container_class: Container)
trap(SIGHUP) do
self.restart
end

@graceful = graceful
end

# The state of the controller.
Expand Down Expand Up @@ -96,7 +98,7 @@ def start

# Stop the container if it's running.
# @parameter graceful [Boolean] Whether to give the children instances time to shut down or to kill them immediately.
def stop(graceful = true)
def stop(graceful = @graceful)
@container&.stop(graceful)
@container = nil
end
Expand Down Expand Up @@ -130,7 +132,7 @@ def restart
if container.failed?
@notify&.error!("Container failed to start!")

container.stop
container.stop(false)

raise SetupError, container
end
Expand All @@ -142,7 +144,7 @@ def restart

if old_container
Console.logger.debug(self, "Stopping old container...")
old_container&.stop
old_container&.stop(@graceful)
end

@notify&.ready!
Expand Down Expand Up @@ -211,11 +213,11 @@ def run
end
end
rescue Interrupt
self.stop(true)
self.stop
rescue Terminate
self.stop(false)
ensure
self.stop(true)
self.stop(false)

# Restore the interrupt handler:
Signal.trap(:INT, interrupt_action)
Expand Down
1 change: 1 addition & 0 deletions lib/async/container/group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ def wait_for(channel)
protected

def wait_for_children(duration = nil)
Console.debug(self, "Waiting for children...", duration: duration)
if !@running.empty?
# Maybe consider using a proper event loop here:
readable, _, _ = ::IO.select(@running.keys, nil, nil, duration)
Expand Down

0 comments on commit 74b0c20

Please sign in to comment.