File_Exists considered harmful?

The function File_Exists (actually Ada.Directories.Exists) should generally be avoided. Instead one should just get on with whatever business one has with the file in question and handle the exceptions that may be raised.

The reason for this is that you (practically) never can be sure that another program doesn't mess with the same file as your program at the same time.

A brainstorm including comp.lang.ada regulars, #Ada (IRC channel on Freenode) and AdaHeads staff has so far only resulted in two use cases for File_Exists.

Open or create file

This procedure will first attempt to open a file with the given name in append mode. If it succeeds all is well. If it doesn't succeed it assumes that the file was missing and attempts to create the file. If that also fails (for example because the file name is invalid) an exception will be raised.

procedure Open_Or_Create (File : in out File_Type;
                          Name : in     String) is
begin
   Open (File => File,
         Name => Name,
         Mode => Append_File);
exception
   when others =>
      Create (File => File,
              Name => Name,
              Mode => Append_File);
end Open_Or_Create;

Similar file system race conditions

Not only “Ada.Directories.Exists” is dangerous and a possible source of race conditions. Other functions checking the state of the file system should also be used with care. Below are a few (so far only one) examples.

Read to end of file

This procedure will read every line in a text file and send it to processing until it reaches the end of the file. Then it will close the file. Even if our loop checked End_Of_File (File) before calling “Get_Line (File)”, we would still have to handle exception End_Error, as another process/task might change the contents/size of the file in parallel with this procedure.

type Processor is access procedure (Item : in String);
 
procedure Process (File         : in out File_Type;
                   Line_Handler : in     Processor) is
begin
   loop
      Line_Handler (Get_Line (File));
   end loop;
exception
   when End_Error =>
      Close (File);
   when others =>
      raise;
end Process;

Navigation