3
votes

I know that stricly speaking in C++98 nothing is thread-safe, because in the standard there are no threads prior to C++11. However, in practice threads have been used in C++ long before C++11.

So lets say two pthreads concurrently call this:

void printSomething(){
    std::cout << "something\n";
}

What can cause the two outputs to be interleaved? Or will I get in practice always two lines that contain "something"?

I ask this because this question made me wonder and I found this answer stating:

... in C++11, std::cout is thread-safe.

but this answer presents an example along the line of

std::cout << "aaaaaaaaaa" << "bbbbbbbbbb";

and draws the conclusion that in C++98, two thread executing this might have interleaving output, but I couldnt find anything about two threads calling the operator<< only once.

1
so what's the actual question? is std::cout thread safe in C++98? no. it wasn't written at the time to be thread safe. C++98 knows nothing about threads. it's not "thread safe", it's simply non existent for the standard presective.David Haim
C++98 has no concept/notion of threads. So whatever you do in that world (as far as threads go) is inherently 'implementation defined' (or worse). So the answer to your question has to be "no".Jesper Juhl
To be explicit: Choose an implementation you are asking about. This is outside the scope of C++98 itself.Baum mit Augen
@BaummitAugen you are right, but acutally it was just a general question with no particular use case in mind. Tbh "It is implementation defined" is already a satisfactory answer for me. I knew there is no guarantee, but I was missing the correct term.463035818_is_not_a_number
What you can do is put a clarifier like "Is writing a single string to cout thread safe in C++98 in typical implementations". Of course, this will just result in language lawyers telling you that there is no such thing as a typical implementation in the standard, etc, etc - but at least you might suss out a few answers from the more practical and less legally inclined posters.BeeOnRope

1 Answers

6
votes

As has been pointed out in the comments, C++98 doesn't offer any notion of threads in the standard, so you can't answer this question by reference to the standard.

However any reasonable C++98 implementation intended to be used with threads (i.e., most of them outside of some embedded markets) would almost certainly offer at least a more-or-less safe std::cout for "plain" use cases. After all, using that stream for output is very common, and using it across threads in a threaded program will also be very common.

Unfortunately, that's about as much as you can say with specificity. Note, in particular, that I'm not even defining "safe" in a particular way. At least you probably expect that it won't crash, corrupt your program or produce output "out of thin air". Beyond that, it depends.

What Does it Depend On

The next step is to check the implementation itself. Hopefully you have the source1!

Typically you'll find that the implementation has some thread-agnostic code (e.g,. copying into stack-local buffers) and at some point locks and then manipulates shared state inside the std::cout object. When and where it locks is important.

Single String

For example, one would hope that the output of a single item on two threads, like std::cout << "AAAA" on thread 1 and std::cout << "BBBB" on thread 2 would at least result in "not-interleaved" output of AAAABBBB or BBBBAAAA but never BBAAAABB or something like that. This may not be true for implementations that buffer in certain ways! For example, the implementation may lock, then copy as much into an internal buffer as possible and if full, output, and then unlock and proceed again.

Keep in mind that even the std::cout object is totally locked (i.e., essentially the entire operator<<(const char *) is run under a lock), you might see interleaving of even single string output when it is run concurrently with other code that is writing to stdout but through a mechanism other than std::cout - such as printf().

In this case the C/C++ runtimes generally won't share any lock, and you'll be relying on the locking of the underlying write()-type call offered by the OS. Although this call is fully locked and thread-safe, it might not write the entire string in one shot (this is common, for example, when an intermediate buffer fills up such as when writing to a pipe).

Multiple Operators

The main problem with the stream interface is multiple operators. Something like std::cout << "AAA" << "BBB" ... is almost always turned into multiple calls on the shared std::cout object. Without external locking on your part, these are free to be interleaved in any way with other threads. Critically, this pretty much means the stream manipulators in iomanip are impossible to use safely. Something like std::cout << std::hex << 123 << std::dec will modify the stream globally, and some other thread that doesn't want hex output might run at the wrong time and get it anyways. Furthermore, the fact that the stream formatting state and flags can change in the middle of an operation may produce some weird, wonderful or downright terrible results.


1 Certainly the source is available for open source runtimes like libstc++ (used by gcc) and libc++ (used by default by LLVM on most non-Linux platforms). Microsoft appears to be providing the source for their C runtime as well.