title: C++ Multithreading: is initialization of a local static lambda thread safe?
body: <p>The C++11 standard says about local static variable initialization that it is supposed to be thread safe (<a href="http://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables" rel="nofollow noreferrer">http://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables</a>). My question concerns what exactly happens when a lambda is initialized as a static local variable?</p>
  
  <p>Let's consider the following code:</p>
  
  <pre><code>#include &lt;iostream&gt;
  #include &lt;functional&gt;
  
  int doSomeWork(int input)
  {
      static auto computeSum = [](int number)                                                                                                                                                                  
      {
        return 5 + number;
      };  
      return computeSum(input);
  }
  
  int main(int argc, char *argv[])
  {
      int result = 0;
  #pragma omp parallel
  {
    int localResult = 0;
  #pragma omp for
    for(size_t i=0;i&lt;5000;i++)
    {
     localResult += doSomeWork(i);
    }
  #pragma omp critical
  {
     result += localResult;
  }
  }
  
  std::cout &lt;&lt; "Result is: " &lt;&lt; result &lt;&lt; std::endl;
  
  return 0;
  }
  </code></pre>
  
  <p>compiled with GCC 5.4, using ThreadSanitizer:</p>
  
  <pre><code>gcc -std=c++11 -fsanitize=thread -fopenmp -g main.cpp -o main -lstdc++
  </code></pre>
  
  <p>Works fine, ThreadSanitizer gives no errors. Now, if I change the line where the lambda "computeSum" is initialized to this:</p>
  
  <pre><code>static std::function&lt;int(int)&gt; computeSum = [](int number)
  {
    return 5 + number;
  };  
  </code></pre>
  
  <p>The code still compiles, but ThreadSanitizer gives me a warning, saying there is a data race:</p>
  
  <pre><code>WARNING: ThreadSanitizer: data race (pid=20887)
    Read of size 8 at 0x000000602830 by thread T3:
      #0 std::_Function_base::_M_empty() const /usr/local/gcc-5.4_nofutex/include/c++/5.4.0/functional:1834 (main+0x0000004019ec)
      #1 std::function&lt;int (int)&gt;::operator()(int) const /usr/local/gcc-5.4_nofutex/include/c++/5.4.0/functional:2265 (main+0x000000401aa3)
      #2 doSomeWork(int) /home/laszlo/test/main.cpp:13 (main+0x000000401242)
      #3 main._omp_fn.0 /home/laszlo/test/main.cpp:25 (main+0x000000401886)
      #4 gomp_thread_start ../../../gcc-5.4.0/libgomp/team.c:118 (libgomp.so.1+0x00000000e615)
  
    Previous write of size 8 at 0x000000602830 by thread T1:
      #0 std::_Function_base::_Function_base() /usr/local/gcc-5.4_nofutex/include/c++/5.4.0/functional:1825 (main+0x000000401947)
      #1 function&lt;doSomeWork(int)::&lt;lambda(int)&gt;, void, void&gt; /usr/local/gcc-5.4_nofutex/include/c++/5.4.0/functional:2248 (main+0x000000401374)
      #2 doSomeWork(int) /home/laszlo/test/main.cpp:12 (main+0x000000401211)
      #3 main._omp_fn.0 /home/laszlo/test/main.cpp:25 (main+0x000000401886)
      #4 gomp_thread_start ../../../gcc-5.4.0/libgomp/team.c:118 (libgomp.so.1+0x00000000e615)
  
    Location is global 'doSomeWork(int)::computeSum' of size 32 at 0x000000602820 (main+0x000000602830)
  
    Thread T3 (tid=20891, running) created by main thread at:
      #0 pthread_create ../../../../gcc-5.4.0/libsanitizer/tsan/tsan_interceptors.cc:895 (libtsan.so.0+0x000000026704)
      #1 gomp_team_start ../../../gcc-5.4.0/libgomp/team.c:796 (libgomp.so.1+0x00000000eb5e)
      #2 __libc_start_main &lt;null&gt; (libc.so.6+0x00000002082f)
  
    Thread T1 (tid=20889, running) created by main thread at:
      #0 pthread_create ../../../../gcc-5.4.0/libsanitizer/tsan/tsan_interceptors.cc:895 (libtsan.so.0+0x000000026704)
      #1 gomp_team_start ../../../gcc-5.4.0/libgomp/team.c:796 (libgomp.so.1+0x00000000eb5e)
      #2 __libc_start_main &lt;null&gt; (libc.so.6+0x00000002082f)
  
  SUMMARY: ThreadSanitizer: data race /usr/local/gcc-5.4_nofutex/include/c++/5.4.0/functional:1834 std::_Function_base::_M_empty() const
  </code></pre>
  
  <p>In any case, the code where ThreadSanitizer reports a data race needs to be executed 5-10 times until the warning mesage appears.</p>
  
  <p>So my question is: is there a conceptional difference between</p>
  
  <pre><code>static auto computeSum = [](int number){ reentrant code returing int };
  </code></pre>
  
  <p>and </p>
  
  <pre><code>static std::function&lt;int(int)&gt; computeSum = [](int number) {same code returning int};
  </code></pre>
  
  <p>What makes the first code to work and the second to be a data race?</p>
  
  <p><strong>Edit #1</strong>:
  It seems that there was (is) quite a discussion going on about my question. I found Sebastian Redl's contribution the most helpful, thus I accepted that answer. 
  I just want to summarize, so that people can refer to this. (Please le me know if this is not appropriate on Stack Overflow, I do not really ask stuff here...) </p>
  
  <p><strong>Why is a data race reported?</strong></p>
  
  <p>It was suggested in a comment (by MikeMB) that the problem is related to a bug in the gcc implementation of TSAN (see <a href="https://stackoverflow.com/questions/27464190/gccs-tsan-reports-a-data-race-with-a-thread-safe-static-local">this</a> and <a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68338]" rel="nofollow noreferrer">this</a> link). It seems to be correct:</p>
  
  <p>If I compile the code that contains:</p>
  
  <pre><code>static std::function&lt;int(int)&gt; computeSum = [](int number){ ... return int;};
  </code></pre>
  
  <p>with GCC 5.4, the machine code looks like:</p>
  
  <pre><code>  static std::function&lt;int(int)&gt; computeSum = [](int number)
    {
      return 5 + number;
    };
    4011d5:       bb 08 28 60 00          mov    $0x602808,%ebx
    4011da:       48 89 df                mov    %rbx,%rdi
    4011dd:       e8 de fd ff ff          callq  400fc0 &lt;__tsan_read1@plt&gt;
    ....
  </code></pre>
  
  <p>whereas, with GCC 6.3, it reads:</p>
  
  <pre><code>  static std::function&lt;int(int)&gt; computeSum = [](int number)                                                                                                                                             
    {
      return 5 + number;
    };
    4011e3:   be 02 00 00 00          mov    $0x2,%esi
    4011e8:   bf 60 28 60 00          mov    $0x602860,%edi
    4011ed:   e8 9e fd ff ff          callq  400f90 &lt;__tsan_atomic8_load@plt&gt;
  </code></pre>
  
  <p>I am not a big master of machine code, but it looks like that in the GCC 5.4 version, <code>__tsan_read1@plt</code> is used to check whether the static variable is initialized. In comparison, GCC 6.3 generates <code>__tsan_atomic8_load@plt</code> . I guess the second one is correct, the first one leads to a false positive.</p>
  
  <p>If I compile the version without ThreadSanitizer, GCC 5.4 generates:</p>
  
  <pre><code>static std::function&lt;int(int)&gt; computeSum = [](int number)
  {                                                                                                                                                                                                        
    return 5 + number;
  };
  400e17:     b8 88 24 60 00          mov    $0x602488,%eax
  400e1c:     0f b6 00                movzbl (%rax),%eax
  400e1f:     84 c0                   test   %al,%al
  400e21:     75 4a                   jne    400e6d &lt;doSomeWork(int)+0x64&gt;
  400e23:     bf 88 24 60 00          mov    $0x602488,%edi
  400e28:     e8 83 fe ff ff          callq  400cb0 &lt;__cxa_guard_acquire@plt&gt;
  </code></pre>
  
  <p>And GCC 6.3:</p>
  
  <pre><code>  static std::function&lt;int(int)&gt; computeSum = [](int number)
    {                                                                                                                                                                                                      
      return 5 + number;
    };
    400e17:   0f b6 05 a2 16 20 00    movzbl 0x2016a2(%rip),%eax        # 6024c0 &lt;guard variable for doSomeWork(int)::computeSum&gt;
    400e1e:   84 c0                   test   %al,%al
    400e20:   0f 94 c0                sete   %al
    400e23:   84 c0                   test   %al,%al
    400e25:   74 4a                   je     400e71 &lt;doSomeWork(int)+0x68&gt;
    400e27:   bf c0 24 60 00          mov    $0x6024c0,%edi
    400e2c:   e8 7f fe ff ff          callq  400cb0 &lt;__cxa_guard_acquire@plt&gt;
  </code></pre>
  
  <p><strong>Why is no data race, if I use <code>auto</code> instead of <code>std::function</code></strong>?</p>
  
  <p>You might have to correct me here, but probably the compiler "inlines" the auto object, so there is no need to do bookkeeping on whether the static object has been initialized or not.</p>
  
  <pre><code>static auto computeSum = [](int number){ ... return int;};
  </code></pre>
  
  <p>produces:</p>
  
  <pre><code>  static auto computeSum = [](int number)
    400e76:   55                      push   %rbp                                                                                                                                                          
    400e77:   48 89 e5                mov    %rsp,%rbp
    400e7a:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
    400e7e:   89 75 f4                mov    %esi,-0xc(%rbp)
    //static std::function&lt;int(int)&gt; computeSum = [](int number)
    {
      return 5 + number;
    };
    400e81:   8b 45 f4                mov    -0xc(%rbp),%eax
    400e84:   83 c0 05                add    $0x5,%eax
    400e87:   5d                      pop    %rbp
    400e88:   c3                      retq
  </code></pre>