c_k
c_k

Reputation: 1766

How to offload a computationally heavy task which is highly dependent on proper exception handling?

In my Qt application I have to perform some computationally heavy tasks, typically loading huge datasets from disk (single-threaded). The code in the task heavily relies on proper exception handling and is strictly non-Qt. I offload these tasks from the GUI thread using QtConcurrent::run(), so that I can show a progress bar. However as I understand from the Qt docs, I can't catch exceptions in the GUI thread. I understand that theoretically this would not make sense. However, in practice I only use threading because of the progress bar. What is the best way to deal with this situation?

Edit: I'd like to emphasize that I do not need fine grained control over the offloaded work. I really only offload it to show a progress bar. This is probably a situation others have encountered as well.

Upvotes: 2

Views: 358

Answers (3)

c_k
c_k

Reputation: 1766

I made it work now. Instead of calling QtConcurrent::run directly, I use a Qt wrapper which is able to catch non-Qt exceptions. When an exception is caught, it is pushed to the main thread to a queued connection and rethrown.

    namespace MyConcurrent {

    template <class U, class V>
    QFuture<U> offload(V func)
    {       
        U (*caller)(V) = offloaded_placeholder<U>;
        return QtConcurrent::run(caller, func);
    }

    template <class U, class V>
    U offloaded_placeholder(V func)
    {
        try
        {       
            return func();
        }
        catch(Utilities::ExceptionBase& e)
        {
            qRegisterMetaType<Utilities::ExceptionBase>("Utilities::ExceptionBase");
            QMetaObject::invokeMethod(ConcurrencyCommunicator::getInstance(), "onRequestPushExceptionToMainThread", Qt::QueuedConnection, Q_ARG(const Utilities::ExceptionBase&, e));           
        }
    }

    }

Usage:

    QFuture<ret_type> = MyConcurrent::offload<ret_type>(std::bind(&some_func, params));

Please note that Utilities::ExceptionBase is a class deriving from standard exceptions. ConcurrencyCommunicator is a simple class, to which the GUI has a pointer. The pushed exception is rethrown inside ConcurrencyCommunicator and then caught by the main thread's event loop.

Upvotes: 1

gentlecolts
gentlecolts

Reputation: 23

I am going to say upfront that I have not used Qt. That being said, if you can, you should try and share variables between the threads. Use a few global variables or pointers to communicate the state of one thread to the other (don't forget that concurrency is a potential issue).

You should also consider which thread is leading and which is following. Are you sure that you want the gui thread controlling the thread that is reading data? Could you possibly have the thread that is loading data just handle its own exceptions and communicate its progress to the gui thread? Why is the gui thread the central/controlling/main thread?

Sorry I cant offer more advice, but good luck with your program :)

Upvotes: -1

Pavel Strakhov
Pavel Strakhov

Reputation: 40512

You can wrap your entire function into one try/catch. When an exception is caught, you can notify GUI thread using a custom signal.

Also you can run your function in GUI thread and call QApplication::processEvents() periodically to update the UI (progress bars, etc).

Upvotes: 1

Related Questions