Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Problem writing to file after closing $stdout & $stderr

1 view
Skip to first unread message

Eric Schwartz

unread,
Nov 14, 2002, 7:29:22 PM11/14/02
to
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

ahoward

unread,
Nov 14, 2002, 8:36:35 PM11/14/02
to Eric Schwartz
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.

[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

--

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

Eric Schwartz

unread,
Nov 15, 2002, 12:28:57 PM11/15/02
to
ahoward <aho...@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

ts

unread,
Nov 15, 2002, 1:09:43 PM11/15/02
to
>>>>> "E" == Eric Schwartz <emsc...@fc.hp.com> writes:

E> 7 $stdout.close
E> 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

Eric Schwartz

unread,
Nov 15, 2002, 1:35:35 PM11/15/02
to
ts <dec...@moulon.inra.fr> writes:
> >>>>> "E" == Eric Schwartz <emsc...@fc.hp.com> writes:
>
> E> 7 $stdout.close
> E> 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?

ahoward

unread,
Nov 15, 2002, 2:08:00 PM11/15/02
to Eric Schwartz
On 15 Nov 2002, Eric Schwartz wrote:

> ts <dec...@moulon.inra.fr> writes:
> > >>>>> "E" == Eric Schwartz <emsc...@fc.hp.com> writes:
> >
> > E> 7 $stdout.close
> > E> 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 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...

Eric Schwartz

unread,
Nov 15, 2002, 2:59:25 PM11/15/02
to
ahoward <aho...@fsl.noaa.gov> writes:
> On 15 Nov 2002, Eric Schwartz wrote:
> > 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.

0 new messages