develooper Front page | perl.perl5.porters | Postings from September 2012

How to close two FILE*s sharing the same fd?

Thread Next
From:
Steve Hay
Date:
September 6, 2012 06:09
Subject:
How to close two FILE*s sharing the same fd?
Message ID:
1B32FF956ABF414C9BCE5E487A1497E70E49E203@ukmail02.planit.group
Running perl on Windows with recent versions of VC++ can generate
reports of invalid parameters passed to CRT functions, and I'm not sure
what is best to do with one such report emanating from this code in
Perl_io_close():

    if (IoOFP(io) && IoOFP(io) != IoIFP(io)) {		/* a socket */
	const bool prev_err = PerlIO_error(IoOFP(io));
	retval = (PerlIO_close(IoOFP(io)) != EOF && !prev_err);
	PerlIO_close(IoIFP(io));	/* clear stdio, fd already
closed */
    }

IoOFP(io) and IoIFP(io) are both FILE*s, but they share the same
underlying fd. Thus, the PerlIO_close(IoOFP(io)) call closes the fd in
IoIFP(io) too, hence the comment that the PerlIO_close(IoIFP(io)) call
is just to "clear stdio", the fd being already closed.

The problem is that this triggers the "invalid parameter handler",
complaining that the underlying fd is already closed. It's arguably a
bug in the MS CRT code, but it's one of those cases where it doesn't
know better. It would be correct for it to complain if the low I/O
close() were directly called twice on the same file descriptor, but in
this case we really ought to fclose() both FILE*s to avoid the risk of
leaking any resources allocated for them and it has no way of knowing
that that's what's happening.

However, whether it's a CRT bug or not, I would still like to find a way
to avoid the noisy output. I could simply temporarily disable the
handler as I've already had to do [but not yet committed] for signals
(win32_signal() often quite knowingly calls signal() with arguments
which are not accepted by the CRT), but the signal code is
Windows-specific so the hack isn't too bad there; I'd rather avoid a
Windows-specific hack like that in doio.c if possible.

The following short program reproduces the problem:

#include <stdio.h>
void main(void) {
    FILE* f1;
    FILE* f2;
    if ((f1 = fopen("test.c", "r")) == NULL)
        return;
    if ((f2 = _fdopen(_fileno(f1), "r")) == NULL) {
        fclose(f1);
        return;
    }
    fclose(f2);
    fclose(f1); /* Triggers invalid parameter handler! */
}

Is there a good way to clean up / clear up / free any allocated
resources in f1 without triggering a second close() on the underlying
file descriptor?

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About