Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi: Why do I sometimes get an I/O Error 103 with this code?

Tags:

file-io

delphi

In several of my apps I have code similar to the following:

if ForceDirectories(ExtractFilePath(lLogName)) then
  begin
    AssignFile(lLog, lLogName);
    try
      if FileExists(lLogName) then
        Append(lLog)
      else
        Rewrite(lLog);
      Writeln(lLog, lLogLine);
    finally
      {$I-}CloseFile(lLog);{$I+}
    end;
  end;

In one application, the first time I try to execute this I consistently get an I/O Error 103 exception on the line with the Append statement (the file does exist prior to calling this). All subsequent attempts at the operation will work fine however - until I restart the app.

All the docs I found about this error so far indicated that this would either be caused by calling CloseFile without prior Reset or Rewrite (Append typically isn't mentioned) or if the file was in use by another process. As the exception occurs before the call to CloseFile it obviously couldn't be the former.

I already tried inserting a Reset right after the AssignFile for good measure but then I get the exception on that line.

There is also no other application overtly accessing that file. I say "overtly" because I do have a slight suspicion that anti-virus (TrendMicro in my case) might be the cuplrit here (so maybe the file is in use). If that was indeed the problem, what would be the best way around it? Hard-coding an automatic retry does not really feel like a clean solution to me...


Another case where I sometimes get the 103 error is this code, which I use to create an empty file (or more often to empty an existing file):

AssignFile(lFile, AFileName);
try
  Rewrite(lFile);
finally
  CloseFile(lFile);
end;

In this case it's much harder to reproduce. It happens a lot less often. Most of the time this seems to happen on the first run after I recompiled the application. Could this again be the anti-virus getting in the way? I have only ever seen this happen on my development machine and never gotten a report from a customer. As with the first scenario this only ever happens once per application session (if at all). Subsequent attempts are always successful.

Any suggestions for a different, potentially more fail-safe approach to creating empty files or emptying existing ones?

like image 257
Oliver Giesen Avatar asked Mar 11 '09 13:03

Oliver Giesen


2 Answers

Okay, it's over a year late, but I'm going to add my comment to this, as it explains why this is happening.

I had the exact same problem in a multi-threaded application with code almost identical to the snippet above and I had critical sections protecting the code.

The problem occurred most readily when one logging operation swiftly followed another. The second operation would fail for the above reason.

I thought it was anti-virus software too, but the error happened on one machine and not the other, where both had Norton 360 installed. The machine with the problem was brand new Windows 7 and the one without was Windows XP. A colleague also had the problem running the system under a virtualised Windows Vista machine with no virus checker installed.

So my question was, "why was this XP machine so different?".

For one, it wasn't virgin, and that is the answer it seems:

Opportunistic locking and NT caching were turned off. Most (mature) Delphi developers will know that when using BDE, these are turned off in order to maintain DBF and DB file integrity in multi-user situations. These settings were not disabled on the newer machines because we no longer develop for Paradox data files!

Delayed write caching seems to leave a read/write lock on the file until the OS has done its business, which could be several milliseconds later.

So, in my case, the second log event was being blocked by the first.

Okay, I'm not suggesting that you turn off opportunistic locking + NT Caching. One of the major reasons we moved away from BDE was to avoid persuading customers to tinker with such settings. So, there are four practical solutions:

1) To retry for an acceptable period of time, as mentioned by dangph.

2) to open the file when the application loads and hold it open for the full duration of the application. Not so useful if you are running multiple instances of the application.

3) Lazily put a sleep(1) before the logging code and hope that is long enough for the lock to be released. But that risks slowing your system down if you are doing lots of logging.

or 4) Put a try...except..end around your code. But then you are probably guaranteed to miss 100% of the second messages (referring to my case).

like image 127
William Coppock Avatar answered Oct 25 '22 20:10

William Coppock


Besides anti-virus it can also be indexing software or file management software, like Google Desktop. However, the real problem here is, that the error message doesn't help you solve the problem. I suggest that you rewrite the code to use TFileStream, instead, just in order to improve your error messages.

like image 27
Lars D Avatar answered Oct 25 '22 18:10

Lars D