Search code examples
c++multithreadingc++11valgrind

C++11 multithreading: Valgrind uninitialized value(s) warning


I have compiled the following dummy program under Linux using gcc 8.2.1:

#include <iostream>
#include <mutex>
#include <thread>

struct Foo
{
  void start() {
    thread = std::thread(&Foo::run, this);
  }

  void stop() {
    mutex.lock();
    done = true;
    mutex.unlock();

    thread.join();
  }

  void run() {
    bool tmp;

    for (;;) {
      mutex.lock();
      tmp = done;
      mutex.unlock();

      if (tmp)
        break;
    }
  }

  std::thread thread;
  std::mutex mutex;
  bool done;
};

int main()
{
  Foo foo;

  std::cout << "starting...\n";
  foo.start();

  std::cout << "stopping...\n";
  foo.stop();

  std::cout << "done\n";
}

If I subsequently run it under valgrind 3.14.0, I receive the following warning:

==30060== Thread 2:
==30060== Conditional jump or move depends on uninitialised value(s)
==30060==    at 0x1095F3: Foo::run() (in /.../a.out)
==30060==    by 0x109AAE: void std::__invoke_impl<void, void (Foo::*)(), Foo*>(std::__invoke_memfun_deref, void (Foo::*&&)(), Foo*&&) (in /.../a.out)
==30060==    by 0x109771: std::__invoke_result<void (Foo::*)(), Foo*>::type std::__invoke<void (Foo::*)(), Foo*>(void (Foo::*&&)(), Foo*&&) (in /.../a.out)
==30060==    by 0x10A012: decltype (__invoke((_S_declval<0ul>)(), (_S_declval<1ul>)())) std::thread::_Invoker<std::tuple<void (Foo::*)(), Foo*> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) (in /.../a.out)
==30060==    by 0x109FB8: std::thread::_Invoker<std::tuple<void (Foo::*)(), Foo*> >::operator()() (in /.../a.out)
==30060==    by 0x109F8D: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (Foo::*)(), Foo*> > >::_M_run() (in /.../a.out)
==30060==    by 0x496A062: execute_native_thread_routine (thread.cc:80)
==30060==    by 0x4894A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==30060==    by 0x4CD7A42: clone (in /usr/lib/libc-2.28.so)

I am not completely sure what is causing this, I have written this snippet in hopes of diagnosing a bug in a more complicated class (that I cannot post here) I am currently working on and which produces exceptions when calling the equivalent of Foo::stop(). Does the valgrind warning imply some serious misunderstanding of the C++ threading interface on my part? And assuming for a moment that Foo::run would actually do something useful, how could I fix this program while keeping Foo's interface the way it is?


Solution

  • What is the initial value of bool done; ? It is indeterminate (some garbage value), so your thread (run) can be stopped without calling stop method.

    done must be initialized:

      //...
      std::mutex mutex;
      bool done = false; // <--