[bug] Some QtQuick Canvas drawing coordinates cause application-wide deadlock
Description of problem:
Some X & Y coordinate combinations cause a deadlock in the QtQuick Canvas element - this apparently also deadlocks the GUI thread, resulting in the whole application irrecoverably freezing. This is usually followed by the "application is not responding dialog".
Version-Release number of selected component (if applicable):
Name: qt5-qtdeclarative-qtquick Version: 5.2.1+git19-1.23.3
How reproducible:
always
Steps to Reproduce:
- save the following minimal reproducer to a file:
import QtQuick 2.0 Canvas { id : canvas width : 800 height : 600 onPaint : { console.log("PAINT!") var ctx = canvas.getContext("2d") // original X & Y values known to cause the deadlock //var destX = -2995504 //var destY = 10467643 // but these "bigger numbers" don't cause the deadlock //var destX = -3000000000000000 //var destY = 5000000000000000 var destX = -30000000 var destY = 50000000 // line width 7 - 5 triggers the deadlock ctx.lineWidth = 7 ctx.arc(destX, destY, 3, 0, 2.0 * Math.PI) ctx.stroke() } onPainted : { console.log("PAINTED!") } Timer { interval : 10 running : true repeat : true onTriggered : { console.log("TIMER TRIGGERED") canvas.requestPaint() } } }
- run the file with qmlscene
Actual results:
The application immediately freezes once launched. Checking the log reveals that the deadlock happened after the onPaint handler fired but before the onPainted handled fired.
Example output:
$ qmlscene canvas_test.qml
[D] QWaylandEglClientBufferIntegration::QWaylandEglClientBufferIntegration:62 - Using Wayland-EGL
[D] onPaint:9 - PAINT!
End of strace output (strace qmlscene canvas_test.qml):
recvmsg(28, {msg_name(0)=NULL, msg_iov(1)=[{"l\2\1\1_\0\0\0C\1\0\0.\0\0\0\6\1s\0\7\0\0\0:1.2296\0"..., 2048}], msg_controllen=0, msg_flags=MSG_CMSG_CLOEXEC}, MSG_CMSG_CLOEXEC) = 159
recvmsg(28, 0xbed3cb78, MSG_CMSG_CLOEXEC) = -1 EAGAIN (Resource temporarily unavailable)
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
clock_gettime(CLOCK_MONOTONIC, {350351, 705681374}) = 0
poll([{fd=4, events=POLLIN}, {fd=28, events=POLLIN}], 2, 0) = 1 ([{fd=4, revents=POLLIN}])
read(4, "\5\0\0\0\0\0\0\0", 16) = 8
socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC, 0) = 31
connect(31, {sa_family=AF_LOCAL, sun_path=@"/tmp/maliit-server/dbus-bgxOt3rfge"}, 37) = 0
fcntl64(31, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(31, F_SETFL, O_RDWR|O_NONBLOCK) = 0
geteuid32() = 100000
getsockname(31, {sa_family=AF_LOCAL, NULL}, [2]) = 0
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
clock_gettime(CLOCK_MONOTONIC, {350351, 711724408}) = 0
poll([{fd=31, events=POLLOUT}], 1, 0) = 1 ([{fd=31, revents=POLLOUT}])
send(31, "\0", 1, MSG_NOSIGNAL) = 1
send(31, "AUTH EXTERNAL 313030303030\r\n", 28, MSG_NOSIGNAL) = 28
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
clock_gettime(CLOCK_MONOTONIC, {350351, 718286288}) = 0
poll([{fd=4, events=POLLIN}, {fd=28, events=POLLIN}, {fd=31, events=POLLIN}], 3, 0) = 2 ([{fd=4, revents=POLLIN}, {fd=31, revents=POLLIN}])
read(4, "\7\0\0\0\0\0\0\0", 16) = 8
read(31, "OK 9c84e1b6d2d892e2427cba0855141"..., 2048) = 37
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
clock_gettime(CLOCK_MONOTONIC, {350351, 721826651}) = 0
write(2, "[D] onPaint:9 - PAINT!\n", 23[D] onPaint:9 - PAINT!
) = 23
mmap2(NULL, 1921024, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x46300000
Expected results:
Canvas safely handles the slightly bogus coordinates without deadlocking the whole application. The coordinates are far outside of the bounding box and should be ignored anyway. This is what happens on desktop (Qt 5.4 on Fedora 21 64 bit) - Canvas has no issues with the weird coordinates and no deadlock occurs.
Example output: (from desktop)
$ qmlscene canvas_test.qml
qml: PAINT!
qml: PAINTED!
qml: TIMER TRIGGERED
qml: TIMER TRIGGERED
qml: PAINT!
qml: PAINTED!
qml: TIMER TRIGGERED
qml: TIMER TRIGGERED
qml: PAINT!
qml: PAINTED!
qml: TIMER TRIGGERED
qml: TIMER TRIGGERED
qml: PAINT!
qml: PAINTED!
qml: TIMER TRIGGERED
qml: TIMER TRIGGERED
qml: PAINT!
qml: PAINTED!
etc.
Additional info:
Note from the minimal reproducer that there seem to be a range of the coordinates that cause the issue - it is not just big numbers causing the deadlock - if the number are too big, the deadlock does not happen. It also seems to depend on line width.
Also even though the coordinates that are causing this deadlock are really rather bogus and not something most applications would want to normally use when drawing on the canvas, this bug is still a serious issue as any miscalculated coordinates that hit the deadlock range will freeze the whole application.
This can easily lead to data loss or worse if a Canvas using OS component would be affected.
Update
Filled on the Mer Bugzilla as 881.
What happens if you change the Canvas
otsaloma ( 2015-04-04 04:10:30 +0200 )editrenderStrategy
from the default "Immediate" to "Threaded" or "Cooperative"?@Osmo Salomaa Good idea! :)
So with renderStrategy : Canvas.Threaded the output looks like this:
No the absence of any PAINTED! messages. ;-) This is because the onPainted signal handler never fires and the timer just queues more and more painting requests. This manifests as a massive memory leak which eventually triggers the OOM killer that kills the application. :) Just to remove the possibility of the timer firing too often and not providing enough time for the painting I tried with a one second interval and the memory leak still happens, only slower.
And renderStrategy : Canvas.Cooperative is the same as with the default rendering strategy - deadlock/freeze after the first PAINT! message.
MartinK ( 2015-04-04 13:02:43 +0200 )edit