Mendes
Mendes

Reputation: 18471

Catching exception from boost::asio::io_service::work running as a detached thread

I have my application main loop control where I do start a thread to handle asio work as follows:

void AsioThread::Run()
{
    try
    {
        /*
         * Start the working thread to io_service.run()
         */
        boost::asio::io_service::work work(ioService);
        boost::thread t(boost::bind(&boost::asio::io_service::run, &ioService));
        t.detach();

        while (true)
        {

            // Process stuff related to protocol, calling 
            // connect_async, send_async and receive_async
        }
    }
    catch (std::runtime_error &ex)
    {
        std::cout << "ERROR IN FTP PROTOCOL: " << ex.what() << std::endl;
    }
    catch (...)
    {
        std::cout << "UNKNOWN EXCEPTION." << std::endl;
    }

During the async operation, the handlers are called and sometimes I do throw exception on those handlers, like:

void AsioThread::ReceiveDataHandler(const boost::system::error_code& errorCode, std::size_t bytesTransferred)
{
        std::cout << "Receive data handler called. " << bytesTransferred << " bytes received." << std::endl;

        if (errorCode)
        {
            std::cout << "Error receiving data from server." << std::endl;
        }

        rxBufferSize = bytesTransferred;

        /* 
         * Check for response
         */
        std::string msg(rxBuffer);
        if (msg.substr(0, 3) != "220")
               throw std::runtime_error("Error connecting to FTP Server");
    }

My problem is that the exception thrown inside the async handler (AsioThread::ReceiveDataHandler) is not catched by the main processing loop try...catch block in AsioThread::Run. Naturally this happens because the working thread t is on another thread, detached, and at runtime that leads to an execution error.

How can I receive exceptions from the detached boost::asio::io_service::work thread ? How can I structure my code to make this logic work ?

Thanks for helping.

Upvotes: 2

Views: 1833

Answers (1)

VLL
VLL

Reputation: 10165

You can catch exceptions in the worker thread, save them into a queue variable that is shared by the two threads, and check that queue periodically in the main thread.

To use a queue, you need to first convert your exceptions to a common type. You can use std::exception or string or whatever is the best for your situation. If you absolutely need to keep information of the original exception class, you can use boost::exception_ptr.

Variables you need (these could be members of AsioThread):

boost::mutex queueMutex;
std::queue<exceptionType> exceptionQueue;

Run this function in the worker thread:

void AsioThread::RunIoService(){
    try{
        ioService.run();
    }
    catch(const exceptionType& e){
        boost::lock_guard<boost::mutex> queueMutex;
        exceptionQueue.push(e);
    }
    catch(...){
        boost::lock_guard<boost::mutex> queueMutex;
        exceptionQueue.push(exceptionType("unknown exception"));
    }
}

Launch the worker thread like this:

boost::thread t(boost::bind(&AsioThread::RunIoService, this));
t.detach();

In the main thread:

while(true){
    // Do something

    // Handle exceptions from the worker thread
    bool hasException = false;
    exceptionType newestException;
    {
        boost::lock_guard<boost::mutex> queueMutex;
        if(!exceptionQueue.empty()){
            hasException = true;
            newestException = exceptionQueue.front();
            exceptionQueue.pop();
        }
    }
    if(hasException){    
        // Do something with the exception
    }
}

This blog post implements a thread-safe queue, which you can use to simplify saving exceptions; in that case you would not need a separate mutex because that would be inside the queue class.

Upvotes: 2

Related Questions