1

Currently I try to improve the design of two windows services (C#). Service A produces data exports (csv files) and writes them to a temporary directory. So the file is written to a temporary directory that is a sub dir. of the main output directory. Then the file is moved (via File.Move) to the output directory (after a successful write). This export may be performed by multiple threads.

Another service B tries to fetch the files from this output directory in a defined interval. How to assure that Directory.GetFiles() excludes locked files.

  1. Should I try to check every file by creating a new FileStream (using (Stream stream = new FileStream("MyFilename.txt", FileMode.Open)) as described here.

  2. Or should the producer service (A) use temporary file names (*.csv.tmp) that are automatically excluded by the consumer serivce (B) with appropriate search pattterns. And rename a file after the move was finished.

  3. Are there better ways to handle such file listing operations.
Community
  • 1
  • 1
Marco Mayer
  • 197
  • 2
  • 10
  • http://stackoverflow.com/questions/1304/how-to-check-for-file-lock-in-c – Corak Feb 26 '13 at 12:06
  • Why not use a `FileSystemWatcher` on the output directory? That way, service B is notified immediately when a file is put there, and doesn't have to worry about scanning. – Jim Mischel Feb 26 '13 at 12:54
  • RE FileSystemWatcher - if this fires when the file is created, rather than when it's closed, it could actually be worse than periodic polling, as 'B' would go after it most likely when it's locked. – FastAl Feb 26 '13 at 15:03

2 Answers2

0

One way would be to mark the files as temporary from the writing app whilst they're in use, and only unmark them once they are written to and closed, eg.

FileStream f = File.Create (filename);
FileAttributes attr = File.GetAttributes (filename);
File.SetAttributes (filename, attr | FileAttributes.Temporary);
 //write to file.
f.Close ();

File.SetAttributes (filename, attr);

From the consuming app, you just want to skip any temporary files.

foreach (var file in Directory.GetFiles (Path.GetDirectoryName (filename))) {
    if ((File.GetAttributes (file) & FileAttributes.Temporary) != 0) continue;
    // do normal stuff.
}
Mark H
  • 13,797
  • 4
  • 31
  • 45
  • 1
    File could get hit by 'B' after line 1 but before line 3. Also when writing the file via file.move, this type of control isn't possible. – FastAl Feb 26 '13 at 15:05
  • Well noticed. I suppose you could create the file in a different directory to where B monitors and have it set Temporary before moving it - then unset the Temporary attribute after the move has completed. – Mark H Feb 26 '13 at 15:08
  • True that. The move would probably preserve the attribe (hopefully atomic-ly) – FastAl Feb 26 '13 at 15:19
0

Don't bother checking!

Huh? How can that be?

If the files are on the same drive, a Move operation is atomic! The operation is effectively a rename, erasing the directory entry from the previous, and inserting it into the next directory, pointing to the same sectors (or whatevers) where the data really are, without rewriting it. The file system's internal locking mechanism has to lock & block directory reads during this process to prevent a directory scan from returning corrupt results.

That means, by the time it ever shows up in a directory, it won't be locked; in fact, the file won't have been opened/modified since the close operation that wrote it to the previous directory.

caveats - (1) definitely won't work between drives, partitions, or other media mounted as a subdirectory. The OS does a copy+delete behind the scenes instead of a directory entry edit. (2) this behaviour is a convention, not a rule. Though I've never seen it, file systems are free to break it, and even to break it inconsistently!

So this will probably work. If it doesn't, I'd recommend using your own idea of temp extensions (I've done it before for this exact purpose, but between a client and server that only could talk by communicating via a shared drive) and it's not that hard and worked flawlessly.

If your own idea is too low-tech, and you're on the same machine (sounds like you are), you can set a mutex (google that), with the filename embedded, that lives while the file is being written, in the writer process; then do a blocking test on it when you open each file you are reading from the other process. If you want the second process to respond ASAP combine this with the filesystem watcher. Then pat yourself on the back for spending ten times the effort as the temp filename idea, with no extra gain >:-}

good luck!

FastAl
  • 6,194
  • 2
  • 36
  • 60