5

I have 2 functions. The first function is opening a file in write mode and writing some contents to it and then closing it.

FILE *fp = fopen("file.txt", "w");
//writing itnot file using fwrite
fclose(fp);

The second function opens the file in read mode, parses the content and then closes the file.

FILE *fp = fopen("file.txt", "r");
//parsing logic
fclose(fp);

In main, I am calling function1 and function2 sequentially.

int main()
{
    function1();
    function2();
    return 1;
}

Sometimes, function1 fopen fails with error number 13 i.e. Permission Denied. I am observing this only sometimes. I introduced a sleep in function1 after fclose for 2 seconds and it started working fine without any issues.

So I am suspecting file is not immediately released after fclose. Sleep is not the right solution. Can anyone suggest how to resolve this problem? The example I have given here is a use case and the actual code is running in a thread environment.

DeiDei
  • 10,205
  • 6
  • 55
  • 80
Arun
  • 2,247
  • 3
  • 28
  • 51
  • What OS? What filesystem? Also, you need to check the return value from `fclose()` as it can fail and you can lose data. – Andrew Henle May 09 '18 at 14:25
  • 3
    There is a note in the Linux `man` pages for `fclose`: *Note that fclose() flushes only the user-space buffers provided by the C library. To ensure that the data is physically stored on disk the kernel buffers must be flushed too, for example, with sync(2) or fsync(2).* – cdarke May 09 '18 at 14:29
  • C does not specify that there is any delay between when you `fclose()` a file and when you can subsequently `fopen()` the same underlying path, though it allows plenty of freedom for that to be the case for specific implementations. For that matter, C does not forbid the same underlying path being open multiple times simultaneously, and some implementations in fact permit that. – John Bollinger May 09 '18 at 14:41
  • 2
    Is this under Windows? I've had weird issues like this under Windows. I can't imagine this happening under Unix or Linux. – Steve Summit May 09 '18 at 14:49
  • This could be an antivirus problem. I've stumbled upon such a thing a few years ago. Disable the antivirus and see if the problems goes away. – Jabberwocky May 09 '18 at 14:53
  • windows10 is the OS and NFTS is the filesystem – Arun May 09 '18 at 14:53
  • will this be solved if I use fflush? – Arun May 09 '18 at 14:55
  • There is no reason to think that calling `fflush()` before `fclose()` will help, because `fclose()` implies `fflush()`. You could try, but even if you see improved behavior, it will be difficult to be confident that the problem is fully solved. – John Bollinger May 09 '18 at 14:58
  • Did you mean that the fopen in function**2** sometimes fails? – rici May 09 '18 at 16:25

3 Answers3

2

Draft N1570 for C11 says as 7.21.5.1 The fclose function

A successful call to the fclose function causes the stream pointed to by stream to be flushed and the associated file to be closed. Any unwritten buffered data for the stream are delivered to the host environment to be written to the file; any unread buffered data are discarded. Whether or not the call succeeds, the stream is disassociated from the file and any buffer set by the setbuf or setvbuf function is disassociated from the stream (and deallocated if it was automatically allocated).

It makes no assumption on what happens at the host environment level, that is does the function returns only when the whole operation is finished, or does it returns as soon as a request has been queued.

As race conditions can happen in your environment, you should retry a failed open a number of times, eventually with a delay between them. If portability is not a problem and if your system supports the POSIX sync function, you can also force a disk synchronisation of the file after closing it:

  • Close part:

    ...
    fclose(fp)
    sync();         // forces synchronization of io buffers to disk 
    
  • Re-open part

    ntries = ...;   // number of open tries
    while (ntries-- > 0) {
        fp = fopen(...);
        if (fp != NULL) break;   // open was successful
        // optionaly add a delay
    }
    
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
1

In an environment and with a C implementation where you must accommodate such behavior, the best approach is probably to implement some fault tolerance around the fopen()s. Although an unconditional sleep() is not the right answer, short, conditional delays via sleep() or a similar function may indeed be part of such an strategy. For example, you might do something along these lines:

#include <stdio.h>
#include <errno.h>
#define MAX_ATTEMPTS 3

FILE *tolerant_fopen(const char *path, const char *mode) {
    FILE *result;
    int attempts = 0;

    while (!(result = fopen(path, mode))) {
        if (errno != EACCES || attempts >= MAX_ATTEMPTS) {
            break;
        }
        if (sleep(1) == 0) {
            attempts += 1;
        }
    }

    return result;
}

That attempts to open the file immediately, and in the event that that fails on account of access permissions, it waits a short time and then makes another attempt. Overall it may make three or more attempts to open the file, spaced up to a second apart or perhaps slightly more. (Note that sleep() can be interrupted early; in that case it returns the number of seconds left to sleep.)

You can of course implement a different strategy for the timing and duration of the retries if you prefer, retry more error conditions, etc..

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
0

If a child process is spawned (even from another thread) while a file is open, then the child process will inherit the file handle and the file will not be fully closed until after the child process has terminated.

To prevent this behavior pass the "N" flag to fopen:

FILE *fp = fopen("file.txt", "wN");
finalman
  • 774
  • 5
  • 14