Op 6-9-2013 19:56, Matt Broadstone schreef:
On Fri, Sep 6, 2013 at 1:52 PM, David Faure <david.fa...@kdab.com
<mailto:david.fa...@kdab.com>> wrote:
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?
+1, or more if possible :)
Is this code up somewhere already for early review? This is very much
needed in Qt imho, I imagine it's one of those patterns that people
roll themselves in many cases.
Matt
First of all: I like the idea of a job API in Qt as well. Like many
others, I have written my own before. However, I'd hope the design would
not limit itself to just event-driven jobs, if I understand correctly
what that means. Some of my jobs are just heavy processing that require
multi-threading. Would this API suport that too, or would that be hacky
again to implement? Another aspect that I build into my own solution and
that I find valuable, is that I have added a mechanism to prevent the
same work being done twice. If different parts of the code ask for the
same calculation to be performed (that can happen in my case), they
actually get a pointer to the same job back. That turns out to be very
convenient. Note however, that that requires a job manager, and it
doesn't look like that is in this design. In my case, I submit a request
to the manager, who then returns a job.
Note that in my case, I return QSharedPointer<Job>, so it is clear to
everyone that the code that everyone that requests a job and gets one,
can't treat it as if it was theirs exclusively.
André
_______________________________________________
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development