#include <iostream>
#include <sstream>
#include <thread>
using namespace std;
int main()
{
auto runner = []() {
ostringstream oss;
for (int i=0; i<100000; ++i)
oss << i;
};
thread t1(runner), t2(runner);
t1.join(); t2.join();
}
Compile the above code in g++6.2.1, then run it with valgrind --tool=helgrind ./a.out
. Helgrind would complain:
==5541== ----------------------------------------------------------------
==5541==
==5541== Possible data race during read of size 1 at 0x51C30B9 by thread #3
==5541== Locks held: none
==5541== at 0x4F500CB: widen (locale_facets.h:875)
==5541== by 0x4F500CB: widen (basic_ios.h:450)
==5541== by 0x4F500CB: fill (basic_ios.h:374)
==5541== by 0x4F500CB: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:73)
==5541== by 0x400CD0: main::{lambda()#1}::operator()() const (43.cpp:12)
==5541== by 0x4011F7: void std::_Bind_simple<main::{lambda()#1} ()>::_M_invoke<>(std::_Index_tuple<>) (functional:1391)
==5541== by 0x401194: std::_Bind_simple<main::{lambda()#1} ()>::operator()() (functional:1380)
==5541== by 0x401173: std::thread::_State_impl<std::_Bind_simple<main::{lambda()#1} ()> >::_M_run() (thread:197)
==5541== by 0x4EF858E: execute_native_thread_routine (thread.cc:83)
==5541== by 0x4C31A04: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5541== by 0x56E7453: start_thread (in /usr/lib/libpthread-2.24.so)
==5541== by 0x59E57DE: clone (in /usr/lib/libc-2.24.so)
==5541==
==5541== This conflicts with a previous write of size 8 by thread #2
==5541== Locks held: none
==5541== at 0x4EF3B1F: do_widen (locale_facets.h:1107)
==5541== by 0x4EF3B1F: std::ctype<char>::_M_widen_init() const (ctype.cc:94)
==5541== by 0x4F501B7: widen (locale_facets.h:876)
==5541== by 0x4F501B7: widen (basic_ios.h:450)
==5541== by 0x4F501B7: fill (basic_ios.h:374)
==5541== by 0x4F501B7: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:73)
==5541== by 0x400CD0: main::{lambda()#1}::operator()() const (43.cpp:12)
==5541== by 0x4011F7: void std::_Bind_simple<main::{lambda()#1} ()>::_M_invoke<>(std::_Index_tuple<>) (functional:1391)
==5541== by 0x401194: std::_Bind_simple<main::{lambda()#1} ()>::operator()() (functional:1380)
==5541== by 0x401173: std::thread::_State_impl<std::_Bind_simple<main::{lambda()#1} ()> >::_M_run() (thread:197)
==5541== by 0x4EF858E: execute_native_thread_routine (thread.cc:83)
==5541== by 0x4C31A04: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5541== Address 0x51c30b9 is 89 bytes inside data symbol "_ZN12_GLOBAL__N_17ctype_cE"
It seems that both threads called locale_facet.h:widen
which caused data race since there appears no synchronization in this function, even though operator <<
is called on two different ostringstream
object. So I was wondering whether this is really a data race or just a false positive of helgrind
.
A function that takes and returns a stream object. It generally is a manipulator function. The standard manipulators which have an effect when used on standard ostream objects are: manipulator.
ostream class − The ostream class handles the output stream in c++ programming language. These output stream objects are used to write data as a sequence of characters on the screen. cout and puts handle the out streams in c++ programming language.
ostream is a general purpose output stream. cout and cerr are both examples of ostreams. ifstream is an input file stream. It is a special kind of an istream that reads in data from a data file.
Overloadable operators The true and false operators must be overloaded together. Must be overloaded in pairs as follows: == and != , < and > , <= and >= .
Update: I admit I didn't fully read the question before answering, so I took it upon myself to research this.
The TL;DR
There are mutable member variables here that could cause race condition. ios has static variables per locale, and these static variables lazy load (initialized when first needed) lookup tables. If you want to avoid concurrency problems, just be sure to initialize the locale prior to joining the threads so that any thread operation is read only to these lookup tables.
The Details
When a stream is initialized it populates a pointer which loads in the correct ctype for the locale (see _M_ctype): https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/basic_ios.h#L273
The error is referring to: https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/locale_facets.h#L875
This could be a race condition if two thread are simultaneously initializing the same locale.
This should be thread-safe (though it may still give error):
// Force ctype to be initialized in the base thread before forking
ostringstream dump;
dump << 1;
auto runner = []() {
ostringstream oss;
for (int i=0; i<100000; ++i)
oss << i;
};
thread t1(runner), t2(runner);
t1.join(); t2.join();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With