runtime/forked_popen_parens.rb
tests/_examples/runtime/forked_popen_parens.rb in the metorex repository.
# `IO.popen("-")` forks. The child answers nil with its standard output and
# input joined to pipes, and the parent answers a stream over the other ends.
io = IO.popen("-")
if io
p(io.gets())
p(io.pid().is_a?(Integer))
io.close()
p($?.success?())
else
puts("hello from child")
exit!()
end
# "r+" joins both ways, and closing the stream waits for the child.
both = IO.popen("-", "r+")
if both
both.puts("to child")
both.close_write()
p(both.read())
both.close()
p($?.exitstatus())
else
line = STDIN.gets()
puts("child read #{line.inspect()}")
exit!(3)
end
# With a block the parent's block gets the stream and answers the result,
# while the child's gets nil and the child exits after it.
answered = IO.popen("-") do |pipe|
if pipe
pipe.read()
else
puts("from the child's block")
end
end
p(answered)
# A stream opened one way refuses the other.
writing = IO.popen("-", "w")
if writing
p((writing.read() rescue $!.class()))
writing.close()
else
exit!()
end
# `fork` goes through `Process._fork`, and both answer `respond_to?`.
p(Process.respond_to?(:fork))
p(Process.respond_to?(:_fork))
child = Process._fork()
Process.exit!(5) if child == 0
p(Process.wait2(child)[1].exitstatus())
# A shared buffer over a file is the same bytes in both processes.
path = "/tmp/metorex_forked_popen_#{Process.pid()}.txt"
File.write(path, "I'm private")
file = File.open(path, "r+")
buffer = IO::Buffer.map(file)
IO.popen("-") do |pipe|
if pipe
pipe.read()
p(buffer.get_string())
else
buffer.set_string("I'm shared!")
end
end
p(File.read(path))
file.close()
File.delete(path)
# `exit!` without a status leaves with false, which is 1.
bare = fork() { exit!() }
p(Process.wait2(bare)[1].exitstatus())
Output:
"hello from child\n"
true
false
"child read \"to child\\n\"\n"
3
"from the child's block\n"
IOError
true
true
5
"I'm shared!"
"I'm shared!"
1