Quantcast
Viewing latest article 6
Browse Latest Browse All 6

Valgrind/Helgrind and STL string

I am trying to track down a thread-safety problem in one of my programs. Valgrind when run as “valgrind –tool=helgrind ./thread-test” claims that there is a problem with the following program (the Valgrind errors are at the end of the post). The SGI documents state [1]: “The SGI implementation of STL is thread-safe only in the sense that simultaneous accesses to distinct containers are safe, and simultaneous read accesses to to shared containers are safe. If multiple threads access a single container, and at least one thread may potentially write, then the user is responsible for ensuring mutual exclusion between the threads during the container accesses. “.

My interpretation of the SGI document is that different STL strings can be manipulated independently. When I have two threads running I have two stacks, so strings that are allocated on the stack will be “distinct containers“. So this looks like a bug in the STL or a bug in Valgrind. I am hesitant to file a bug report because I remember the advice that a wise programmer once gave me: “the people who develop compilers and tool chains are much better at coding than you, any time you think there’s a bug in their code there is probably a bug in yours“. I know that my blog is read by some really great programmers. I look forward to someone explaining what is wrong with my code or confirming that I have found a bug in Valgrind or the STL.

#define NUM_THREADS 2
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <string>

using namespace std;

void whatever()
{
string str;
str.erase();
}

struct thread_data
{
int thread_id;
};

void *do_work(void *data)
{
struct thread_data *td = (struct thread_data *)data;
printf("%d:stack:%X\n", td->thread_id, &td);
while(1)
  whatever();
}

int main(int argc, char **argv)
{
pthread_t *thread_info = (pthread_t *)calloc(NUM_THREADS, sizeof(thread_info));
pthread_attr_t attr;
if(pthread_attr_init(&attr) || pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) || pthread_attr_setstacksize(&attr, 32*1024))
  fprintf(stderr, "Can't set thread attributes.\n");

int t;
struct thread_data td[NUM_THREADS];
for(t = 0; t < NUM_THREADS; t++)
{
  printf("created thread %d\n", t);
  td[t].thread_id = t;
  int p = pthread_create(&thread_info[t], &attr, do_work, (void *)&td[t]);
  if(p)
  {
  fprintf(stderr, "Can't create thread %d\n", t);
  exit(1);
  }
}
pthread_attr_destroy(&attr);
void *value_ptr;
for(t = 0; t < NUM_THREADS; t++)
  pthread_join(thread_info[t], &value_ptr);
free(thread_info);

return 0;
}

==30622== Possible data race during write of size 4 at 0x414FED0
==30622==    at 0x40FAD9A: std::string::_Rep::_M_set_sharable() (basic_string.h:201)
==30622==    by 0x40A945E: _ZNSs4_Rep26_M_set_length_and_sharableEj@@GLIBCXX_3.4.5 (basic_string.h:206)
==30622==    by 0x40FD6B6: std::string::_M_mutate(unsigned, unsigned, unsigned) (basic_string.tcc:471)
==30622==    by 0x40FDBEE: std::string::erase(unsigned, unsigned) (basic_string.h:1133)
==30622==    by 0x8048AA8: whatever() (thread-test.cpp:16)
==30622==    by 0x8048B0A: do_work(void*) (thread-test.cpp:29)
==30622==    by 0x402641B: mythread_wrapper (hg_intercepts.c:193)
==30622==    by 0x40464BF: start_thread (in /lib/i686/cmov/libpthread-2.7.so)
==30622==    by 0x42696DD: clone (in /lib/i686/cmov/libc-2.7.so)
==30622==  Old state: shared-readonly by threads #2, #3
==30622==  New state: shared-modified by threads #2, #3
==30622==  Reason:    this thread, #3, holds no consistent locks
==30622==  Location 0x414FED0 has never been protected by any lock
==30622==
==30622== Possible data race during write of size 4 at 0x414FEC8
==30622==    at 0x40A9465: _ZNSs4_Rep26_M_set_length_and_sharableEj@@GLIBCXX_3.4.5 (basic_string.h:207)
==30622==    by 0x40FD6B6: std::string::_M_mutate(unsigned, unsigned, unsigned) (basic_string.tcc:471)
==30622==    by 0x40FDBEE: std::string::erase(unsigned, unsigned) (basic_string.h:1133)
==30622==    by 0x8048AA8: whatever() (thread-test.cpp:16)
==30622==    by 0x8048B0A: do_work(void*) (thread-test.cpp:29)
==30622==    by 0x402641B: mythread_wrapper (hg_intercepts.c:193)
==30622==    by 0x40464BF: start_thread (in /lib/i686/cmov/libpthread-2.7.so)
==30622==    by 0x42696DD: clone (in /lib/i686/cmov/libc-2.7.so)
==30622==  Old state: shared-readonly by threads #2, #3
==30622==  New state: shared-modified by threads #2, #3
==30622==  Reason:    this thread, #3, holds no consistent locks
==30622==  Location 0x414FEC8 has never been protected by any lock
==30622==
==30622== Possible data race during write of size 1 at 0x414FED4
==30622==    at 0x40A9012: std::char_traits<char>::assign(char&, char const&) (char_traits.h:246)
==30622==    by 0x40A9488: _ZNSs4_Rep26_M_set_length_and_sharableEj@@GLIBCXX_3.4.5 (basic_string.h:208)
==30622==    by 0x40FD6B6: std::string::_M_mutate(unsigned, unsigned, unsigned) (basic_string.tcc:471)
==30622==    by 0x40FDBEE: std::string::erase(unsigned, unsigned) (basic_string.h:1133)
==30622==    by 0x8048AA8: whatever() (thread-test.cpp:16)
==30622==    by 0x8048B0A: do_work(void*) (thread-test.cpp:29)
==30622==    by 0x402641B: mythread_wrapper (hg_intercepts.c:193)
==30622==    by 0x40464BF: start_thread (in /lib/i686/cmov/libpthread-2.7.so)
==30622==    by 0x42696DD: clone (in /lib/i686/cmov/libc-2.7.so)
==30622==  Old state: owned exclusively by thread #2
==30622==  New state: shared-modified by threads #2, #3
==30622==  Reason:    this thread, #3, holds no locks at all


Viewing latest article 6
Browse Latest Browse All 6

Trending Articles