The POSIX standard has features like putc_unlocked() , where the comment says:
The versions of the getc() , getchar() , putc() and putchar() , respectively, are called getc_unlocked() , getchar_unlocked() , putc_unlocked() and putchar_unlocked() , which are functionally equivalent to the original versions, except that they are not must be implemented in a thread-safe manner. They can only be used safely in the area protected by flockfile() (or ftrylockfile() ) and funlockfile() . These functions can be safely used in a multi-threaded program if and only if they are called when the calling thread owns an object ( FILE * ), as is the case after a successful call to the flockfile() or ftrylockfile() functions.
This clearly indicates that low-level single-character I / O functions are typically thread safe. However, this also indicates that the level of detail is a single character output operation. The printf() specification says:
The characters generated by fprintf() and printf() print as if fputc() was called.
And for putc() it says:
The putc() function should be equivalent to fputc() , except that if it is implemented as a macro, it can evaluate the stream more than once, so the argument should never be an expression with side effects.
The fputc() page does not say anything about thread safety, so you will have to look elsewhere for this information.
Another section describes threads and says:
All functions defined by this POSIX.1-2008 volume must be thread safe, except that the following functions must not be thread safe.
And the following list includes the functions *_unlocked() .
So, printf() and fputc() should be thread safe, but recording with printf() is done “as if” on fputc() , so the alternation of output between streams can be at the character level, which is more or less consistent with what do you see. If you want to make printf() calls without interlacing, you will need to use flockfile() and funlockfile() to give you ownership of stdout when funlockfile() executed. Similarly for fprintf() . You can easily write the fprintf_locked() function to achieve this result:
int fprintf_locked(FILE *fp, const char *format, ...) { flockfile(fp); va_list args; va_start(args, format); int rc = vfprintf(fp, format, args); va_end(args); funlockfile(fp); return rc; }
You can insert fflush(fp) there if you want. You can also have vfprintf_locked() and have a function above the call that performs lock, format (reset), and unlock operations. I probably coded it somehow, trusting the compiler to embed the code, if that was appropriate and doable. Versioning with stdout also quite simple.
Note the snippet of the POSIX specification for flockfile() pointed out by Michael Burr in the answer:
All functions that reference objects ( FILE * ), except those that have names ending in _unlocked , should behave as if they use flockfile() and funlockfile() internally to obtain ownership of these objects ( FILE * ).
Besides the odd parentheses around FILE * , these lines affect all other standard I / O functions, but you should be aware that these lines exist on one of the less frequently used manual pages. So my fprintf_locked() function is not needed. If you find an aberrant implementation of fprintf() that does not lock the file, the fprintf_locked() function can be used fprintf_locked() , but this should only be done under protest - the library should do it for you anyway.