65 lines
1.3 KiB
Ruby
65 lines
1.3 KiB
Ruby
module Mutant
|
|
# Module providing isolationg
|
|
module Isolation
|
|
Error = Class.new(RuntimeError)
|
|
|
|
# Call block in isolation
|
|
#
|
|
# This isolation implements the fork strategy.
|
|
# Future strategies will probably use a process pool that can
|
|
# handle multiple mutation kills, in-isolation at once.
|
|
#
|
|
# @return [Object]
|
|
#
|
|
# @raise [Error]
|
|
#
|
|
# @api private
|
|
#
|
|
def self.call(&block)
|
|
reader, writer = IO.pipe.each(&:binmode)
|
|
|
|
pid = fork
|
|
|
|
if pid.nil?
|
|
begin
|
|
reader.close
|
|
writer.write(Marshal.dump(block.call))
|
|
Kernel.exit!(0)
|
|
ensure
|
|
Kernel.exit!(1)
|
|
end
|
|
end
|
|
|
|
writer.close
|
|
|
|
read_result(reader, pid)
|
|
end
|
|
|
|
# Read result from child process
|
|
#
|
|
# @param [IO] reader
|
|
# @param [Fixnum] pid
|
|
#
|
|
# @return [Object]
|
|
#
|
|
# @raise [Error]
|
|
#
|
|
def self.read_result(reader, pid)
|
|
begin
|
|
data = Marshal.load(reader.read)
|
|
rescue ArgumentError, TypeError
|
|
raise Error, 'Childprocess wrote un-unmarshallable data'
|
|
end
|
|
|
|
status = Process.waitpid2(pid).last
|
|
|
|
unless status.exitstatus.zero?
|
|
raise Error, "Childprocess exited with nonzero exit status: #{status.exitstatus}"
|
|
end
|
|
|
|
data
|
|
end
|
|
private_class_method :read_result
|
|
|
|
end # Isolator
|
|
end # Mutant
|