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