C++ CTRL+C Handler having undefined behavior?

0

I have a large C++ program running on Windows 10, that has a main system answering socket requests and 2 threads running monitoring systems via separate instances of a socket client class.

At software shutdown (CTRL+C or exit X button), I need to be able to gracefully stop 2 threads, the threads shut down some physical equipment via sockets.

When a CTRL+C event is registered, the sExit() function is called. This function sets an atomic variable kill_child_threads=true to kill while loops in the remote threads. Unfortunately, when I join the remote threads, they do not seem to join, or at least the join does not block the sExit() function.

Then the sExit() function randomly 'exits?', without completing all the code. But the program seems to cleanly shutdown according to windows and exit codes. VS2019 does not detect any issues, and the program exits with 0xc000013a (Windows CTRL+C).

When I try to debug step into the sExit() function, it will run a few lines, but at some point it 'crashes' or just exits the entire program without any exceptions or completion of the function. It does not happen at the same line every time, rather at different points. It sometimes 'crashes' before or after the thread.join()'s are called, and based on logging, the program crashes often before the completion of the 2 child threads.

The configuration below historically worked with 1 thread, but upon adding a 2nd thread recently, I get what I would consider undefined behavior.

Any help or methods to debug would be very helpful.
Please note the LOG system is thread safe.

Def in top of main.cpp & Creation of threads inside main(void):

//top of main.cpp
std::thread *rotor_thread;
std::thread *heat_thread;

//main()
rotor_thread= new std::thread(TubeControl::rotorThing);
heat_thread= new std::thread(HeatExControl::heatThing);
BOOL WINAPI sExit(DWORD CtrlType)
{

    if ((CtrlType == CTRL_CLOSE_EVENT) | (CtrlType == CTRL_C_EVENT)) {
        LOG(DEBUG) << "Entering Close Event or Ctrl-C Event";

        //Kill Tube rotor thread & spin to ECO mode.
        kill_child_threads.store(true, std::memory_order_seq_cst);
        try 
        {
            heat_thread->join();
            rotor_thread->join();
        }
        catch (std::exception & e)
        {
            LOG(ERROR) << "Issue Killing:" << e.what();
        }
    //More stuff here on main thread...
    //That never executes, or sometimes partially executes.
}

Thread 1:

void rotorThing(void)
{
    while (!kill_child_threads.load(std::memory_order_seq_cst))
    {
        //Do Thread safe stuff.
        //Sleep
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    //Program shutting down
    try
    {
        LOG(INFO) << "[Rotor] xxxxxxxxxxxxxxx.";
        //Do Thread safe stuff.
    }
    catch (std::exception & e)
    {
        LOG(ERROR) << "Issue shutting down tube" << e.what();

    }
}

Thread 2:

void heatThing(void)
{
    enum HEAT_STATE { COLD, HOT };
    HEAT_STATE heat_state = HEAT_STATE::COLD;
    LOG(DEBUG) << "[Heat] Starting Heat Exchanger Monitoring Thread.";
    std::string resp = "";

    while (!kill_child_threads.load(std::memory_order_seq_cst))
    {
        //Check tube speed, and Hx temps and flows
        hwHxSock->sendCmd("hx,get_inlet_temp");
        float itemp = stof(hwHxSock->getRespString());
        LOG(DEBUG) << "[Heat Xfer Mon] xxxxxxxxx";
        std::this_thread::sleep_for(std::chrono::seconds(5));
    }

    hwHxSock->sendCmd("hx,get_inlet_temp");
    float itemp = stof(hwHxSock->getRespString());
}

c++
winapi
crash
undefined-behavior
asked on Stack Overflow Nov 19, 2019 by MadHatter • edited Nov 20, 2019 by MadHatter

1 Answer

1

when user click close on console window - HandlerRoutine callback ( sExit in your case) receive CTRL_CLOSE_EVENT. with this event - system force terminate your process after SPI_GETHUNGAPPTIMEOUT (by default 5000ms). whatever you do in your process(Circumstances - any) - after some timeout - if you not exit yourself - your process will be external terminated with STATUS_CONTROL_C_EXIT code. this exactly give effect "exit in a random place" (really your code can be at any place when terminate called)

but in case CTRL_C, CTRL_BREAK - no timeout - look for Timeouts section in the end

answered on Stack Overflow Nov 20, 2019 by RbMm

User contributions licensed under CC BY-SA 3.0