Reputation: 18471
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
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