JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
16k stars 1.16k forks source link

Race conditions (JVM Core Dump, Exceptions, Failing to render) #974

Closed jimgoog closed 2 years ago

jimgoog commented 3 years ago

Probably a race condition of some sort, the following stress test demonstrates the issue with all sorts of failures (JVM Core Dump, Exceptions, Failing to render, etc).

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.TestComposeWindow
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import kotlinx.coroutines.delay

fun main() = application {

    Thread {
        run {
            while (true) TestComposeWindow(500, 500).setContent { Text("Hello, World!") }
        }
    }.start()

    Window(onCloseRequest = ::exitApplication) {
        Column {
            var value by remember { mutableStateOf(0) }
            LaunchedEffect(Unit) {
                while(true) {
                    value++
                    delay(1)
                }
            }
            Text(value.toString())
            TextField(
                "a".repeat(1000),
                {},
                Modifier.fillMaxSize()
            )
        }
    }
}

cc @igordmn

olonho commented 3 years ago

Crash happened where two threads were inside Skia:

C  [libskiko-macos-x64.dylib+0x19ad30]  void SkRecorder::append<SkRecords::DrawPoints, SkPaint const&, SkCanvas::PointMode&, unsigned int, SkPoint*>(Sk
Paint const&, SkCanvas::PointMode&, unsigned int&&, SkPoint*&&)+0xc0
C  [libskiko-macos-x64.dylib+0x19ab87]  SkRecorder::onDrawPoints(SkCanvas::PointMode, unsigned long, SkPoint const*, SkPaint const&)+0x197
C  [libskiko-macos-x64.dylib+0x69109]  SkCanvas::drawPoints(SkCanvas::PointMode, unsigned long, SkPoint const*, SkPaint const&)+0xa9
C  [libskiko-macos-x64.dylib+0x6c470]  SkCanvas::drawLine(float, float, float, float, SkPaint const&)+0x40
j  org.jetbrains.skija.Canvas._nDrawLine(JFFFFJ)V+0
j  org.jetbrains.skija.Canvas.drawLine(FFFFLorg/jetbrains/skija/Paint;)Lorg/jetbrains/skija/Canvas;+38
j  androidx.compose.ui.graphics.DesktopCanvas.drawLine-Wko1d7g(JJLandroidx/compose/ui/graphics/Paint;)V+33
j  androidx.compose.ui.graphics.drawscope.CanvasDrawScope.drawLine-NGM6Ib0(JJJFILandroidx/compose/ui/graphics/PathEffect;FLandroidx/compose/ui/graphics
/ColorFilter;I)V+40
j  androidx.compose.ui.node.LayoutNodeDrawScope.drawLine-NGM6Ib0(JJJFILandroidx/compose/ui/graphics/PathEffect;FLandroidx/compose/ui/graphics/ColorFilter;I)V+20
j  androidx.compose.ui.graphics.drawscope.DrawScope$DefaultImpls.drawLine-NGM6Ib0$default(Landroidx/compose/ui/graphics/drawscope/DrawScope;JJJFILandroidx/compose/ui/graphics/PathEffect;FLandroidx/compose/ui/graphics/ColorFilter;IILjava/lang/Object;)V+110
j  androidx.compose.material.TextFieldKt$drawIndicatorLine$1.invoke(Landroidx/compose/ui/graphics/drawscope/DrawScope;)V+66
j  androidx.compose.material.TextFieldKt$drawIndicatorLine$1.invoke(Ljava/lang/Object;)Ljava/lang/Object;+5
j  androidx.compose.ui.draw.DrawBackgroundModifier.draw(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V+11
j  androidx.compose.ui.node.ModifiedDrawNode.performDraw(Landroidx/compose/ui/graphics/Canvas;)V+261
J 1425 c1 androidx.compose.ui.node.LayoutNodeWrapper.draw(Landroidx/compose/ui/graphics/Canvas;)V (71 bytes) @ 0x0000000114253f74 [0x00000001142539a0+0x00000000000005d4]
j  androidx.compose.ui.node.LayoutNodeDrawScope.drawContent()V+39
j  androidx.compose.ui.draw.DrawBackgroundModifier.draw(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V+18
j  androidx.compose.ui.node.ModifiedDrawNode.performDraw(Landroidx/compose/ui/graphics/Canvas;)V+261
j  androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke()V+8
j  androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke()Ljava/lang/Object;+1
J 1400 c1 androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)V (561 bytes) @ 0x0000000114240c1c [0x000000011423f160+0x0000000000001abc]
J 2140 c1 androidx.compose.ui.node.LayoutNodeWrapper.invoke(Landroidx/compose/ui/graphics/Canvas;)V (57 bytes) @ 0x0000000114395f64 [0x00000001143958a0+0x00000000000006c4]
J 2139 c1 androidx.compose.ui.node.LayoutNodeWrapper.invoke(Ljava/lang/Object;)Ljava/lang/Object; (12 bytes) @ 0x000000011439544c [0x0000000114395240+0x000000000000020c]
j  androidx.compose.ui.platform.SkijaLayer.performDrawLayer(Landroidx/compose/ui/graphics/DesktopCanvas;Landroidx/compose/ui/geometry/Rect;)V+180

and

j  org.jetbrains.skija.Canvas._nDrawLine(JFFFFJ)V+0
j  org.jetbrains.skija.Canvas.drawLine(FFFFLorg/jetbrains/skija/Paint;)Lorg/jetbrains/skija/Canvas;+38
j  androidx.compose.ui.graphics.DesktopCanvas.drawLine-Wko1d7g(JJLandroidx/compose/ui/graphics/Paint;)V+33
j  androidx.compose.ui.graphics.drawscope.CanvasDrawScope.drawLine-NGM6Ib0(JJJFILandroidx/compose/ui/graphics/PathEffect;FLandroidx/compose/ui/graphics/ColorFilter;I)V+40
j  androidx.compose.ui.node.LayoutNodeDrawScope.drawLine-NGM6Ib0(JJJFILandroidx/compose/ui/graphics/PathEffect;FLandroidx/compose/ui/graphics/ColorFilter;I)V+20
j  androidx.compose.ui.graphics.drawscope.DrawScope$DefaultImpls.drawLine-NGM6Ib0$default(Landroidx/compose/ui/graphics/drawscope/DrawScope;JJJFILandroidx/compose/ui/graphics/PathEffect;FLandroidx/compose/ui/graphics/ColorFilter;IILjava/lang/Object;)V+110
j  androidx.compose.material.TextFieldKt$drawIndicatorLine$1.invoke(Landroidx/compose/ui/graphics/drawscope/DrawScope;)V+66
j  androidx.compose.material.TextFieldKt$drawIndicatorLine$1.invoke(Ljava/lang/Object;)Ljava/lang/Object;+5
j  androidx.compose.ui.draw.DrawBackgroundModifier.draw(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V+11
j  androidx.compose.ui.node.ModifiedDrawNode.performDraw(Landroidx/compose/ui/graphics/Canvas;)V+261
J 1425 c1 androidx.compose.ui.node.LayoutNodeWrapper.draw(Landroidx/compose/ui/graphics/Canvas;)V (71 bytes) @ 0x0000000114253f74 [0x00000001142539a0+0x00000000000005d4]
j  androidx.compose.ui.node.LayoutNodeDrawScope.drawContent()V+39
j  androidx.compose.ui.draw.DrawBackgroundModifier.draw(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V+18
j  androidx.compose.ui.node.ModifiedDrawNode.performDraw(Landroidx/compose/ui/graphics/Canvas;)V+261
j  androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke()V+8
j  androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke()Ljava/lang/Object;+1
J 1400 c1 androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)V (561 bytes) @ 0x0000000114240c1c [0x000000011423f160+0x0000000000001abc]
J 2140 c1 androidx.compose.ui.node.LayoutNodeWrapper.invoke(Landroidx/compose/ui/graphics/Canvas;)V (57 bytes) @ 0x0000000114395f64 [0x00000001143958a0+0x00000000000006c4]
J 2139 c1 androidx.compose.ui.node.LayoutNodeWrapper.invoke(Ljava/lang/Object;)Ljava/lang/Object; (12 bytes) @ 0x000000011439544c [0x0000000114395240+0x000000000000020c]
j  androidx.compose.ui.platform.SkijaLayer.performDrawLayer(Landroidx/compose/ui/graphics/DesktopCanvas;Landroidx/compose/ui/geometry/Rect;)V+180
J 1932 c1 androidx.compose.ui.platform.SkijaLayer.drawLayer(Landroidx/compose/ui/graphics/Canvas;)V (131 bytes) @ 0x000000011433c3ec [0x000000011433bac0+0x000000000000092c]
J 1425 c1 androidx.compose.ui.node.LayoutNodeWrapper.draw(Landroidx/compose/ui/graphics/Canvas;)V (71 bytes) @ 0x0000000114253d2c [0x00000001142539a0+0x000000000000038c]
j  androidx.compose.ui.node.ModifiedLayoutNode.performDraw(Landroidx/compose/ui/graphics/Canvas;)V+11
J 1425 c1 androidx.compose.ui.node.LayoutNodeWrapper.draw(Landroidx/compose/ui/graphics/Canvas;)V (71 bytes) @ 0x0000000114253f74 [0x00000001142539a0+0x00000000000005d4]
J 1434 c1 androidx.compose.ui.node.DelegatingLayoutNodeWrapper.performDraw(Landroidx/compose/ui/graphics/Canvas;)V (15 bytes) @ 0x0000000114257044 [0x0000000114256ca0+0x00000000000003a4]

Interesting fact is that RCX=0x000000013bd2b920: _ZN12SkArenaAlloc9NextBlockEPc+0 in /Users/nike/.skiko/1ceb3153bf4d4bea7858bb7b10bd6771a6e10a50b01c1325711cf59590a1c3f4/libskiko-macos-x64.dylib at 0x000000013bce2000 so could be race in Skia.

We crash here: mov dword [r14+r12], 0x1e as r14 is 0. image

olonho commented 3 years ago

With debug Skia we crash like that

  [libskiko-macos-x64.dylib+0x44c884]  SkRecords::Save* SkRecord::Record::set<SkRecords::Save>(SkRecords::Save*)+0x14
C  [libskiko-macos-x64.dylib+0x44c865]  SkRecords::Save* SkRecord::append<SkRecords::Save>()+0x65
C  [libskiko-macos-x64.dylib+0x4455db]  void SkRecorder::append<SkRecords::Save>()+0x3b
C  [libskiko-macos-x64.dylib+0x445595]  SkRecorder::willSave()+0x15
C  [libskiko-macos-x64.dylib+0x137f6d]  SkCanvas::doSave()+0x1d
C  [libskiko-macos-x64.dylib+0x137f3b]  SkCanvas::checkForDeferredSave()+0x2b
C  [libskiko-macos-x64.dylib+0x13bbc0]  SkCanvas::internalConcat44(SkM44 const&)+0x20
C  [libskiko-macos-x64.dylib+0x13bb84]  SkCanvas::concat(SkM44 const&)+0x24
C  [libskiko-macos-x64.dylib+0x13a490]  SkCanvas::concat(SkMatrix const&)+0x50
C  [libskiko-macos-x64.dylib+0x28092]  Java_org_jetbrains_skija_Canvas__1nConcat+0x62

or like that

C  [libskiko-macos-x64.dylib+0x44d534]  SkRecords::Concat44* SkRecord::Record::set<SkRecords::Concat44>(SkRecords::Concat44*)+0x14
C  [libskiko-macos-x64.dylib+0x44d505]  SkRecords::Concat44* SkRecord::append<SkRecords::Concat44>()+0x65
C  [libskiko-macos-x64.dylib+0x4459bf]  void SkRecorder::append<SkRecords::Concat44, SkM44 const&>(SkM44 const&)+0x3f
C  [libskiko-macos-x64.dylib+0x44596d]  SkRecorder::didConcat44(SkM44 const&)+0x1d
C  [libskiko-macos-x64.dylib+0x13bb98]  SkCanvas::concat(SkM44 const&)+0x38
C  [libskiko-macos-x64.dylib+0x13a490]  SkCanvas::concat(SkMatrix const&)+0x50
C  [libskiko-macos-x64.dylib+0x28092]  Java_org_jetbrains_skija_Canvas__1nConcat+0x62

what generally hints that picture recorder isn't fully thread safe.

igordmn commented 3 years ago

what generally hints that picture recorder isn't fully thread safe.

So, we access the same PictureRecorder from two threads, or two PictureRecorder's access the same shared object?

olonho commented 3 years ago

Seems the reason of crash is reusing of the same Canvas concurrently via shared sharedDrawScope instance in LayoutNode.kt. Most likely it shalln't be a global.

igordmn commented 2 years ago

Should be fixed in 1.0.0-beta5

okushnikov commented 1 month ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.