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
enddef self.spawn(*arguments, name: nil, **options)
Spawn a child process using ::Process.spawn.
The child process will need to inform the parent process that it is ready using a notification protocol.
Signature
	- 
					parameter argumentsArray
- The arguments to pass to the new process. 
- 
					parameter nameString
- The name of the process. 
- 
					parameter optionsHash
- Additional options to pass to - ::Process.spawn.
Implementation
						def self.spawn(*arguments, name: nil, **options)
	self.new(name: name) do |process|
		Notify::Pipe.new(process.out).before_spawn(arguments, options)
		
		::Process.spawn(*arguments, **options)
	end
enddef initialize(name: nil, **options)
Initialize the process.
Signature
	- 
					parameter nameString
- The name to use for the child process. 
Implementation
						def initialize(name: nil, **options)
	super(**options)
	
	@name = name
	@status = nil
	@pid = nil
	
	@pid = yield(self)
	
	# The parent process won't be writing to the channel:
	self.close_write
enddef as_json(...)
Convert the child process to a hash, suitable for serialization.
Signature
	- 
					returns Hash
- The request as a hash. 
Implementation
						def as_json(...)
	{
		name: @name,
		pid: @pid,
		status: @status&.to_i,
	}
enddef to_json(...)
Convert the request to JSON.
Signature
	- 
					returns String
- The request as JSON. 
Implementation
						def to_json(...)
	as_json.to_json(...)
enddef 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?
endattr :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}>"
endalias to_s inspect
Signature
	- 
					returns String
- A string representation of the process. 
def close
Invoke #terminate! and then #wait for the child process to exit.
Implementation
						def close
	self.terminate!
	self.wait
ensure
	super
enddef interrupt!
Send SIGINT to the child process.
Implementation
						def interrupt!
	unless @status
		::Process.kill(:INT, @pid)
	end
enddef terminate!
Send SIGTERM to the child process.
Implementation
						def terminate!
	unless @status
		::Process.kill(:TERM, @pid)
	end
enddef kill!
Send SIGKILL to the child process.
Implementation
						def kill!
	unless @status
		::Process.kill(:KILL, @pid)
	end
enddef restart!
Send SIGHUP to the child process.
Implementation
						def restart!
	unless @status
		::Process.kill(:HUP, @pid)
	end
enddef wait(timeout = 0.1)
- asynchronous
Wait for the child process to exit.
Signature
	- asynchronous
- This method may block. 
- 
					parameter timeoutNumeric | Nil
- Maximum time to wait before forceful termination. 
- 
					returns ::Process::Status
- The process exit status. 
Implementation
						def wait(timeout = 0.1)
	if @pid && @status.nil?
		Console.debug(self, "Waiting for process to exit...", child: {process_id: @pid}, timeout: timeout)
		
		_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
		
		if @status.nil?
			sleep(timeout) if timeout
			
			_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
			
			if @status.nil?
				Console.warn(self, "Process is blocking, sending kill signal...", child: {process_id: @pid}, caller: caller_locations, timeout: timeout)
				self.kill!
				
				# Wait for the process to exit:
				_, @status = ::Process.wait2(@pid)
			end
		end
	end
	
	Console.debug(self, "Process exited.", child: {process_id: @pid, status: @status})
	
	return @status
end