3

In the following simple test code, the SIGSEGV signal handler is not called from the secondary thread, although in case if the crash is in main thread, it's called. The handler of SIGABRT signal is called in both cases. Could anyone explain why or what I am doing wrong?

#include <iostream>
#include <Windows.h>
#include <signal.h>

void signal_handler(int sig) 
{
    const char* name = NULL;
    switch( sig )
    {
        case SIGABRT: name = "SIGABRT";  break;
        case SIGSEGV: name = "SIGSEGV";  break;
    }
    std::cout << "Handler: Caught signal: " << name << std::endl;   
}

void install_signal_handlers()
{
    signal(SIGABRT, signal_handler);
    signal(SIGSEGV, signal_handler); 
    std::cout << "Signal handlers are installed" << std::endl;
}

void crash()
{
    //abort();
    //raise(SIGSEGV);
    try
    {
        int* ptr = NULL;
        *ptr = 2;
        std::cout << *ptr << std::endl;
    }
    catch (...) 
    {
        std::cout << "Caught exception: " << std::endl; 
    }   
} 

DWORD WINAPI thread(LPVOID lpParameter)
{
    //install_signal_handlers();    
    crash();

    return 0;
}

int main()
{
    install_signal_handlers(); 
    //crash();

    DWORD myThreadID;
    HANDLE myHandle = CreateThread(0, 0, thread, 0, 0, &myThreadID);

    if (myHandle == NULL) 
    {
        std::cout << "Failed to create thread" << std::endl; 
    }

    WaitForSingleObject(myHandle, INFINITE);
    CloseHandle(myHandle);

    return 0;
}
Fedor
  • 17,146
  • 13
  • 40
  • 131
  • Looks like Windows is not POSIX compliant, is it? – Maxim Egorushkin Apr 30 '15 at 15:40
  • Some possibly unrelated notes: Writing to a null pointer is not guaranteed to cause a crash, it most likely will, but the problem with undefined behavior is that the behavior is just that, undefined, and anything could happen. It also will not cause a C++ exception to be thrown. – Some programmer dude Apr 30 '15 at 15:43
  • As for your question, *what* "secondary" thread? And in POSIX there's no guarantee which thread will receive signals, if it's the same in Windows I don't know. – Some programmer dude Apr 30 '15 at 15:46
  • Thanks, but my question is a bit different. If you uncomment 2nd line in main function and call crash function, the signal handler will always be invoked with the signal SIGSEGV. But in function 'thread' (not main thread) calling the same crash function does not invoke the signal handler. Re "writing to a null pointer is not guaranteed to cause a crash" - I tried directly raise(SIGSEGV) to be sure, that the problem still holds. – Ani Smbatyan May 01 '15 at 17:07
  • According to [this answer](https://stackoverflow.com/a/6533431/7325599), signal handler can be installed only once in the process and shall influence on all threads, but unfortunately it is not so on Windows as you found (where each thread has to register its own signal handler); a simpler online demo: https://gcc.godbolt.org/z/sMv3dGGGq – Fedor Oct 07 '22 at 09:15

2 Answers2

0

What's going wrong here is that you're using signal from the MSVCRT, yet you're creating threads using CreateThread from Win32 native. _beginthreadex is a MSVCRT wrapper around CreateThread that tells the runtime about the new thread.

The link references "Multithreading Support for Older Code (Visual C++)". This is because _beginthreadex predates C++11. The current C++ runtime also supports std::thread.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • It looks like the issue is still present in the latest MSVC and `std::thread`: `SIGSEGV` does not reach signal handler registered in another thread: https://gcc.godbolt.org/z/sMv3dGGGq – Fedor Oct 07 '22 at 18:16
0

By C11 standard, it is undefined behavior to use signal() as you do, see 7.14.1.1.7:

Use of this function in a multi-threaded program results in undefined behavior. The implementation shall behave as if no library function calls the signal function.

If you are interested in Windows-specific behavior, then according to Microsoft:

This allows an enormous amount of leeway, but in fact we do define behavior and that behavior is that the set of signal handlers is thread-specific. I don’t know why this particularly POSIX-hostile behavior was chosen, but given that Windows typically favors backwards compatibility over all other concerns it’s unlikely to change.

The best workaround I can recommend at this point is to use a wrapper function to create threads that has each newly-spawned thread set signal handlers before performing other work.

Fedor
  • 17,146
  • 13
  • 40
  • 131