0

Referring to Catching exception from worker thread in the main thread, I created a worker thread that throws exception to main thread and then terminates the program (the logic is to exit program upon exception happens).

I didn't seems to be implementing it correctly, as the program won't execute till the line where exit() was called.

Sample code:

#include <thread>
#include <iostream>
#include <stdexcept>

static std::exception_ptr _exceptionPtr = nullptr;

struct WorkerThread
{
    std::thread thread;

    void doSomething()
    {
        int seconds = 0;
        bool shouldStop = false;

        while(shouldStop == false)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
            std::cout << "time passed : " << ++seconds << "seconds" << std::endl;

            if (seconds == 10) // something bad happened 10 seconds later
            {
                try
                {
                    shouldStop = true;
                    throw std::runtime_error("something really bad happened");
                }
                catch (const std::runtime_error &ex)
                {
                    _exceptionPtr = std::current_exception();
                }
            }
        }
    }

    void run()
    {
        thread = std::thread([this] { doSomething(); });
        thread.detach();
    }
};

int main(int argc, char *argv[])
{
    WorkerThread workerThread;

    try
    {
        workerThread.run();
    }
    catch (...)
    {
        if (_exceptionPtr)
        {
            try
            {
                std::rethrow_exception(_exceptionPtr);
            }
            catch (std::runtime_error &ex)
            {
                // terminates program if exception happens
                std::cout << "Program will now exit" << std::endl;
                exit(EXIT_FAILURE); // but program never executes till here
            }
        }
    }

    for (;;)
    {
        // do A
        // do B
        // do C
        // do ...
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        std::cout << "waiting for thread..." << std::endl;
    }

    return 0;
}

What did i miss ? Is the current approach correct ? If no, how do i do it correctly ? thanks

HohoHaha
  • 25
  • 6
  • You need to test whether workers thread is wrong in the for loop, not for run() function, which will return immediately. – llllllllll Mar 16 '18 at 04:12
  • Possible duplicate of [How can I propagate exceptions between threads?](https://stackoverflow.com/questions/233127/how-can-i-propagate-exceptions-between-threads) – Mikhail Mar 16 '18 at 05:29
  • It doesn't look like the worker thread is actually throwing. You are catching the exception and setting the shared ptr, but not re-throwing. I don't see how your catch around the .run has anything to catch. – ttemple Mar 16 '18 at 21:35
  • have you considered passing a callback function to the thread and letting it report errors that way? – ttemple Mar 16 '18 at 21:52
  • @liliscent u are right – HohoHaha Mar 28 '18 at 05:40

2 Answers2

0

In the code you posted, the exception check is happening only once, and possibly before the thread has been launched.

Also you're catching an error from the host thread but the error you're throwing is on the second thread.

I fixed these issues, by waiting for the second thread to finish before checking the exception.

Anyways, the paradigmatic way to throw an exception across a thread can be found here: How can I propagate exceptions between threads?

#include <thread>
#include <iostream>
#include <stdexcept>

static std::exception_ptr _exceptionPtr = nullptr;

struct WorkerThread
{
    std::thread thread;

    void doSomething()
    {
        int seconds = 0;
        bool shouldStop = false;

        while (shouldStop == false)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
            std::cout << "time passed : " << ++seconds << "seconds" << std::endl;

            if (seconds == 10) // something bad happened 10 seconds later
            {
                try
                {
                    shouldStop = true;
                    throw std::runtime_error("something really bad happened");
                }
                catch (const std::runtime_error &ex)
                {
                    _exceptionPtr = std::current_exception();
                }
            }
        }
    }

    void run()
    {
        thread = std::thread([this] { doSomething(); });
        //thread.detach();
    }
};

int main(int argc, char *argv[])
{
    WorkerThread workerThread;

        workerThread.run();
        workerThread.thread.join();
        if (_exceptionPtr)
        {
            try
            {
                std::rethrow_exception(_exceptionPtr);
            }
            catch (std::runtime_error &ex)
            {
                // terminates program if exception happens
                std::cout << "Program will now exit" << std::endl;
                exit(EXIT_FAILURE); // but program never executes till here
            }
        }

    for (;;)
    {
        // do A
        // do B
        // do C
        // do ...
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        std::cout << "waiting for thread..." << std::endl;
    }

    return 0;
}
Mikhail
  • 7,749
  • 11
  • 62
  • 136
  • I might be wrong but if making host thread wait for worker thread finished, doesn't it lose the intention of threading ? i wan them to run concurrent. – HohoHaha Mar 16 '18 at 06:07
  • @HohoHaha In this case yes, but in general no. You can do work before calling the wait command. You ca have more than one thread you're waiting on. You can also have a loop that polls if an error has occurred. – Mikhail Mar 16 '18 at 06:36
0

Exception should be caught at the for loop instead of workerThread.run(), since run() will exit immediately.

#include <thread>
#include <iostream>
#include <stdexcept>

static std::exception_ptr _exceptionPtr = nullptr;

struct WorkerThread
{
    std::thread thread;

    void doSomething()
    {
        int seconds = 0;
        bool shouldStop = false;

        while (shouldStop == false)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
            std::cout << "time passed : " << ++seconds << "seconds" << std::endl;

            if (seconds == 10) // something bad happened 10 seconds later
            {
                try
                {
                    shouldStop = true;
                    throw std::runtime_error("something really bad happened");
                }
                catch (const std::runtime_error &ex)
                {
                    _exceptionPtr = std::current_exception();
                }
            }
        }
    }

    void run()
    {
        thread = std::thread([this] { doSomething(); });
        thread.detach();
    }
};

int main(int argc, char *argv[])
{
    WorkerThread workerThread;

    workerThread.run();



    for (;;)
    {
        // do A
        // do B
        // do C
        // do ...
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        std::cout << "waiting for thread..." << std::endl;

        if (_exceptionPtr)
        {
            try
            {
                std::rethrow_exception(_exceptionPtr);
            }
            catch (std::runtime_error &ex)
            {
                // terminates program if exception happens
                std::cout << "Program will now exit" << std::endl;
                exit(EXIT_FAILURE); // but program never executes till here
            }
        }
    }

    return 0;
}

Credits to @liliscent

HohoHaha
  • 25
  • 6