Class: Async::Container::Process

Inherits:
Channel
  • Object
show all
Defined in:
lib/async/container/process.rb

Overview

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

Defined Under Namespace

Classes: Instance

Instance Attribute Summary collapse

Attributes inherited from Channel

#in, #out

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Channel

#close_read, #close_write, #receive

Constructor Details

#initialize(name: nil) ⇒ Process

Initialize the process.



98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/async/container/process.rb', line 98

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

Instance Attribute Details

#nameObject

The name of the process.



122
123
124
# File 'lib/async/container/process.rb', line 122

def name
  @name
end

Class Method Details

.fork(**options) ⇒ Object

Fork a child process appropriate for a container.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/async/container/process.rb', line 66

def self.fork(**options)
	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)}
			
			begin
				yield Instance.for(process)
			rescue Interrupt
				# Graceful exit.
			rescue Exception => error
				Console.logger.error(self) {error}
				
				exit!(1)
			end
		end
	end
end

Instance Method Details

#closeObject

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



131
132
133
134
135
136
# File 'lib/async/container/process.rb', line 131

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

#interrupt!Object

Send ‘SIGINT` to the child process.



139
140
141
142
143
# File 'lib/async/container/process.rb', line 139

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

#terminate!Object

Send ‘SIGTERM` to the child process.



146
147
148
149
150
# File 'lib/async/container/process.rb', line 146

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

#to_sObject

A human readable representation of the process.



126
127
128
# File 'lib/async/container/process.rb', line 126

def to_s
	"\#<#{self.class} #{@name}>"
end

#waitObject

Wait for the child process to exit.



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/async/container/process.rb', line 154

def wait
	if @pid && @status.nil?
		_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
		
		if @status.nil?
			sleep(0.01)
			_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
		end
		
		if @status.nil?
			Console.logger.warn(self) {"Process #{@pid} is blocking, has it exited?"}
			_, @status = ::Process.wait2(@pid)
		end
	end
	
	return @status
end