Michael Weber writes:
> On Feb 8, 2008, at 23:40 , Richard M Kreuter wrote:
> > (let ((truename/nil (probe-file filespec)))
> > ;; If we get this far, the file system is in a good way either for
> > ;; trying to open an existing file or trying to create a new one.
> > ...)
> Did you take into account that the situation can change between the
> probe-file call and the call to open?
> If the only way to make this reliable is to open the file
> unconditionally (and deal with possible errors) it seems to me that
> having probe-file signal an error is not helping all that much.
I had a paragraph in my previous message about race conditions, but cut
it for length. I agree with you about race conditions, but I think the
question of whether PROBE-FILE is appropriate for a particular program
is different from what PROBE-FILE's semantics should be.
First, I hope we can agree that where PROBE-FILE is the wrong thing, it
doesn't matter at all what its semantics are. So maybe having a more
persnickety PROBE-FILE will make people think a bit before using
More importantly, there are some programs that must go one way or
another depending on whether a file exists. In fact, CL:OPEN is one of
them, at least under some implementation strategies. The IF-EXISTS
actions are to be performed only if the file exists, for example. And
some valid (if peculiar) combinations of IF-EXISTS and
IF-DOES-NOT-EXISTS flags cannot be implemented by a straight call to
Unix's open(2): when IF-EXISTS and IF-DOES-NOT-EXIST are both either NIL
or :ERROR, for example, no file should be opened, but Unix's open(2)
doesn't have a combination of flags that can do this without also having
side-effects on the file system. Further, for openings that create a
new file when one already exists (e.g., :RENAME, :RENAME-AND-DELETE),
there are a number of actions that might have to be done during OPEN:
resolving the truename, renaming or deleting existing files, etc. [*].
Personally, I find that it makes for a better separation of concerns in
a hairy program like OPEN to bail out (or offer a restart) as early as
possible when some prerequisite like the existence of the directory can
be found not to be satisfied. So IMO this is anecdotal evidence that
there's a place for an operator that returns NIL only when the host,
device, and directory exist but the file does not. I grant that
PROBE-FILE need not be that operator, but I've already argued that
having PROBE-FILE return NIL in many cases makes it hard to know what to
do with a NIL from PROBE-FILE, so ISTM that having PROBE-FILE be the
operator I want to have in OPEN is the right thing.
[*] This does mean that CL:OPEN (or CL:CLOSE) cannot be implemented as a
single, atomic operation, at least not all paths through it. But
there's no reason to suppose that OPEN could be atomic
portably-across-Lisps anyway: a Lisp can conformingly access files using
network protocols that don't support any atomic operations, and a
network connection can always drop out. And even for Lisps that access
files only through Unix file system calls, a Unix can't always guarantee
atomicity of system calls that translate to network accesses. So
there's race conditions all the way down, really.
In fact, CL:OPEN has never been atomic during the history of SBCL, and
AFAICT not in CMU since at least 1993. But if the experience of people
using SBCL and CMU for the last 15 years counts as empirical evidence,
these sorts of race conditions don't occur often enough or repeatably
enough in practice to matter.
However, that's not an argument for ignoring race conditions. In fact
when a program is driven by a fact like "the file existed when I looked
for it", you can code the rest of it in a manner that catches changes to
the file system after that determination (e.g., if the file existed when
you checked, don't use O_CREAT in a subsequent call to open(2) that
file, and if it didn't exist, use O_CREAT and O_EXCL when trying to
create a fresh file). And this means you can signal errors that
indicate that the race condition, which might even be more useful to the
user, who might not expect concurrent munging of the file system. (I
can explain in more detail if you're interested, but right now I must
catch a train. Happy hacking.)