PDA

View Full Version : Catching exceptions that were thrown by a concurrent thread



blooglet
22nd December 2011, 16:17
I have a problem similar to this poster (http://www.qtcentre.org/threads/18771-throwing-exceptions-across-thread-boundaries-(using-QtConcurrent)), but I can't figure out what I should do in my case. Instead of calling exit(EXIT_FAILURE) on a separate thread, I want the main thread to handle the exception. To me this looks clean and organized. I tried this:


#include <QString>
#include <iostream>
#include <QtConcurrentRun>
#include <qtconcurrentexception.h>
using std::cerr;
using std::ostream;

class MyException : public QtConcurrent::Exception {
public:
MyException(const QString& msg) { _msg = msg; }
virtual ~MyException() throw() {}

const QString& msg() { return _msg; }

void raise() const { throw *this; }
Exception *clone() const { return new MyException(*this); }
private:
QString _msg;
};

void separateThread() {
int i = 0;
while (i < 10000)
++i;

throw MyException("This exception should be caught by the main thread");
}

int main() {
QFuture<void> otherThread = QtConcurrent::run(&separateThread);

int i = 0;
while (i < 10)
++i;

try {
otherThread.waitForFinished();
} catch (MyException& e) {
std::cerr << "QUITTING WITH ERROR\n" << e.msg().toStdString() << std::endl;
}

return 0;
}


I kept in mind that


When using QFuture, transferred exceptions will be thrown when calling the following functions:
- QFuture::waitForFinished()
- QFuture::result()
- QFuture::resultAt()
- QFuture::results()

The exception, however, isn't handled and the program crashes. What am I doing wrong here?

amleto
22nd December 2011, 18:22
It might be thrown when you call QtConcurrent::run.

get rid of the pointless while i < 10 loop, and put the run(...) command inside the try block. Does it help?

edit: nope, just tried it. hmmm....

Added after 16 minutes:

I note that refactoring to use blockingMap does work as expected.

I saw this:


Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting. The QFuture returned can only be used to query for the running/finished status and the return value of the function.


So I presume there is something slightly different about QtConcurrent::run(...) that means you can't use run for your desired behaviour.

I haven't tried, but I bet map(...) and .waitForFinished(...) would work - you just need to make a dummy sequence

blooglet
23rd December 2011, 16:14
I managed to replicate the desired behavior by connecting a signal from the other thread to a slot that executes on the main thread, where the exception is thrown and caught.

amleto
23rd December 2011, 16:53
But what is the point in using concurrent to start a slot running in the main thread :confused::confused:

blooglet
23rd December 2011, 16:56
I'm not delegating the work to the main thread. When I want to throw an exception on the other thread, I broadcast a signal that is connect with a private slot on the main thread. This private slot throws the exception. But I claimed victory too soon.


Qt has caught an exception thrown from an event handler. Throwing
exceptions from an event handler is not supported in Qt. You must
reimplement QApplication::notify() and catch all exceptions there.

Am I searching in the right direction though? I don't see why using QtConcurrent::map with a dummy sequence (sounds kinda clumsy) would fix the issue.

amleto
23rd December 2011, 17:09
You don't see why? Even though I told you that blockingMap works fine?



#include <QString>

#include <iostream>
#include <vector>

#include <QtCore>

using std::cerr;
using std::ostream;

class MyException : public QtConcurrent::Exception {

public:

MyException(const QString& msg) { _msg = msg; }
virtual ~MyException() throw() {}

const QString& msg() const { return _msg; }
void raise() const { throw *this; }
Exception *clone() const { return new MyException(*this); }

private:
QString _msg;
};



void separateThread(int) {

int i = 0;
while (i < 10000)
++i;

throw MyException("This exception should be caught by the main thread");
}


int main()
{
std::vector<int> ints(1);

try
{
QtConcurrent::blockingMap(ints, separateThread);
}
catch (const MyException& e)
{
std::cerr << "QUITTING WITH ERROR\n" << e.msg().toStdString() << std::endl;
}

return 0;
}

blooglet
26th December 2011, 21:19
Note: This function will block until all items in the sequence have been processed.
-- http://developer.qt.nokia.com/doc/qt-4.8/qtconcurrent.html#blockingMap

But if blockingMap blocks until the function is done executing for each item in the sequence, then the function isn't being executed in a separate thread while the main thread carries on its duties, is it?

amleto
26th December 2011, 23:02
Firstly, yes it IS in a separate thread. Secondly, where is that your requirement?? Your own example blocks with waitforfinished!!

In any event, if you want asych behaviour then just use map. like I said already.

blooglet
27th December 2011, 10:02
Your own example blocks with waitforfinished!!
At the end of my calculations on the main thread, I wait for the second thread to finish his calculations. In practice you could use two threads to do calculations that can be done in parallel. The main thread does his calculations while the second thread does his calculations as well, then the main thread waits for the second thread to finish working. The main thread then uses calculation results from both threads to make a final calculation.

Why would I ever want to do work on a second thread and then exit without waiting for the results?


Firstly, yes it IS in a separate thread. Secondly, where is that your requirement??
It seems kinda pointless to do work on a separate thread while the main thread can't carry out any calculations itself. The whole point of multithreading is that you can execute calculations in parallel.


In any event, if you want asych behaviour then just use map. like I said already.

If I change blockingMap to map in your code, the program exits before the exception is thrown. That isn't very productive. This seems to work though:


#include <QString>
#include <QtConcurrentRun>

#include <iostream>
#include <vector>

#include <QtCore>

using std::cerr;
using std::ostream;

class MyException : public QtConcurrent::Exception {

public:

MyException(const QString& msg) { _msg = msg; }
virtual ~MyException() throw() {}

const QString& msg() const { return _msg; }
void raise() const { throw *this; }
Exception *clone() const { return new MyException(*this); }

private:
QString _msg;
};



void separateThread(int) {

int i = 0;
while (i < 10000)
++i;

throw MyException("This exception should be caught by the main thread");
}


int main()
{
std::vector<int> ints(1);
QFuture<void> future;

try
{
future = QtConcurrent::map(ints, separateThread);

int i = 0;
while (i < 10000)
++i;

future.waitForFinished();
}
catch (const MyException& e)
{
std::cerr << "QUITTING WITH ERROR\n" << e.msg().toStdString() << std::endl;
}

return 0;
}

amleto
27th December 2011, 11:30
At the end of my calculations on the main thread, I wait for the second thread to finish his calculations. In practice you could use two threads to do calculations that can be done in parallel. The main thread does his calculations while the second thread does his calculations as well, then the main thread waits for the second thread to finish working. The main thread then uses calculation results from both threads to make a final calculation.

Why would I ever want to do work on a second thread and then exit without waiting for the results?


It seems kinda pointless to do work on a separate thread while the main thread can't carry out any calculations itself. The whole point of multithreading is that you can execute calculations in parallel.



If I change blockingMap to map in your code, the program exits before the exception is thrown. That isn't very productive. This seems to work though:

I didn't say replace word for word. I said use map instead of blocking map. And your code uses exactly what I said to do in post #2!



I don't see why using QtConcurrent::map with a dummy sequence (sounds kinda clumsy) would fix the issue.

Can you see now?

blooglet
27th December 2011, 11:35
Can you see now?
I've verified that it works, but since I have to supply a dummy sequence it looks like I'm using that function for a purpose it wasn't intended for.

amleto
27th December 2011, 11:54
you are welcome.