STDOUT.close
13 Oct 2014Closing STDOUT
and then creating another ruby process has some
interesting behaviors across different versions and
implementations of ruby. Consider the following two files:
close_and_redirect.rb
STDERR.puts "STDOUT closed?: #{STDOUT.closed?}"
STDERR.puts "sleeping... try lsof -p #{$$}\n"
sleep 10
STDOUT.close
STDERR.puts "STDOUT closed?: #{STDOUT.closed?}"
STDERR.puts "sleeping... try lsof -p #{$$}\n"
sleep 10
exec "ruby", "test_stdout.rb"
test_stdout.rb
STDERR.puts "STDOUT closed?: #{STDOUT.closed?}"
STDERR.puts "sleeping... try lsof -p #{$$}"
sleep 10
puts "STDOUT says hello"
MRI 2.0
$ ruby -v close_and_redirect.rb
ruby 2.0.0p481 (2014-05-08 revision 45883) [universal.x86_64-darwin13]
STDOUT closed?: false
sleeping... try lsof -p 59100
$ lsof -p 59100 | grep 1u
ruby 59100 sshao 1u CHR 16,2 0t866 773 /dev/ttys002
$ (continued) ruby -v close_and_redirect.rb
STDOUT closed?: true
sleeping... try lsof -p 59100
$ lsof -p 59100 | grep 1u
ruby 59100 sshao 1u CHR 16,2 0t917 773 /dev/ttys002
# stdout is still open!
$ (continued) ruby -v close_and_redirect.rb
STDOUT closed?: false
sleeping... try lsof -p 59100
STDOUT says hello
$ lsof -p 59100 | grep 1u
ruby 59100 sshao 1u CHR 16,2 0t969 773 /dev/ttys002
stdout (fd 1) is never actually closed -- even when ruby says it is.
RBX 2.x
$ ruby -v close_and_redirect.rb
rubinius 2.2.10 (2.1.0 bf61ae2e 2014-06-27 JI) [x86_64-darwin13.4.0]
STDOUT closed?: false
sleeping... try lsof -p 30091
$ lsof -p 30091 | grep 1u
rbx 30091 sshao 1u CHR 16,2 0t5050 773 /dev/ttys002
$ (continued) ruby -v close_and_redirect.rb
STDOUT closed?: true
sleeping... try lsof -p 30091
$ lsof -p 30091 | grep 1u
# stdout is closed
$ (continued) ruby -v close_and_redirect.rb
ERROR: the VM is exiting improperly running test_stdout.rb
intended operation: :exception
associated value: nil
destination scope: unknown
An exception occurred
closed stream (IOError)
Backtrace:
IO#fileno at kernel/common/io.rb:1560
IO.setup at kernel/common/io.rb:977
IO(File)#initialize at kernel/common/io.rb:995
File#initialize at kernel/common/file.rb:1142
Class#new at kernel/alpha.rb:94
IO.open at kernel/common/io.rb:624
Rubinius::Loader#write_last_error at kernel/loader.rb:794
Rubinius::Loader#main at kernel/loader.rb:854
Unlike MRI 2.0, Rubinius actually closes stdout -- and propagates its
closed status to the exec'd process, which fails in Rubinius::Loader
due
to the closed stream.
MRI 1.8
$ ruby -v close_and_redirect.rb
ruby 1.8.7 (2012-10-12 patchlevel 371) [i686-darwin13.4.0]
STDOUT closed?: false
sleeping... try lsof -p 59166
$ lsof -p 59166 | grep 1u
ruby 59166 sshao 1u CHR 16,1 0t3231295 771 /dev/ttys001
STDOUT closed?: true
sleeping... try lsof -p 59166
$ lsof -p 59166 | grep 1u
# stdout is closed!
STDOUT closed?: false
sleeping... try lsof -p 59166
$ lsof -p 59166 | grep 1u
# stdout is still closed!
Although lsof
shows us that stdout is closed in the last process
(after the exec), ruby tells us that STDOUT.closed?
is false! Furthermore,
the puts "STDOUT says hello"
is never output.