views:

631

answers:

7

I get an EInOutError with message 'Too many open files' when executing this code block repeatedly for some time from a number of client threads:

var InputFile : Text;
...
Assign (InputFile, FileName);
Reset (InputFile)
try
  // do some stuff
finally
  CloseFile (InputFile);
end;

The number of client threads is approximately 10, so only 10 files can be open at any time. Is there any possibility that Delphi refuses to close files right away? Can I ensure that it does? Or am I making a mistake here? This is the only place where I open files and the try..finally block should guarantee that opened files get closed, shouldn't it?

REEDIT: forget the edit

+1  A: 

Delphi closes immidiately in the CloseFile. Your example code seems to be correct.

Try again without anything between try and finally.

BennyBechDk
same error without anything between try and finally
Smasher
A: 

Might be useful to put some debug output alongside the Reset and the Close so you can see how long each thread has the file open for.

Steve Claridge
A: 

Do you really need threads? It sounds like they are causing you problems. Your code would be easier to debug without them.

dangph
The threads are simply for simulating multiple clients...they doesn't add any complexity.
Smasher
I see nothing in this or any of Smasher's other questions that would suggest having trouble with threads.
Rob Kennedy
+1  A: 

You aren't running this on an older Windows 9x based computer, are you? If so, you might be running into a DOS filehandle problem.

Costa Rica Dev
no, I'm using windows xp for the server machine and Windows Vista on the client machine
Smasher
+4  A: 

I can only advise you to use the more "modern" facilities for dealing with files. I don't know whether there is a limit of open files using the Windows API, but I just tested and could easily open 1000 streams in parallel:

procedure TForm1.Button1Click(Sender: TObject);
var
  Strs: TList;
  i: integer;
begin
  Strs := TList.Create;
  try
    for i := 1 to 1000 do begin
      Strs.Add(TFileStream.Create('D:\foo.txt', fmOpenRead or fmShareDenyWrite));
    end;
  finally
    FreeObjectList(Strs);
  end;
end;

I have never understood why people still use untyped files instead of TStream and its descendants in new code.

Edit: In your comment you write that you only want to read plain text files - if so just create a TStringList and use its LoadFromFile() method.

mghie
The text files are in plain text format and I can't change the format. Can I use TReader to read normal text files instead of tagged/typed files?
Smasher
Just create a TStrings instance and use its LoadFromFile() method - no use to deal with files at all if all you need to do is loading a text file line by line.
mghie
I changed it like you proposed it. It seems to work. I don't understand why it didn't work in the first place. However, I'm happy for now ... thanks!
Smasher
A: 

This code should work just fine. There are no known problems related to using files from threaded code (as far as I know). We use such idioms fairly regularly and everything works fine.

I would suggest adding some logging code (before Assign and CloseFile) to see if a) close is executed and b) you really have only 10 threads running. Maybe your thread terminating logic is faulty and CloseFile never executes.

gabr
For a while, I think the OpenMode global variable could cause file-related code to be non-thread-safe, but that's fixed nowadays, and wasn't a major risk anyway since hardly anyone every writes to it.
Rob Kennedy
+1  A: 

There IS a thread safety issue here although I can't see how it could cause the problem.

The problem is Reset uses the global FileMode variable.

As for client threads--are you sure they aren't leaking away on broken connections or something?

Loren Pechtel