2

I have the following code:

FILE *file = fopen("testfile.txt", "r+");
int fdfile = open("testfile.txt", O_RDONLY | O_SYNC);

const char word[] = "writing to a file and read test";
fwrite(word, sizeof(word)-1, 1, file);

fsync(fdfile);

char word1[1024] = {0};
int n = read(fdfile, word1, sizeof(word)-1);

printf("%s\n%d", word1, n);

fclose(file);

Just before running this, the file testfile.txt has the content prev content in it

Once I run this, I get

prev content
12

even though it should be the latest contents, writing to a file and read test. I have used fsync(fdfile), that doesn't work. I have also tried sync() and fsync(fileno(file)), none of which work, even though they should because they are supposed to flush the contents to disk after the write

I know the situation can be mitigated by using the same file descriptor to read and write both, but I am not in a position to do that in my code. How can I mitigate this issue and sync the contents?

2

The open(), read(), write(), etc. functions are low level functions defined by Posix. They usually involve a buffer at the system level (kernel) and this is the buffer you are flushing to the actual medium with fsync() and you don't have much control over that buffer.

The fopen(), fread(), fwrite(), etc. functions are higher level functions offered by the C language. They involve and additional buffer which you can configure with setbuf(). This buffer is typically a line-buffer (flushed at each end of line \n) for text files (thus including stdin, stdout and stderr), and a block-buffer (flushed when the buffer is full) for binary files.

Of course this is discouraged to mix calls to both low-level and high-level read/write functions. Your issue is due to the fact that the high-level buffer used by fwrite() is not flushed (to the low-level buffer, which you flush with fsync()).

The solution is not to call fsync() or open with O_SYNC (you can remove those from your code); it is either:

  • to explicitly flush the high-level buffer with fflush() when you need to
  • or to make the high-level functions unbuffered with setbuf(fdfile, _IONBF)

The latter is easier but buffers are here to improve efficiency and unbuffering the high-level calls will cause each fwrite() to trigger a corresponding write() system call which may have an impact on performances, that's why you might prefer to fflush() when you deem it necessary.

1
  • 1
    fseek will also flush the buffer. – Jasen May 6 at 11:14
0

I figured out the issue.

The FILE* had its own buffer which needs to be fflush()ed before the sync can happen

1
  • 1
    Yes, exactly, see my answer. This is a misunderstanding that catches every C developer at least once, this is why your question is interesting and I took the time to answer. – xhienne May 6 at 10:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.