winapi

Wait for a file to be writable


I am working on a tool which writes data to files.

At some point, a file might be "locked" and is not writable until other handles have been closed.

I could use the CreateFile API in a loop until the file is available for writing access.

But I have 2 concerns using CreateFile in a loop:

So my question is:

What is the best solution to wait for a file to be writable and instantly get a valid handle?

Are there any event solutions or anything, which allows to "queue/reserve" for a handle once, so that there is no "uncontrolled" race condition with others?


Solution

  • A file can be "locked" for two reasons:

    1. An actual file lock which prevents writing to, and possibly reading from the file.
    2. The file being opened without sharing access (accidentially or voluntarily) which even prevents you from opening a handle. If you already see CreateFile failing, that's likely the case rather than a real lock.

    There are conceptually[1] at least two ways of knowing that no other process has locked a file without busy waiting:

    1. By finding out who holds locks and waiting on the process or thread to exit (or, by outright killing them...)
    2. By locking the file yourself

    Who holds locks?
    Finding out about lock owners is rather nasty, you can do it via the totally undocumented SystemLocksInformation class used with the undocumented NtQuerySystemInformation function (the latter is "only undocumented", but the former is so much undocumented that it's really hard to find any information at all). The returned structure is explained here, and it contains an owning thread id.

    Luckily, holding a lock presumes holding a handle. Closing the file handle will unlock all file ranges. Which means: No lock without handle.

    In other words, the problem can also be expressed as "who is holding an open handle to the file?". Of course not all processes that hold a handle to a file will have the file locked, but no process having a handle guarantees that no process has the file locked.
    Code for finding out which processes have a file open is much easier (using restart manager) and is readily available at Raymond Chen's site.

    Now that you know which processes and threads are holding file handles and locks, make a list of all thread/process handles and use WaitForMultipleObjects on the list of process handles. When a process exits, all handles are closed.
    This also transparently deals with the possibility of a "lock" because a process does not share access.

    Locking the file yourself
    You can use LockFileEx, which operates asynchronously. Note that LockFileEx needs a valid handle that has been opened with either read or write permissions (getting write permission may not be possible, but read should work almost always -- even if you are prevented from actually reading by an exclusive lock, it's still possible to create a handle that could read if there was no lock).

    You can then wait on the asynchronous locking to complete either via the event in the OVERLAPPED structure, or on a completion port, and can even do other useful stuff in the mean time, too. Once you have locked the file, you know that nobody else has it locked.


    [1] The wording _"conceptually"_ suggests that I am pretty sure either method will work, but I have not tested them.