[Development] A QtCore class for event-driven jobs

David Faure david.faure at kdab.com
Fri Sep 6 19:52:47 CEST 2013


I would like to propose the inclusion of a QJob class in QtCore, based on 
years of experience with KIO::Job and KJob.

The idea is to encapsulate an event driven asynchronous operation into a job 
class. Example use cases:
- a network download with QNetworkAccessManager.
- operations (command+reply) over a QLocalSocket or QTcpSocket (like akonadi).
- long async dbus calls (special case of the previous line)
- long tasks executed by external processes (e.g. "make" from an IDE, "unrar" 
from an archive program)
...

At the core, QJob is really just a class with start() and kill(), calling pure 
virtual methods doStart() and doKill(), and signals, most importantly the 
result(QJob *) signal.

The expected use case is:

 void SomeClass::methodWithAsynchronousJobCall()
 {
   QJob* job = someoperation(some parameters);
   connect(job, SIGNAL(result(QJob*)),
           this, SLOT(handleResult(QJob*)));
   job->start(); // or it could just autostart, which I actually prefer...
 }
   (other connects, specific to the job)

 And handleResult is usually at least:

 void SomeClass::handleResult( QJob *job )
 {
   if (job->error()) {
       // handle error
   } else {
      // handle succesful job completion, if needed
   }
 }

But it can and should also have the following features:
* error code, error text
* suspend/resume with doSuspend/doResume virtual methods
* capabilities Killable and Suspendable, to avoid trying these on jobs that 
don't support them
* kill(Quietly) vs kill(EmitResult), for the app's convenience
* a finished signal that is emitted with both types of killing, for things 
like progress dialogs
* auto-deletion (on by default, can be turned off)
* synchronous exec() using a QEventLoop, with a big fat huge warning about not 
using that in GUI apps (ideally only to be used in unittests or separate 
threads).
* more standard signals for progress info, messages, warnings..

The whole point of standardizing such signals is that it allows generic GUIs 
to be built on top, so that your app or your desktop can show the progress of 
multiple concurrent jobs, of different types (file download, CD burning, mail 
checking, etc. etc.)

Finally, for the benefit of job implementors, QJob would support sub-jobs.
The job implementation would choose when to create these subjobs (all at once 
initially, to have them run in parallel, or one after the other, for sequence 
of operations). KDE currently does that in a subclass (KCompositeJob) but 
Thiago and I (and kio, and akonadi) agree that it's better to have it all in 
one class instead.

We also have a standard interface for error handling so that all jobs from a 
given framework can have their error handled the same way, but thinking about 
it, that part could stay separate, at least for now.

Well, that's it. So why this email? Because Thiago asked me to, and to gather 
some support. I plan to make a merge request for Qt 5.2.

Thiago asked more specifically:

* API-wise, can't this be merged with QFuture?
-> no, because QFuture encapsulates a value, with blocking methods for getting 
the value, even available as casting-to-the-value. If we imagine a QFuture 
that wraps a QJob, and the blocking method calling exec(), we'd have a lot 
more nested event loops than is good for the health of our programs.
On the other hand, QJob would not be related to any value. It's really a 
QObject that informs of progress via signals.

* relation to QRunnable?
A runnable is also some sort of "job", but the implementation is completely 
different: a runnable is one sync method, while a qjob is all signals/slots 
based, with start() returning immediately. So one can't be used like the 
other, a given task implementation is either a blocking task for a thread with 
no event loop (QRunnable) or an async task that can actually be used in any 
thread with an event loop.

* relation to QNetworkReply?
If that one didn't exist yet, we'd definitely write it as a QJob subclass. So 
instead, I propose to wrap QNetworkReply into a QNetworkJob or something, in 
order to offer the QJob interface for QNAM requests. On one hand this doesn't 
have to go in at the same time as QJob itself, but OTOH it could be a real-
world testcase for it, proving its usefulness and correctness...

Any other questions?

-- 
David Faure | david.faure at kdab.com | Managing Director KDAB France
KDAB (France) S.A.S., a KDAB Group company
Tel. France +33 (0)4 90 84 08 53, Sweden (HQ) +46-563-540090
KDAB - Qt Experts - Platform-independent software solutions




More information about the Development mailing list