File Pointer Status

E

Eric Sosman

Might it be possible that the FILE is lazily free'd rather than
immediately? I know that free'd pointers that were malloc'd can still
temporarily hold the same data.

Do I detect some wishful thinking here? *Don't* go through
the door you seem to be thinking about opening; heed the sign
that reads "Lasciate ogni speranza, voi ch'entrate" and turn
away before it's too late.

Or if Dante isn't your dish, see

http://www.cirris.com/humor/images/danger_50kohms.png
 
B

Billy Mays

Yes, his program closed it, but it may have done so via some library
that he doesn't control.

If the OP controls the code that opens and closes the file, he needs
to modify that code so it keeps track of the file's current status.

If he doesn't, then sadly he has a real problem for which the
language offers no solution.

More details might help us to offer a better solution.

In my program, I had two pthreads that shared a pointer to a common
struct with one of its attributes being a file pointer. Each thread
would lock a mutex (also in the struct) and then do any file IO. When
the producer was done, it would close the FILE pointer. I had wanted to
make the consumer check to see if the FILE pointer was closed in order
to determine if it should continue waiting for data, which is why I
wanted to see if the FILE pointer was still valid.

While this isn't a great approach, the solution was trivial: add
another int to the struct signifying the producer's status. I was just
trying to get away with using as few variables in that shared struct as
possible. Thanks for the help c.l.c
 
R

robertwessel2

On 5/20/2010 1:41 PM, Ben Pfaff wrote:
Is it possible to determine if a given file pointer (FILE *) is still
open or not?
No.  It's unsafe to do anything with a closed FILE.
In my current situation, I have a file pointer that I do not know the
status.  It might still be open or closed.  [...]
Right.  Like I said, it's unsafe to do anything with a closed
FILE.  So if the file might be closed, you can't do anything with
it.

And, even if there were some implementation-specific way of checking if a
FILE* is still open, there's nothing that prevents a subsequent fopen() from
returning the same pointer as was previously fclose()d.

In other words, even though such an implementation-specific method may
return true for a given FILE*, that doesn't necessarily mean that it's the
FILE* you think it is.


Although a system that never reuse handles (obviously fopen() wouldn't
be returning an actual pointer in that case), is at least
theoretically possible, and has some nice attributes from a debugging/
development standpoint. It wouldn't even be unreasonably difficult or
inefficient to implement, you could use a hash table to look up the
translation for a handle to whatever internal structure it logically
references, or add a version number to the actual handle index that
can be tested. It would pretty much require handles bigger than 32
bits, though.

Of course I don't actually know of any system that's actually
implemented that.
 
M

Moi

In my program, I had two pthreads that shared a pointer to a common
struct with one of its attributes being a file pointer. Each thread
would lock a mutex (also in the struct) and then do any file IO. When
the producer was done, it would close the FILE pointer. I had wanted to
make the consumer check to see if the FILE pointer was closed in order
to determine if it should continue waiting for data, which is why I
wanted to see if the FILE pointer was still valid.

While this isn't a great approach, the solution was trivial: add
another int to the struct signifying the producer's status. I was just
trying to get away with using as few variables in that shared struct as
possible. Thanks for the help c.l.c

Why would you need another "indicator" variable?
Setting the file pointer to NULL (after closing the file)
serves the same purpose.

HTH,
AvK
 
N

Nobody

This will work, except that if any other file descriptor is
opened after the previous one is closed, the previous file
descriptor might be associated with a new and entirely different
file (or with something that is not a traditional file at all).

This can also occur with stdio streams, although it's somewhat less
likely.
 
N

Nobody

Might it be possible that the FILE is lazily free'd rather than
immediately?

Who cares whether its *possible*? It's *possible* to play Russian
roulette and survive; that doesn't mean that it's a good idea.

Once a stdio stream has been closed, any dereference of the FILE*
*might* cause segfault.
 
N

Nobody

Even freopen()?

Yes, even freopen().

Once you call fclose() on a FILE*, the implementation may (and probably
will) free() it. This may result in the underlying memory being unmapped,
resulting in subsequent access causing a segfault. And even if it isn't,
the memory may be re-used for something else; it could even look
identical to a valid FILE while not actually being one (this isn't as
unlikely as it seems; given that neither free() nor malloc() will
typically overwrite the memory, if whatever re-uses the memory only
modifies portions of it, the remainder will have the same contents as when
it was a valid FILE).
C99 7.19.5.4 "The freopen function", p4: "The freopen function first
attempts to close any file that is associated with the specified stream."

My English could be failing me, but it says "any" file, not "the" file.

The stream might not be associated with a file. It's conceivable that
an implementation could disassociate the stream from its file if EOF or
certain errors occur, or the stream might never have been associated with
a file in the first place (e.g. GNU libc allows you to create streams
which read or write blocks of memory).
 
K

Keith Thompson

Nobody said:
Who cares whether its *possible*? It's *possible* to play Russian
roulette and survive; that doesn't mean that it's a good idea.

Once a stdio stream has been closed, any dereference of the FILE*
*might* cause segfault.

Any *reference* of the FILE* might cause a segfault (or any other
arbitrarily bad behavior). For example:

fclose(fp);
if (fp == NULL) /* UB */
 
E

Ersek, Laszlo

Yes, even freopen().

Once you call fclose() on a FILE*, the implementation may (and probably
will) free() it. This may result in the underlying memory being
unmapped, resulting in subsequent access causing a segfault. And even if
it isn't, the memory may be re-used for something else; it could even
look identical to a valid FILE while not actually being one (this isn't
as unlikely as it seems; given that neither free() nor malloc() will
typically overwrite the memory, if whatever re-uses the memory only
modifies portions of it, the remainder will have the same contents as
when it was a valid FILE).

What convinces me ultimately is 7.19.3p4, especially the parenthesized
part:

----v----
[...] The value of a pointer to a FILE object is indeterminate after the
associated file is closed (including the standard text streams). [...]
----^----

The stream might not be associated with a file.

Yes, that was my exact point originally: the wording suggests there are at
least some cases where a stream not associated with a file can be used
with freopen(). Like, an fclose[d](stdout). But now I'm asking, how?

Re-quoting 7.19.3p4:

----v----
A file may be disassociated from a controlling stream by closing the file.
Output streams are flushed (any unwritten buffer contents are transmitted
to the host environment) before the stream is disassociated from the file.
The value of a pointer to a FILE object is indeterminate after the
associated file is closed (including the standard text streams). [...]
----^----

If the stream is not associated with a file (because it was closed), then
the pointer to the FILE object can't be evaluated at all. (I may have an
actual pointer object that aliases stdout.) You can't pass the value of
that pointer to freopen(). This means two things: (1) a standard text
stream, once closed, is completely lost for the rest of the program, (2)
if the quoted sentence of 7.19.5.4p4,

----v----
The freopen function first attempts to close any file that is associated
with the specified stream.
----^----

is meant as "the freopen function first attempts to close *the* file that
is associated with the specified stream (*if there is any*)"

then the parenthesized part is a no-op, because it can never happen. A
stream that is not associated with a file cannot be operated on by
freopen().

It's conceivable that an implementation could disassociate the stream
from its file if EOF or certain errors occur,

I don't think so. See clearerr(), and the part above which suggests (to me
at least) that the only way of disassociation is fclose().

or the stream might never have been associated with a file in the first
place (e.g. GNU libc allows you to create streams which read or write
blocks of memory).

Yes, exactly, you can create a TCP socket and put an (advisably
unbuffered) stdio stream on top of it with fdopen(), but I'm convinced
"file" in the quoted language doesn't refer to "regular file" in the
UNIX(R) sense. See 7.19.2p1:

----v----
Input and output, whether to or from physical devices such as terminals
and tape drives, or whether to or from files supported on structured
storage devices, are mapped into logical data /streams/ [...]
----^----

and 7.19.3p1:

----v----
A stream is associated with an external file (which may be a physical
device) by opening a file, which may involve creating a new file. [...]
----^----

In short,
- you can't use freopen() on fclose()'d streams at all (that answers my
original question, thanks everyone),
- disassociation is only achieved by fclose(),
- therefore a correct program can't call freopen() on a stream that is not
associated with a file,
- therefore the wording of 7.19.5.4p4 seems to imply a case that actually
can't occur.

Or, there's a tiny bit of chance that I went wrong somewhere :)

Cheers,
lacos
 
B

BruceS

Any *reference* of the FILE* might cause a segfault (or any other
arbitrarily bad behavior).  For example:

    fclose(fp);
    if (fp == NULL) /* UB */

Does this include assignment? e.g.

fclose(fp);
fp = NULL;
 
S

Seebs

is it because "fp == NULL" implies dereferencing fp while "fp = NULL"
does not?


No, because "fp == NULL" does not imply dereferencing fp. However, it does
imply *reading* fp, and reading an indeterminate value (not of type
unsigned char) is undefined behavior. "fp = NULL", by contrast, does not
imply reading fp.

-s
 
B

Ben Pfaff

superpollo said:
Joachim Schmitz ha scritto:

is it because "fp == NULL" implies dereferencing fp while "fp = NULL"
does not?

No.

It's because 'fp == NULL' implies reading the value of 'fp' but
'fp = NULL' does not.
 
S

sandeep

Keith said:
f0 = fopen(file0, "r");
assert(f0 != NULL);
puts("Closing f0");
fclose_result = fclose(f0);
assert(fclose_result == 0);

f1 = fopen(file1, "r");

What has caught you out here is that you have opened a new file that has
taken the lowest free descriptor - which is now the descriptor of the
file you just closed!

In that case there is obviously nothing you can do, but my code will give
the right answer under favorable circumstances.

On gcc you could also look at fp->_fileno, it gets set to -1 after fclose.
 
S

Seebs

What has caught you out here is that you have opened a new file that has
taken the lowest free descriptor - which is now the descriptor of the
file you just closed!

In other words, your code doesn't work.
In that case there is obviously nothing you can do, but my code will give
the right answer under favorable circumstances.

In other words, on a system which happens to support this behavior, your code
will work some of the time, but be wrong some of the time.

Hey, guys, I wrote an implementation of sqrt(), which gives the right answer
for at least thirty or forty possible numbers, although I can't tell you
in advance which ones!
On gcc you could also look at fp->_fileno, it gets set to -1 after fclose.

That's glibc, not gcc.

-s
 
K

Keith Thompson

sandeep said:
What has caught you out here is that you have opened a new file that has
taken the lowest free descriptor - which is now the descriptor of the
file you just closed!

Actually the new file had the same FILE* pointer value, but that's
probably tied to the descriptor value in the implementation I'm using.

So when I find a flaw in *your* code, it's *my* problem? Are you
personally incapable of error?
In that case there is obviously nothing you can do, but my code will give
the right answer under favorable circumstances.

There are better solutions that give the right answer under both
favorable and unfavorable circumstances.
On gcc you could also look at fp->_fileno, it gets set to -1 after
fclose.

And it gets set back to something other than -1 after the second
fopen().

You say "there is obviously nothing you can do". Of course there
is: keep track of whether the file is open by some other means that
checking whether f0==NULL -- which is what the OP ended up doing.
Or, in some cases, you can structure the code so you never look at
the file after it's been closed (that wasn't possible for the OP,
thus this discussion).

Your suggestion wasn't a bad try, but your continued defense of it
after a flaw has been demonstrated is absurd.
 
N

Nobody

On gcc you could also look at fp->_fileno, it gets set to -1 after fclose.

Then it gets set to something completely arbitrary when the memory is
handed out by a subsequent malloc() then overwritten. That's assuming
that the memory doesn't just get handed back to the OS (i.e. unmapped).
 
N

Nobody

I don't think so. See clearerr(), and the part above which suggests (to me
at least) that the only way of disassociation is fclose().

It depends whether you're talking about "disassociation" as defined by the
standard or in a broader sense.

It's conceivable that on some platforms the "file" (i.e. the underlying OS
entity) associated with the stream could suddenly cease to exist, making
it impossible for the stream to continue to be "associated" with it in any
meaningful sense.

So the "attempts to close any file" could be interpreted as e.g. "fclose()
will call close(fileno(fp)) but doesn't care if it fails with EBADF". If
it did fail with EBADF, it would be reasonable to say that the stream
wasn't actually associated with a file, regardless of whether or not it
"should" have been.
 
N

Nick Keighley

So it is usually rendered. But I think it trips more easily off the
tongue if you leave "abandon" until third place. Try it. Much more
"musical".


hey! I was just quoting google translator. And I have tin ear for
"musical"
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,093
Messages
2,570,609
Members
47,229
Latest member
bennettnguyen

Latest Threads

Top