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