C++ boost threadgroup.interrupt_all() causing main thread to exit too

StackOverflow https://stackoverflow.com/questions/20894975
  •  
  •  | 
  •   ( words)
 Checked

Question

I'm using the below code to create threads and add them to a thread pool. The threads load fine and each perform a simple looping routine until the main thread calls ResetWorkerThreads a second time and kills off the sub threads. The sub threads are interrupted however the main thread exits also. There are no errors written to console. I can't wrap my head around it as it doesn't appear to have any exception and the main thread has not been added to the vecThreads thread pool. Also the second time this function is all the "All Threads Killed" is not outputted as if it never reaches that point.

std::string strPreviousSettings = "0";
std::string strPreviousAgentSettings = "0";
bool boolResetWorkers;
std::string strIP;
std::string strMACAddress;
boost::thread_group vecThreads;

std::string GetIP()
{

    std::string strIP;

    try
    {
        using namespace boost::network;

        std::string strRequest;
        http::client client;

        http::client::request request("http://test.com/ip.php");
        http::client::response response = client.get(request);
        strIP = body(response);




    }
    catch(...)
    {
        cout << "GetLocalIP - Error: " << endl;
    }

    return strIP;
}


std::string getMacAddress()
{
    std::string strMACAddress = GetFileContents("/sys/class/net/eth0/address");
    boost::replace_all(strMACAddress, ":", "");
    boost::replace_all(strMACAddress, "\n", "");
    return strMACAddress;
}


void ThreadSettingsWorker()
{
    int x = 1;
    strIP = GetIP();
    strMACAddress = getMacAddress();

    do {
        CheckEventSettings();
        CheckAgentSettings();

        if(boolResetWorkers==true)
        {
            ResetWorkerThreads();
        } else {
            boost::this_thread::sleep(boost::posix_time::milliseconds(3000));
        }


    } while ( x != 0 );
}

void ResetWorkerThreads()
{
    cout << "Resetting Workers Threads\n";
    boolResetWorkers = false;
    int intWorkerCount = 10; //Spawn 10 workers
    int X = 0;
    int intI = 1;

    cout << "Kill All Threads\n";

    try
    {
        vecThreads.interrupt_all();
    }
    catch(...)
    {
        //std::cerr << "Kill All Threads: " << std::endl;
    }

    cout << "All Threads Killed\n";


    for (int i = 0; i < intWorkerCount; ++i)
    {
        cout << "Starting Worker: " << (i + 1) << "\n";
        boost::thread tWorker(&ThreadWorker, (i + 1));
        vecThreads.add_thread(&tWorker);
    }




}


void TestRequest()
{
    try
    {
        using namespace boost::network;
        std::stringstream ss;
        http::client client;
        ss << "http://test.com/sadasdasd.html";
        http::client::request request(ss.str());
        http::client::response response = client.get(request);
        std::string strOutput = body(response);

        cout << "Test Request Out: " << strOutput << "\n";



    }
    catch(...)
    {
        cout << "TestRequest - Error: " << endl;
        return;
    }
}



void ThreadWorker(int intThread)
{
    try
    {
        int X = 0;

        do {
            cout << "Thread " << intThread << "\n";
            TestRequest();
        } while ( X != 55 );
    }
    catch(...)
    {

    }
}



void CheckEventSettings()
{

    try
    {
        using namespace boost::network;

        std::string strRequest;
        http::client client;
        http::client::request request("http://test.com/events.php");
        http::client::response response = client.get(request);
        std::string strOutput = body(response);


        if(strPreviousSettings==strOutput)
        {
            cout << "No Event Settings Changes\n";
        } else {

            cout << "Applying New Event Settings\n";
            strPreviousSettings = strOutput;
            std::string strDividerLine = "<br>";
            std::string strDividerField = "<field>";
            std::vector<std::string> vEvents;

            vEvents = EasySplit(strOutput, strDividerLine);

            for(std::vector<std::string>::const_iterator iEvent = vEvents.begin(); iEvent != vEvents.end() - 1; ++iEvent) { 

            }


        }



    }
    catch(...)
    {
        cout << "CheckEventSettings - Error: " << endl;
        return;
    }
}




void CheckAgentSettings()
{

    try
    {
        using namespace boost::network;
        std::stringstream ss;
        http::client client;
        ss << "http://test.com/checksettings.php";
        http::client::request request(ss.str());
        http::client::response response = client.get(request);
        std::string strOutput = body(response);

        if(strPreviousAgentSettings==strOutput)
        {
            cout << "No Agent Settings Changes\n";
        } else {
            cout << "Applying New Agent Settings\n";
            strPreviousAgentSettings = strOutput;
            boolResetWorkers = true;
        }



    }
    catch(...)
    {
        cout << "CheckAgentSettings - Error: " << endl;
        return;
    }
}




int main()
{


    // Start thread
    boost::thread tCheckSettings(&ThreadSettingsWorker);


    // Ask thread to stop
    //tCheckSettings.interrupt();

    // Join - wait when thread actually exits
    tCheckSettings.join();



    return 0;

}

Solution

You have an error here:

boost::thread tWorker(&ThreadWorker, (i + 1));
vecThreads.add_thread(&tWorker);

You create a local object tWorker that is deleted just after call to add_thread(). So vecThreads contains the dangling pointers to threads. When you call vecThreads.interrupt_all() you get undefined behavior because vecThreads tries to access the deleted thread objects and I suppose your program just terminates because of access violation or something.

You have to change your code to something like this:

boost::thread* ptWorker = new boost::thread(&ThreadWorker, (i + 1));
vecThreads.add_thread(ptWorker);

Please note that you don't need to delete those thread objects yourself. thread_group will delete them itself.

ADDITION:
The problem with terminate() may be caused by destructor of http::client throwing an exception. Please try this to possibly eliminate that problem in TestRequest():

try{
    http::client client;
    try{
        // other code
    }
    catch (){}
}
catch(){}

Also I'd suggest resetting vecThreads after interrupt_all(). For example you can define it as boost::scoped_ptr and then do pvecThreads.reset(new boost::thread_group()) after the call to interrupt_all().
At present the interrupted threads still remain in the thread_group after the interruption and then you try to interrupt them again along with the new threads added to the thread_group later in ResetWorkerThreads().

OTHER TIPS

Inside of ResetWorkerThreads You have:

for (int i = 0; i < intWorkerCount; ++i)
{
    cout << "Starting Worker: " << (i + 1) << "\n";
    // One issue is here, don't create a thread on the stack
    // and pass it to the thread group use new instead!
    boost::thread tWorker(&ThreadWorker, (i + 1));
    vecThreads.add_thread(&tWorker);
}

You are adding a thread created on the stack to the thread group. As soon as you iterate over the loop that threads memory is invalidated. You will need to new the thread and pass that pointer to add_thread.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow