Menu

#527 thread_local stl object's destructor causing crash

v1.0 (example)
open
nobody
None
9
2017-12-23
2016-01-24
Palash Das
No

Whenever a thread_local object is accessed from different threads one instance of this object will be created for each threads. As soon as thread exits the object must be destructed. UNFORTUNATELY THIS CALL TO THE DESTRUCTOR FOR COMMON STL CONTAINERS IS CRASHING THE PROGRAM!

I have tried different versions of mingw-w64 ( ver : (i686-posix-dwarf-rev1, Built by MinGW-W64 project) 4.9.2) as well as TDM GCC 4.9.2 but still its crashing.

Below is the program to reproduce the problem.

' #include <thread></thread>

include <string></string>

using namespace std;

thread_local string s("Hello");

int main()
{
thread([]
{
s;
}).join();
return 0;
}

'
which works fine in ideone,
http://ideone.com/H8aGwJ

Discussion

  • John Maddock

    John Maddock - 2017-06-11

    This is effecting me as well, in fact it is impossible to declare a variable with a destructor as thread_local without a getting a segfault. Tested with x86_64-6.3.0-win32-seh-rt_v5-rev2 and x86_64-5.4.0-win32-seh-rt_v5-rev0. A simplified test case is:

    #include <iostream>
    #include <vector>
    
    
    int main() 
    {
       static const thread_local std::vector<int> two(2, 3);
       std::cout << two.size() << std::endl;
       return 0;
    }
    

    Which will segfault on program exit. There is a workaround, which is to link to the static runtime libraries, so this looks like an order-of-destruction issue.

    I ran into this while tracking down an issue in the Boost.Multiprecision library, and this is a bit of a showstopper for us, as it prevents me from making our library thread safe under mingw.

     
  • sav

    sav - 2017-09-04

    For mingw-w64 x86_64-6.3.0-posix-seh-rt_v5-rev2 both of test cases above were built and run successfully (i.e. %ERRORLEVEL% == 0).

    For mingw-w64 x86_64-6.3.0-win32-seh-rt_v5-rev2 first test case throw error during build, second finished with segfault.

     

    Last edit: sav 2017-09-04
  • dejan crnila

    dejan crnila - 2017-11-12

    I'm having the same issue with mingw gcc 7.2 win32 both posix and win threads. In fact any thread_local class with destructor throws segmentation fault on destructor call. value of "this" pointer seems to be corrupt.

     
  • Zufu Liu

    Zufu Liu - 2017-11-13

    Both code compile and run successfully with GCC 7.1.0 and 7.2.0 x86_64-posix-seh from https://sourceforge.net/projects/mingw-w64/files/

    C++ std::thread and related features currently not supported in win32 thread model.

     
    • dejan crnila

      dejan crnila - 2017-11-13

      true, it does compile and run successfully wih x86_64-posix-seh, but i686-posix sjlj or dwarf produces segmentation fault. I have attached my test file

       
  • sav

    sav - 2017-11-13

    C++ std::thread and related features currently not supported in win32 thread model.

    To be more presize, not supported in mingw-w64 win32 thread model implementation. Since both tests above could be successfully built using MSVC 2017 and "ICC 2018 on Windows", and run without errors.

     
  • LIU Hao

    LIU Hao - 2017-12-22

    The original problem is caused by GCC. If emutls is in effect (this is true for mingw-w64), or the thread_local object block scope in question has block scope instead of namespace scope, GCC emits a call to the __cxa_thread_atexit() function after successful construction of it. This function, as its name suggests, is similar to atexit() and takes a pointer to a function using the __cdecl calling convention, where the first argument is pushed onto stack. Unfortunately on x86 destructors use the __thiscall convention, where the implicit this parameter is to be passed via the ECX register. The inconsistency of calling conventions inside the dtor merely results in ECX having whatever garbage values that happen to reside in it. x86_64 is not suffering from this problem because there is only one calling convention - the __fastcall calling convention,

     

    Last edit: LIU Hao 2017-12-22
  • LIU Hao

    LIU Hao - 2017-12-22

    The issue discovered by @johnmaddock is another one.

    Compiling and running the attached program yields the following result:

    E:\Desktop>g++ test.cpp -std=c++11  && a.exe
    the cat with namespace scope and static storage duration is born: 0040b020
    the cat with block scope and static storage duration is born: 0040b021
    the cat with block scope and thread storage duration is born: 007b707c
    the cat with namespace scope and static storage duration meows: 0040b020
    the cat with namespace scope and thread storage duration is born: 007b709c
    the cat with namespace scope and thread storage duration meows: 007b709c
    the cat with block scope and static storage duration meows: 0040b021
    the cat with block scope and thread storage duration meows: 007b707c
    the cat with block scope and static storage duration dies: 0040b021
    the cat with namespace scope and static storage duration dies: 0040b020
    the cat with namespace scope and thread storage duration dies: 00000001
    the cat with block scope and thread storage duration dies: 00000001
    

    Notice the last four lines about how destruction of static and thread_local objects are sequenced. The behavior here violates the standard, as the standard mandates the behavior as follows:

    ISO/IEC:14882 N4713
    6.8.3.4 Termination [basic.start.term]
    2. Destructors for initialized objects with thread storage duration within a given thread are called as a result of returning from the initial function of that thread and as a result of that thread calling std::exit. The completions of the destructors for all initialized objects with thread storage duration within that thread strongly happen before the initiation of the destructors of any object with static storage duration.

     

    Last edit: LIU Hao 2017-12-22
  • LIU Hao

    LIU Hao - 2017-12-23

    I filed a PR to GCC about the original program: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83562

     

Log in to post a comment.

MongoDB Logo MongoDB