QProcess::startDetached and bash stdout capture
Thiago Macieira
thiago at kde.org
Sat Mar 25 15:50:37 GMT 2017
On sábado, 25 de março de 2017 08:07:37 PDT David Faure wrote:
> So I have now separated this into two processes:
> * kdialog, the parent process that exits as soon as things are set up
> * kdialog_progress_helper, the child process that keeps running and answers
> to the dbus calls.
>
> It almost works.
> It works when copy/pasting the output of kdialog into qdbus calls :
> $ kdialog --progressbar 'YOUR TEXT HERE' 4
> org.kde.kdialog-1707 /ProgressDialog
> $ qdbus org.kde.kdialog-1707 /ProgressDialog value 1
>
> But it doesn't work when using the shell $(...) or `...` syntax to capture
> stdout. The shell just never makes it to the next line, it blocks forever.
> It's like kdialog doesn't exit, but it does... it's shown as <defunct> by
> `ps`, meaning (AFAIK) that it has terminated. The shell just doesn't move
> along, but why?
>
> It's related to QProcess::startDetached, because if I skip calling that then
> the shell is happy to capture stdout and move on.
>
> Bug in QProcess::startDetached ?
Not really.
One problem is because the launched process inherits the pipes to stdout and
stderr, but the shell sees the process group it launched exiting. It will
probably close the pipe. Normally, this process would exit soon with SIGPIPE,
but since a lot of code relating to sockets and pipes turns SIGPIPE off, it's
possible that the detached process never receives SIGPIPE and simply remains
forever.
> I tried close(1) like the kde4 kdialog was doing (due to forking, though),
> no change.
There's another problem: you have a race condition between the parent
(kdialog) exiting and the grandchild (helper) writing its information before
closing stdout. The shell doesn't know about the grandchild, so it will stop
reading from the pipe when kdialog exits.
kdialog needs to wait until the grandchild has written. But startDetached has
no synchronisation opportunities. You'll need something external.
Options:
1) on Unix, you can open a non O_CLOEXEC pipe from the parent and pass its
file descriptor number on the command-line to the helper. The helper can write
there and close. kdialog can wait for it with QSocketNotifier.
2) you can open a socket (like a QLocalSocket/QLocalServer) and pass its
address on the command-line to the helper. Then same as above.
3) you can also use D-Bus: pass a service name to the helper that it will
make a call to indicating it's ready.
All three options also allow passing of extra information, which should be the
helper's own D-Bus service name. That way, the helper need not write to its
stdout and should just close it like a good daemon (remember to reopen to
/dev/null).
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
More information about the kde-core-devel
mailing list