1

I have multiple threads attempting to log to the same file.

Each thread has a FILE * that points to the file. The FILE *s were opened in append ('a') mode and are using line buffering.

Opening multiple FILE * to the same file within the same process is implementation defined according to ANSI C.

Would anyone happen to know the implementation specific behaviour for MacOS, FreeBSD and Linux, specifically whether each FILE * will have its own line buffer, and whether there's any chance of lost or interleaved writes.

Arran Cudbard-Bell
  • 5,912
  • 2
  • 26
  • 48
  • 3
    OT: it would be a lot simpler to have a function (with proper mutex usage) that handles writing to the log file. Race conditions by writing to the same file from multiple threads like these are bound to cause issues sooner or later – UnholySheep Sep 17 '21 at 17:52
  • 1
    @UnholySheep not true in general case. Posix has guarantees about atomic writes to file. – SergeyA Sep 17 '21 at 18:08

1 Answers1

3

MacOS, FreeBSD and Linux are all POSIX systems. As such each FILE* will have its own user-space buffer (or none if you disable it), and once that buffer is flushed it will be written to the underlying file descriptor. POSIX guarantees that append opened file descriptor writes are atomic, thus no data will be lost. As long as your data isn't split across multiple flushes it won't interleave with each other either.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • 1
    While the answer is correct, it would most certainly benefit from quotes from supporting documentation. – SergeyA Sep 17 '21 at 18:08
  • line-buffered writes occur on a newline or when the buffer is full, so writes greater than the buffer size may be split. – stark Sep 17 '21 at 18:36
  • 1
    *POSIX guarantees that append opened file descriptor writes are atomic, thus no data will be lost.* But it does not guarantee any correspondence between calls to `fwrite()`/`fprintf()`/etc. on the `FILE *` structure and any calls to `write()` on the underlying file descriptor. *As long as your data isn't split across multiple flushes it won't interleave with each other either.* But there is no guaranteed way to control that. You can't disable buffering - without no guaranteed mapping between calls such as `fprintf()` and underlying writes, one `fwrite()` can cause multiple `write()` calls. – Andrew Henle Sep 17 '21 at 21:01
  • If you really want to guarantee non-interleaved data, you have to marshal the entire package you want to write into a single chunk and make one call to `write()`. Of course Linux then turns around and breaks the POSIX guarantee of not tearing writes if your write crosses a page boundary. Something about the maintainer of the code preferring to tear the write so he could interrupt it, and so much for POSIX compliance... See https://stackoverflow.com/questions/10650861/atomicity-of-write2-to-a-local-filesystem for some background – Andrew Henle Sep 17 '21 at 21:03