Async::ContainerSourceAsyncContainerForkedChild

class Child

Represents a running child process from the point of view of the parent container.

Nested

Definitions

def self.fork(**options)

Fork a child process appropriate for a container.

Signature

returns Process

Implementation

def self.fork(**options)
	# $stderr.puts fork: caller
	self.new(**options) do |process|
		::Process.fork do
			# We use `Thread.current.raise(...)` so that exceptions are filtered through `Thread.handle_interrupt` correctly.
			Signal.trap(:INT) {::Thread.current.raise(Interrupt)}
			Signal.trap(:TERM) {::Thread.current.raise(Terminate)}
			Signal.trap(:HUP) {::Thread.current.raise(Restart)}
			
			# This could be a configuration option:
			::Thread.handle_interrupt(SignalException => :immediate) do
				yield Instance.for(process)
			rescue Interrupt
				# Graceful exit.
			rescue Exception => error
				Console.error(self, error)
				
				exit!(1)
			end
		end
	end
end

def initialize(name: nil)

Initialize the process.

Signature

parameter name String

The name to use for the child process.

Implementation

def initialize(name: nil)
	super()
	
	@name = name
	@status = nil
	@pid = nil
	
	@pid = yield(self)
	
	# The parent process won't be writing to the channel:
	self.close_write
end

def name= value

Set the name of the process. Invokes ::Process.setproctitle if invoked in the child process.

Implementation

def name= value
	@name = value
	
	# If we are the child process:
	::Process.setproctitle(@name) if @pid.nil?
end

attr :name

The name of the process.

Signature

attribute String

attr :pid

Signature

attribute Integer

The process identifier.

def inspect

A human readable representation of the process.

Signature

returns String

Implementation

def inspect
	"\#<#{self.class} name=#{@name.inspect} status=#{@status.inspect} pid=#{@pid.inspect}>"
end

def close

Invoke #terminate! and then #wait for the child process to exit.

Implementation

def close
	self.terminate!
	self.wait
ensure
	super
end

def interrupt!

Send SIGINT to the child process.

Implementation

def interrupt!
	unless @status
		::Process.kill(:INT, @pid)
	end
end

def terminate!

Send SIGTERM to the child process.

Implementation

def terminate!
	unless @status
		::Process.kill(:TERM, @pid)
	end
end

def restart!

Send SIGHUP to the child process.

Implementation

def restart!
	unless @status
		::Process.kill(:HUP, @pid)
	end
end

def wait

  • asynchronous

Wait for the child process to exit.

Signature

asynchronous

This method may block.

returns ::Process::Status

The process exit status.

Implementation

def wait
	if @pid && @status.nil?
		Console.debug(self, "Waiting for process to exit...", pid: @pid)
		
		_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)

		while @status.nil?
			sleep(0.1)
			
			_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
			
			if @status.nil?
				Console.warn(self) {"Process #{@pid} is blocking, has it exited?"}
			end
		end
	end
	
	Console.debug(self, "Process exited.", pid: @pid, status: @status)
	
	return @status
end