stdout thread-safe in C on Linux?
Asked Answered
S

5

50

Is writing to stdout using printf thread-safe on Linux? What about using the lower-level write command?

Solatium answered 22/1, 2009 at 3:16 Comment(0)
V
64

It's not specified by the C standard -- it depends on your implementation of the C standard library. In fact, the C standard doesn't even mention threads at all, since certain systems (e.g. embedded systems) don't have multithreading.

In the GNU implementation (glibc), most of the higher-level functions in stdio that deal with FILE* objects are thread-safe. The ones that aren't usually have unlocked in their names (e.g. getc_unlocked(3)). However, the thread safety is at a per-function call level: if you make multiple calls to printf(3), for example, each of those calls is guaranteed to output atomically, but other threads might print things out between your calls to printf(). If you want to ensure that a sequence of I/O calls gets output atomically, you can surround them with a pair of flockfile(3)/funlockfile(3) calls to lock the FILE handle. Note that these functions are reentrant, so you can safely call printf() in between them, and that won't result in deadlock even thought printf() itself makes a call to flockfile().

The low-level I/O calls such as write(2) should be thread-safe, but I'm not 100% sure of that - write() makes a system call into the kernel to perform I/O. How exactly this happens depends on what kernel you're using. It might be the sysenter instruction, or the int (interrupt) instruction on older systems. Once inside the kernel, it's up to the kernel to make sure that the I/O is thread-safe. In a test I just did with the Darwin Kernel Version 8.11.1, write(2) appears to be thread-safe.

Varhol answered 22/1, 2009 at 5:7 Comment(4)
This answer is ignoring that the question was tagged unix/linux. POSIX requires stdio to be thread-safe, which is quite unfortunate since it kills performance and since there's no practical way to operate on the same FILE from multiple threads (data will come out hopelessly interleaved; atomicity is only at the character level).Generation
Sometimes it's quite Ok if output is interleaved , e.g. during logging via printf from multiple threads.Durazzo
@couling I think he means that the thread-safety is useless because everything gets interleaved anyway -- unless you use an explicit f[un]lockfile anyway.Berserk
i am using amd64 linux with up to date kernel .. when i am using setvbuf to _IONBF for stdout and then calling clone() with CLONE_VM ... causes printf to make a segmantation fault from the children ... even when allocate each there own buffer manually ... it still happens ... still trying to track down the memory overlapping that is happening ... happens with or without CLONE_IO and or CLONE_IO flags ...Chelseychelsie
G
26

Whether you'd call it "thread-safe" depends on your definition of thread-safe. POSIX requires stdio functions to use locking, so your program will not crash, corrupt the FILE object states, etc. if you use printf simultaneously from multiple threads. However, all stdio operations are formally specified in terms of repeated calls to fgetc and fputc, so there is no larger-scale atomicity guaranteed. That is to say, if threads 1 and 2 try to print "Hello\n" and "Goodbye\n" at the same time, there's no guarantee that the output will be either "Hello\nGoodbye\n" or "Goodbye\nHello\n". It could just as well be "HGelolodboy\ne\n". In practice, most implementations will acquire a single lock for the entire higher-level write call simply because it's more efficient, but your program should not assume so. There may be corner cases where this is not done; for instance an implementation could probably entirely omit locking on unbuffered streams.

Edit: The above text about atomicity is incorrect. POSIX guarantees all stdio operations are atomic, but the guarantee is hidden in the documentation for flockfile: http://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html

All functions that reference ( FILE *) objects shall behave as if they use flockfile() and funlockfile() internally to obtain ownership of these ( FILE *) objects.

You can use the flockfile, ftrylockfile, and funlockfile functions yourself to achieve larger-than-single-function-call atomic writes.

Generation answered 26/7, 2010 at 13:54 Comment(0)
G
17

They are both thread-safe to the point that your application won't crash if multiple threads call them on the same file descriptor. However, without some application-level locking, whatever is written could be interleaved.

Germanism answered 22/1, 2009 at 3:20 Comment(0)
C
9

C got a new standard since this question was asked (and last answered).

C11 now comes with multithreading support and addresses multithreaded behavior of streams:

§7.21.2 Streams

¶7 Each stream has an associated lock that is used to prevent data races when multiple threads of execution access a stream, and to restrict the interleaving of stream operations performed by multiple threads. Only one thread may hold this lock at a time. The lock is reentrant: a single thread may hold the lock multiple times at a given time.

¶8 All functions that read, write, position, or query the position of a stream lock the stream before accessing it. They release the lock associated with the stream when the access is complete.

So, an implementation with C11 threads must guarantee that using printf is thread-safe.

Whether atomicity (as in no interleaving1) is guaranteed, wasn't that clear to me at a first glance, because the standard spoke of restricting interleaving, as opposed to preventing, which it mandated for data races.

I lean towards it being guaranteed. The standard speaks of restricting interleaving, as some interleaving that doesn't change the outcome is still allowed to happen; e.g. fwrite some bytes, fseek back some more and fwrite till the original offset, so that both fwrites are back-to-back. The implementation is free to reorder these 2 fwrites and merge them into a single write.


1: See the strike-through text in R..'s answer for an example.

Curia answered 13/12, 2016 at 21:56 Comment(0)
J
6

It's thread-safe; printf should be reentrant, and you won't cause any strangeness or corruption in your program.

You can't guarantee that your output from one thread won't start half way through the output from another thread. If you care about that you need to develop your own locked output code to prevent multiple access.

Jae answered 22/1, 2009 at 3:19 Comment(3)
All calls to printf are likely to use the same buffer to build the string. Many implementations also share a buffer between scanf and printf - which can cause some weird debugging dependant bugs.Avantgarde
I don't know what others do, but the GNU C library is thread-safe by default, so no it won't use the same buffer.Jae
I don't think printf is reentrant, see #3941771Michaud

© 2022 - 2024 — McMap. All rights reserved.