class Shrimple::Process

Attributes

start_time[R]
stop_time[R]

Public Class Methods

new(cmd, inio, outio, errio, timeout=nil) click to toggle source

runs cmd, passes instr on its stdin, and fills outio and errio with the command’s output.

# File lib/shrimple/process.rb, line 15
def initialize cmd, inio, outio, errio, timeout=nil
  @start_time = Time.now
  @chin, @chout, @cherr, @child = Open3.popen3(*cmd)

  Shrimple.processes._add(self)
  @chout.binmode

  @killed = false
  @timed_out = false

  @thrin  = Thread.new { drain(inio, @chin) }
  @throut = Thread.new { drain(@chout, outio) }
  @threrr = Thread.new { drain(@cherr, errio) }

  # ensure cleanup is called when the child exits. (strange it requires a whole new thread...?)
  @thrchild = Thread.new {
    if timeout
      outatime unless @child.join(timeout)
    else
      @child.join
    end
    stop
  }
end

Public Instance Methods

_child_thread() click to toggle source

only meant to be used by the ProcessMonitor

# File lib/shrimple/process.rb, line 83
def _child_thread
  @child
end
_cleanup() click to toggle source

may only be called once, synchronized by stop()

# File lib/shrimple/process.rb, line 88
def _cleanup
  raise "Someone else already stopped this process??!!" if @stop_time
  @stop_time = Time.now
end
_deactivate() click to toggle source

returns true if process was previously active. must be externally synchronized.

# File lib/shrimple/process.rb, line 94
def _deactivate
  retval = @inactive
  @inactive = true
  return !retval
end
finished?() click to toggle source
# File lib/shrimple/process.rb, line 41
def finished?
  @stop_time != nil
end
kill(seconds_until_panic=2) click to toggle source

kill-o-zaps the phantom process now (using -9 if needed), then waits until it’s truly gone

# File lib/shrimple/process.rb, line 59
def kill seconds_until_panic=2
  @killed = true
  if @child.alive?
    # rescue because process might have died between previous line and this one
    ::Process.kill("TERM", @child.pid) rescue Errno::ESRCH
  end
  if !@child.join(seconds_until_panic)
    ::Process.kill("KILL", @child.pid) if @child.alive?
  end
  # ensure kill doesn't return until process is truly gone
  # (there may be a chance of this deadlocking with a blocking callback... not sure)
  @thrchild.join unless Thread.current == @thrchild
end
killed?() click to toggle source
# File lib/shrimple/process.rb, line 50
def killed?
  @killed
end
stop() click to toggle source

waits patiently until phantom process terminates, then cleans up

# File lib/shrimple/process.rb, line 74
def stop
  wait_for_the_end   # do all our waiting outside the sync loop
  Shrimple.processes._remove(self) do
    _cleanup
  end
end
success?() click to toggle source

returns false if the process hasn’t finished yet

# File lib/shrimple/process.rb, line 46
def success?
  finished? && @child.value.success? ? true : false
end
timed_out?() click to toggle source
# File lib/shrimple/process.rb, line 54
def timed_out?
  @timed_out
end

Private Instance Methods

drain(reader, writer) click to toggle source

reads every last drop, then closes both files. must be threadsafe.

# File lib/shrimple/process.rb, line 113
def drain reader, writer
  begin
    # randomly chosen buffer size
    loop { writer.write(reader.readpartial(256*1024)) }
  rescue EOFError
    # not an error
    # puts "EOF STDOUT" if reader == @chout
    # puts "EOF STDERR" if reader == @cherr
    # puts "EOF STDIN #{reader}" if writer == @chin
  rescue Errno::EPIPE
    # child was killed, no problem
  ensure
    reader.close
    writer.close rescue Errno::EPIPE
  end
end
outatime() click to toggle source
# File lib/shrimple/process.rb, line 107
def outatime
  @timed_out = true
  kill
end
wait_for_the_end() click to toggle source
# File lib/shrimple/process.rb, line 102
def wait_for_the_end
  [@thrin, @throut, @threrr, @child].each(&:join)
  @thrchild.join unless Thread.current == @thrchild
end