Problem writing to file after closing $stdout & $stderr

So I have a program now that starts off tests on remote machines, and
is supposed to fork itself off, wait for the tests to finish, and
fetch their logfiles back to the server.

Here’s the bit of relevant code:

def closeFileHandles
if($opt_debug)
$stdin.close()
$stderr.reopen(’/tmp/stderr’, ‘a’)
$stdout = $stderr
else
$stdout.close(); $stderr.close(); $stdin.close()
end
end

def RunTest(test,run_id)
handle = startTest(@machine, test, run_id)
fork {
logfile = File.open(’/tmp/testout’, ‘a’)
logfile.sync = true
logfile.puts "about to close filehandles"
closeFileHandles()
logfile.puts "waiting for test to finish"
sleep 20 while ProcessRunning?(handle, run_id)
logfile.puts "go get logfiles"
getLogFiles(test, run_id)
logfile.close()
}
end

The problem is, if $opt_debug is not set, and $stdout, $stderr, and
$stdin are closed, then no data past “about to close filehandles” gets
written to /tmp/testout. Actually, something seems to crash the child
during or after closeFileHandles(), since I wrote this code to debug
why the logfiles weren’t getting copied back.

If I’m missing something, then please clue me in; I’ve been tearing my
hair out trying to fix this.

-=Eric

···


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton

[snip]

perhaps something in the parent is writing to stderr/out? i ask because this
works fine for me

def close
$stderr.close
$stdout.close
$stdin.close
end

def method
fork do
f = File.open ‘forkout’, ‘w’
f.puts Time.now
close
#$stdout.puts ‘foobar’
f.puts Time.now
f.close
end
end

method
Process.wait

unless i uncomment the line writing to $stdout, in which case i get the
behaviour you define too.

-a

···

On 14 Nov 2002, Eric Schwartz wrote:

So I have a program now that starts off tests on remote machines, and
is supposed to fork itself off, wait for the tests to finish, and
fetch their logfiles back to the server.

====================================

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

ahoward ahoward@fsl.noaa.gov writes:

perhaps something in the parent is writing to stderr/out? i ask because this
works fine for me

Why shouldn’t the parent write to stderr/out? The child should
inherit its own copy of all open file descriptors (which at this point
is just stdin, stdout and stderr). Did you mean the child may be
writing?

Here’s a minimal example:

1 puts “before fork”
2 fork {
3 puts “in child”
4 logfile = File.open(‘/tmp/logfile’,‘w’)
5 logfile.sync=true
6 logfile.puts “before cleosing file descriptors”
7 $stdout.close
8 $stdout = nil
9 logfile.puts “closed $stdout”
10 $stderr.close
11 $stderr = nil
12 logfile.puts “closed $stderr”
13 $stdin.close
14 $stdin = nil
15 logfile.puts “closed $stdin”
16 }
17 puts “after fork, in parent”

This crashes at the line “$stdout = nil” with:

/tmp/test.rb:8: closed stream (IOError)
from /tmp/test.rb:3:in ‘fork’
from /tmp/test.rb:3

This is what I don’t get: I closed $stdout on the line before. Why
does it appear to be writing to a closed stream on line 8? I get the
same result if I try to replace line 8 with

$stdout = $stderr

-=Eric

···


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton

7 $stdout.close
8 $stdout = nil

It want to flush $stdout, don't make an assignement after the close

pigeon% cat b.rb
#!/usr/bin/ruby
puts "before fork"
fork do
   puts "in child"
   logfile = File.open('/tmp/logfile','w')
   logfile.sync=true
   logfile.puts "before cleosing file descriptors"
   $stdout.close
   logfile.puts "closed $stdout"
   $stderr.close
   logfile.puts "closed $stderr"
   $stdin.close
   logfile.puts "closed $stdin"
end
puts "after fork, in parent"
pigeon%

pigeon% b.rb
before fork
after fork, in parent
in child
pigeon%

pigeon% cat /tmp/logfile
before cleosing file descriptors
closed $stdout
closed $stderr
closed $stdin
pigeon%

Guy Decoux

ts decoux@moulon.inra.fr writes:

7 $stdout.close
8 $stdout = nil

It want to flush $stdout, don’t make an assignement after the close

Why? $stdout.close should flush the filehandle. At least, in every
other programming language I’ve ever used, closing a filehandle
flushes its output. I cannot see any good reason why any filehandle,
once closed, should have any I/O associated with it.

I’m all for blaming myself before the language, but this seems very
clearly a bug to me. Am I just missing something?

-=Eric

···


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton

i think this is because sync is false for stdout, stderr etc. eg. the close
is a request to close and flush, but the os may defer the flush. that is,
unless sync were true for all the closed handles…

-a

···

On 15 Nov 2002, Eric Schwartz wrote:

ts decoux@moulon.inra.fr writes:

7 $stdout.close
8 $stdout = nil

It want to flush $stdout, don’t make an assignement after the close

Why? $stdout.close should flush the filehandle. At least, in every
other programming language I’ve ever used, closing a filehandle
flushes its output. I cannot see any good reason why any filehandle,
once closed, should have any I/O associated with it.

====================================

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

ahoward ahoward@fsl.noaa.gov writes:

Why? $stdout.close should flush the filehandle. At least, in every
other programming language I’ve ever used, closing a filehandle
flushes its output. I cannot see any good reason why any filehandle,
once closed, should have any I/O associated with it.

i think this is because sync is false for stdout, stderr etc. eg. the close
is a request to close and flush, but the os may defer the flush. that is,
unless sync were true for all the closed handles…

Yes, but the point is that close() should flush the filehandle from
Ruby’s perspective. If the OS wants to delay its flush, that’s fine,
it’s allowed, but Ruby should not be trying to do a flush (or, indeed,
any I/O on any filehandle, be it $stdout or $myfilehandle) after
calling close() on it.

I’ve filed a bug on ruby-lang.org about this, hopefully someone can
take a look at it. In the meantime, I’ve managed to work around it.

-=Eric

···

On 15 Nov 2002, Eric Schwartz wrote:

Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton