views:

551

answers:

4

I've run into an issue catching a std::invalid_argument exception that I'm not able to trace. I'm using gcc 4.4.0 (windows), pthreads-win32 2.8.0 the GC2 dll.

Basically, from two threads (main thread and a thread started using pthread_create), I try to create an instance of class A at roughly the same time. The constructor throws an std::invalid_argument, but it is surrounded by try/catch blocks which should catch the exception. However, that does not happen (very rarely, only one of the threads might catch the exception - no rule as to which will do it though)

If I attempt to create the object on only one of the threads, the create works as it should, and the exception is caught. If I create the two objects at different times, the create works as it should and the exception is caught. If I try to create them at the same time, ::terminate() gets called.

Maybe someone has an idea of why this happens (I've excluded headers):

void *run(void *ptr)
{
    Sleep(5000);
    try
    {
        A *a = new A(5);
        a->a = 12;
    }
    catch (std::exception &ex)
    {
        printf("t - %s\n", ex.what());
    }
    return NULL;
}

int main(void) {
    pthread_t t;
    if (pthread_create(&t, NULL, run, NULL) != 0)
    {
        printf("No thread\n");
    }
    else
    {
        Sleep(5000);
        try
        {
            A *a = new A(5);
            a->a = 13;
        } catch (std::exception &ex)
        {
            printf("M - %s\n", ex.what());
        }
        pthread_join(t, NULL);
    }
    return 0;
}

class A
{
public:
    A(int a);
    virtual ~A();
    int a;
};
A::A(int a)
{
    throw std::invalid_argument("Invalid!");
}
A::~A(){}

The makefile is:

CXXFLAGS = -O0 -g -Wall -Werror -fmessage-length=0
OBJS =  WOpenTest.o A.o
INCL = -I../pthreads-win32/include 
LIBS =   -lws2_32 -lgdi32 -lpthreadGC2 
LIB_DIRS =  -L ../pthreads-win32/lib 
TARGET = WOpenTest.exe
$(TARGET): $(OBJS)
 $(CXX) -o $(TARGET) $(OBJS) $(LIBS) $(LIB_DIRS) $(INCL)
WOpenTest.o : WOpenTest.cpp
 g++ $(CXXFLAGS) -c WOpenTest.cpp $(INCL)  
A.o : A.cpp A.h
 g++ $(CXXFLAGS) -c A.cpp $(INCL)
all: $(TARGET)
clean:
 rm -f $(OBJS) $(TARGET)

The output I am seeing is:

(Most frequently) $ ./WOpenTest.exe

This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.

This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. terminate called after throwing an instance of 'std::invalid_argument' terminate called recursively

or

$ ./WOpenTest.exe

This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. M - Invalid!

or

$ ./WOpenTest.exe

This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. t - Invalid!

or

This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.

This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. terminate called after throwing an instance of 'std::invalid_argument'
what(): Invalid!

Any ideas on what I should be doing and I'm not? Or something I'm missing with pthreads?

+1  A: 

Try linking with pthreadGCE2 rather than pthreadGC2.

Pavel Minaev
I tried doing that (forgot to mention it), it did nothing. As I understand, the difference between the two versions is how they handle thread exit/cancellation - I don't know if it would make a difference for this test case
laura
+1  A: 

I can't see anything wrong.

Try adding a catch(...) to see if there is something else strange happening.
If an exception escapes the thread (ie run()) then pthreads will terminate the application. But I can not see where that is happening.

Just as a side note:
try catching exceptions by const reference.

Martin York
It escapes catch(...) as well, as there is a problem with mingw - throwing exceptions is not thread safe. The solution in this case seems to be writing my own thread safe exceptions.
laura
+2  A: 

You have concluded that this is connected with the single-threaded libraries that MinGW links to. I found a page about configuring NetBeans to use MinGW, and it has the following advice under the "Multithreading with Posix threads" section:

  • Important if using exception handling: Compile your application with the additional compiler switch "-mthreads". Otherwise exception handling will not work reliable and unspecific crashes may occur. Other problems may arise while propagating exceptions out of a DLL into an application. I have found some hints concerning this in the net, but have no experience with it because my DLL does not throw exceptions.

It's also described in the GCC documentation for x86 options:

-mthreads

Support thread-safe exception handling on 'Mingw32'. Code that relies on thread-safe exception handling must compile and link all code with the -mthreads option. When compiling, -mthreads defines -D_MT; when linking, it links in a special thread helper library -lmingwthrd which cleans up per thread exception handling data.

Please try that before resorting to writing your own thread-safe exceptions.

Rob Kennedy
Thanks for the answer. I've compiled and linked with -mthreads: the runtime error still appears, although not as often as before. However, "not as often" isn't good enough, so for now I'll have to go ahead with the thread-safe exceptions. I guess I am missing something, but after looking at all the gcc options I can't determine what that is
laura
+1  A: 

Posting final answer here in case someone looks for it in the future. The issue is a critical bug in gcc 4.4:

http://n2.nabble.com/gcc-4-4-multi-threaded-exception-handling-thread-specifier-not-working-td3440749.html

laura