from http://etbe.coker.com.au/2009/06/22/valgrindhelgrind-and-stl-string
simplified test case: #include <pthread.h> #include <string> void *do_work(void *) { std::string s; s.erase(); return 0; } int main() { pthread_t tid[2]; pthread_create(&tid[0], NULL, do_work, NULL); pthread_create(&tid[1], NULL, do_work, NULL); pthread_join(tid[0], NULL); pthread_join(tid[1], NULL); return 0; } Helgrind shows data races, because calling erase() on an empty string calls _M_set_length_and_sharable() on the shared empty _Rep object, which assigns zero to the refcount, length, and refdata[0]. Those locations contain zero already, so as long as the writes are atomic then no values are modified, but it is a race. $ valgrind --tool=helgrind ./a.out ==17699== Helgrind, a thread error detector. ==17699== Copyright (C) 2007-2008, and GNU GPL'd, by OpenWorks LLP et al. ==17699== Using LibVEX rev 1878, a library for dynamic binary translation. ==17699== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP. ==17699== Using valgrind-3.4.0, a dynamic binary instrumentation framework. ==17699== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al. ==17699== For more details, rerun with: -v ==17699== ==17699== Thread #3 was created ==17699== at 0x8EDC38: clone (in /lib/libc-2.5.so) ==17699== by 0x4AA960F: pthread_cre...@* (hg_intercepts.c:214) ==17699== by 0x804867D: main (etbe2.cc:16) ==17699== ==17699== Thread #2 was created ==17699== at 0x8EDC38: clone (in /lib/libc-2.5.so) ==17699== by 0x4AA960F: pthread_cre...@* (hg_intercepts.c:214) ==17699== by 0x8048657: main (etbe2.cc:15) ==17699== ==17699== Possible data race during read of size 4 at 0x607d688 by thread #3 ==17699== at 0x602C162: std::string::erase(unsigned int, unsigned int) (basic_string.h:607) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== This conflicts with a previous write of size 4 by thread #2 ==17699== at 0x602B99F: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (basic_string.h:207) ==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int) (basic_string.h:1133) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== ==17699== Possible data race during read of size 4 at 0x607d688 by thread #3 ==17699== at 0x602B915: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (basic_string.h:607) ==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int) (basic_string.h:1133) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== This conflicts with a previous write of size 4 by thread #2 ==17699== at 0x602B99F: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (basic_string.h:207) ==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int) (basic_string.h:1133) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== ==17699== Possible data race during read of size 4 at 0x607d690 by thread #3 ==17699== at 0x602B9B8: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (basic_string.tcc:450) ==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int) (basic_string.h:1133) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== This conflicts with a previous write of size 4 by thread #2 ==17699== at 0x602B998: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (basic_string.h:201) ==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int) (basic_string.h:1133) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== ==17699== Possible data race during write of size 4 at 0x607d690 by thread #3 ==17699== at 0x602B998: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (basic_string.h:201) ==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int) (basic_string.h:1133) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== This conflicts with a previous write of size 4 by thread #2 ==17699== at 0x602B998: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (basic_string.h:201) ==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int) (basic_string.h:1133) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== ==17699== Possible data race during write of size 4 at 0x607d688 by thread #3 ==17699== at 0x602B99F: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (basic_string.h:207) ==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int) (basic_string.h:1133) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== This conflicts with a previous write of size 4 by thread #2 ==17699== at 0x602B99F: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (basic_string.h:207) ==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int) (basic_string.h:1133) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== ==17699== Possible data race during write of size 1 at 0x607d694 by thread #3 ==17699== at 0x602B9A1: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (char_traits.h:246) ==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int) (basic_string.h:1133) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== This conflicts with a previous write of size 1 by thread #2 ==17699== at 0x602B9A1: std::string::_M_mutate(unsigned int, unsigned int, unsigned int) (char_traits.h:246) ==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int) (basic_string.h:1133) ==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8) ==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194) ==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so) ==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so) ==17699== ==17699== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 8 from 2) The storage is written to, even if not modified, so this comment may not hold true: static _Rep& _S_empty_rep() { // NB: Mild hack to avoid strict-aliasing warnings. Note that // _S_empty_rep_storage is never modified and the punning should // be reasonably safe in this case. void* __p = reinterpret_cast<void*>(&_S_empty_rep_storage); return *reinterpret_cast<_Rep*>(__p); } As expected, the helgrind errors are not present with --enable-fully-dynamic-string -- Summary: data races when calling std::string::erase() on empty string Product: gcc Version: 4.3.2 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ AssignedTo: unassigned at gcc dot gnu dot org ReportedBy: jwakely dot gcc at gmail dot com http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40518