libksane seems to break QProcess::start calls
Thiago Macieira
thiago at kde.org
Fri Mar 4 20:06:14 GMT 2022
On Friday, 4 March 2022 10:52:00 PST Tobias Leupold wrote:
> I simply made this a QObject subclass and rather then using start() etc.
> directly, I created a QProcess object and did the same stuff with this one.
>
> No freezes anymore. No matter what scanner I use.
>
> I never thought subclassing the QProcess would change the behavior in such a
> radical way :-O Is there some passage in the docs that says "Never ever
> subclass a QProcess unless you exactly know what you are doing, otherwise,
> you will experience the weirdest problems"? :-D
It's a side-effect. The problem is the QProcess::setupChildProcess virtual in
Qt 5, that had been there since QProcess was introduced the the Paleolithic
Era. When we use CLONE_PIDFD, we have to call clone() directly, which means
the hooks installed by pthread_atfork() do not get run. In particular, this
may mean the mutexes locked by other threads are all still locked, including
one inside malloc(). That would mean the user's code in setupChildProcess()
could deadlock depending on the kernel version.
To avoid this possibility, we needed to know if you'd overridden
setupChildProcess(). When I introduced the CLONE_PIDFD content, we originally
added a very hacky way of detecting that, but that was way too fragile. So it
was simplified to "if you derive, assume setupChildProcess was overridden." Qt
6 removed this virtual and replaced it with a callback using std::function
(QProcess::setChildProcessModifier). So we can be much more sure that you have
code that you want to run on the child side before execve().
Anyway, please note that simply your problem still exists even without
subclassing on kernels older than 5.4. For those, we have to use the SIGCHLD
handler anyway. This must be fixed in the SANE backend.
Or you can work around it by doing what it is doing: fork(). Run the SANE
backend entirely in a child process, so it can't affect the main application's
SIGCHLD handler. If you need to share memory, you can do that by memory-
mapping a file before fork(), as that gets shared between parent and child. A
memfd would be ideal, but if that fails, you can use a regular QTemporaryFile.
On Linux, QSharedMemory is just a memory-mapped file on a tmpfs (/dev/shm)
after all, only it's hiding behind two or three layers of abstraction.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel DPG Cloud Engineering
More information about the kde-devel
mailing list