PDA

View Full Version : throwing exceptions across thread boundaries (using QtConcurrent)



glenn
14th February 2009, 15:13
Hello,

I need to throw an exception across a thread boundary.
I thought this was possible with QtConcurrent::Exception after reading the docs (http://doc.trolltech.com/4.4/qtconcurrent-exception.html) however this does not seem to work for me. I tested the following code with gcc 4.3 and with VS2008:


#include <QtCore>

class MyException : public QtConcurrent::Exception
{
public:
void raise() const { throw *this; }
Exception *clone() const { return new MyException(*this); }
};

void runInOtherThread()
{
qDebug("other thread");
throw MyException();
}

int main(int argc, char* argv[])
{
QCoreApplication a(argc, argv);
try
{
QtConcurrent::run(&runInOtherThread);
}
catch(MyException &e)
{
qDebug("exception caught");
}
return a.exec();
}

I would expect to end up in the catch block in the main() function. However i get the following output:

$ ./exceptiontest
other thread
Qt Concurrent has caught an exception thrown from a worker thread.
This is not supported, exceptions thrown in worker threads must be
caught before control returns to Qt Concurrent.
terminate called after throwing an instance of 'MyException'
what(): std::exception
Aborted
$

The warning is printed in QThreadPoolThread::run().
This conflicts with my interpretation of the manual, which says:

Qt Concurrent supports throwing and catching exceptions across thread boundaries, provided that the exception inherit from QtConcurrent::Exception and implement two helper functions:

But probably i'm just doing something wrong here :)
Any help or pointers would be very much appreciated :)

thanks,
glenn

wysota
16th February 2009, 21:57
Strictly from the logical point of view catching C++ exceptions in threads different than the ones they originate from wouldn't make sense. I'm not sure if it would make sense even with Java exceptions...

You have two different flows which may be at arbitrary points during the exception and you want one of them (the outer one) to jump completely elsewhere (to the "catch" block) and then what? go back? Continue after the catch block?

run() is an asynchronous call - the calling thread continues execution. From what I see it is only possible to catch transferred exceptions thrown by the four methods mentioned at the end of the reference section you mention when using QFuture (and run() uses one). Other exceptions won't be caught properly. So either use a blocking call or don't use exceptions (but signals and slots or events, for example).

spud
16th February 2009, 23:30
I guess the documentaion could be a bit clearer. In the following scenario, the exception gets caught as expected:


void runInOtherThread(int)
{
qDebug("other thread");
throw MyException();
}

int main(int argc, char* argv[])
{
QCoreApplication a(argc, argv);
try
{
QVector<int> argument(100);
QtConcurrent::map(argument, &runInOtherThread).waitForFinished();
}
catch(MyException &e)

So if you have a scenario where you have a lot of jobs to be executed asynchronously while the main thread waits, it would make sense to throw an exception to cancel all processing on failure.
But Wysota is right, it only makes sense when using a blocking call. In your example, the main thread would most likely have left the try-catch block before the worker thread gets around to throwing the exception.

glenn
18th February 2009, 14:51
Thanks for your responses.
I can now see that the code i posted can indeed not work as is.
However shouldn't i be able to catch the exception when i use a QFuture with the QtConcurrent::run() and call future.result() in a slot connected to QFutureWatcher::finished() ?

I tried to do this but it does not work either..

wysota
18th February 2009, 17:09
No, because it has no way of knowing you are calling it from a slot connected to finished. And even if it did, it still wouldn't make sense to thow an exception there, result() is only a getter and nothing bad can happen after finished() is signalled as there are no more threads performing the job.

magalha
6th September 2011, 23:19
I have a similar problem with the following piece of code (Manager :: processPlugins is a valid method that receives 3 ints as parameters; the piece of code is inside a method that belongs to a Manager instance)


QList<QFuture<void> > workers;
int numWorkers = QThread::idealThreadCount();
for( int worker = 0; worker < numWorkers; worker++ ){

workers.push_back( QtConcurrent::run(


this,


&Manager :: processPlugins,


worker,


2,


3);
}
for( int worker = 0; worker < workers.size(); worker++ ) {

try {


workers[worker].waitForFinished();

} catch ( MyException& ex ) {


printf( "Exception thrown\n" );

}
}



However, when a MyException instance is thrown inside processPlugins, the catch block does not execute. Instead, the application terminates, and this warning appears:

Qt Concurrent has caught an exception thrown from a worker thread.
This is not supported, exceptions thrown in worker threads must be
caught before control returns to Qt Concurrent

Am I missing something?
Thanks for any help

Cheers,
João Magalhães