Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Double "place" and "measure" calls in specific setting with background threads #1548

Closed
ialokim opened this issue Dec 6, 2021 · 11 comments
Closed
Assignees
Labels
Milestone

Comments

@ialokim
Copy link

ialokim commented Dec 6, 2021

While trying to move our code from 1.0.0-beta5 to 1.0.0, we (eventually) face the following (rather mysterious) exception. Unfortunately, I was unable to find a small reproducer yet that does not use our code under the hood and I'm thus not sure if it is a problem on our side that became only apparent after the changes in Compose or if it is actually a regression in Compose.

Just to provide some background on when this exception has been encountered: All our database calls are executed on a single separate thread to avoid concurrency issues in the database (this design is inherited from a part we use under the hood, so it cannot be easily changed). When selecting an item in a list, a State object is updated whose value x is passed to a second composable function. This function uses LaunchedEffect(x) to retrieve data from the database dependent on x in two different callbacks executed on the DB thread. The data (a text and a list) is saved in other MutableState objects from within the callbacks and displayed in Composables, a Text and a LazyColumn containing Row { Text() } or Box { Text() }. The exception occurs (sometimes) when switching to a different item in the first list. Interestingly, when removing the Row or the Box in the (second) list items, I was unable to trigger the exception.

I also tried to find out starting from which Compose version the problem first arises: It is still working fine with 1.0.0-beta6-dev464 but crashes with 1.0.0-beta6-dev474. Unfortunately, there is a rather large changeset between those two versions (see here and especially here). As the stacktrace and exception look rather mysterious to me, I tried to debug the code (setting a breakpoint to inspect the variables just before the exception is thrown) to find out which part of the layout is actually tried to be recomposed, but I couldn't find this information in the call stack. Is there some way to debug internal Compose calls to (e.g.) find out what is "placed" and "measured" twice? Or does someone have an idea what could cause the problem? Might it perhaps be related to changes in the pointer events API between those two compose versions?

The exception(s):

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Place was called on a node which was placed already
	at androidx.compose.ui.node.LayoutNode.onNodePlaced$ui(LayoutNode.kt:906)
	at androidx.compose.ui.node.InnerPlaceable.placeAt-f8xVGno(InnerPlaceable.kt:126)
	at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
	at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative(Placeable.kt:359)
	at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative$default(Placeable.kt:179)
	at androidx.compose.foundation.layout.SizeModifier$measure$1.invoke(Size.kt:783)
	at androidx.compose.foundation.layout.SizeModifier$measure$1.invoke(Size.kt:782)
	at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.kt:68)
	at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno(DelegatingLayoutNodeWrapper.kt:113)
	at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
	at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative(Placeable.kt:359)
	at androidx.compose.ui.layout.Placeable$PlacementScope.placeRelative$default(Placeable.kt:179)
	at androidx.compose.foundation.layout.FillModifier$measure$1.invoke(Size.kt:663)
	at androidx.compose.foundation.layout.FillModifier$measure$1.invoke(Size.kt:662)
	at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.kt:68)
	at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.placeAt-f8xVGno(DelegatingLayoutNodeWrapper.kt:113)
	at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
	at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.kt:370)
	at androidx.compose.ui.node.OuterMeasurablePlaceable.placeOuterWrapper-f8xVGno(OuterMeasurablePlaceable.kt:171)
	at androidx.compose.ui.node.OuterMeasurablePlaceable.access$placeOuterWrapper-f8xVGno(OuterMeasurablePlaceable.kt:28)
	at androidx.compose.ui.node.OuterMeasurablePlaceable$placeAt$1.invoke(OuterMeasurablePlaceable.kt:159)
	at androidx.compose.ui.node.OuterMeasurablePlaceable$placeAt$1.invoke(OuterMeasurablePlaceable.kt:158)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:126)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:88)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutModifierSnapshotReads$ui(OwnerSnapshotObserver.kt:69)
	at androidx.compose.ui.node.OuterMeasurablePlaceable.placeAt-f8xVGno(OuterMeasurablePlaceable.kt:158)
	at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
	at androidx.compose.ui.layout.Placeable$PlacementScope.place(Placeable.kt:367)
	at androidx.compose.ui.layout.Placeable$PlacementScope.place$default(Placeable.kt:191)
	at androidx.compose.material.ScaffoldKt$ScaffoldLayout$1$1$1.invoke(Scaffold.kt:325)
	at androidx.compose.material.ScaffoldKt$ScaffoldLayout$1$1$1.invoke(Scaffold.kt:241)
	at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.kt:68)
	at androidx.compose.ui.layout.SubcomposeLayoutState$createMeasurePolicy$1$measure$1.placeChildren(SubcomposeLayout.kt:367)
	at androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke(LayoutNode.kt:943)
	at androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke(LayoutNode.kt:933)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:126)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:88)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutSnapshotReads$ui(OwnerSnapshotObserver.kt:62)
	at androidx.compose.ui.node.LayoutNode.layoutChildren$ui(LayoutNode.kt:933)
	at androidx.compose.ui.node.LayoutNode.onNodePlaced$ui(LayoutNode.kt:919)
	at androidx.compose.ui.node.InnerPlaceable.placeAt-f8xVGno(InnerPlaceable.kt:126)
	at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:31)
	at androidx.compose.ui.layout.Placeable$PlacementScope.place-70tqf50(Placeable.kt:370)
	at androidx.compose.ui.node.OuterMeasurablePlaceable.placeOuterWrapper-f8xVGno(OuterMeasurablePlaceable.kt:171)
	at androidx.compose.ui.node.OuterMeasurablePlaceable.access$placeOuterWrapper-f8xVGno(OuterMeasurablePlaceable.kt:28)
	at androidx.compose.ui.node.OuterMeasurablePlaceable$placeAt$1.invoke(OuterMeasurablePlaceable.kt:159)
	at androidx.compose.ui.node.OuterMeasurablePlaceable$placeAt$1.invoke(OuterMeasurablePlaceable.kt:158)
	at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:1798)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:121)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:88)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeLayoutModifierSnapshotReads$ui(OwnerSnapshotObserver.kt:69)
	at androidx.compose.ui.node.OuterMeasurablePlaceable.placeAt-f8xVGno(OuterMeasurablePlaceable.kt:158)
	at androidx.compose.ui.node.OuterMeasurablePlaceable.replace(OuterMeasurablePlaceable.kt:183)
	at androidx.compose.ui.node.LayoutNode.replace$ui(LayoutNode.kt:825)
	at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:238)
	at androidx.compose.ui.node.MeasureAndLayoutDelegate.access$remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:38)
	at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:205)
	at androidx.compose.ui.platform.SkiaBasedOwner.measureAndLayout(SkiaBasedOwner.skiko.kt:276)
	at androidx.compose.ui.node.Owner$DefaultImpls.measureAndLayout$default(Owner.kt:182)
	at androidx.compose.ui.platform.SkiaBasedOwner.render(SkiaBasedOwner.skiko.kt:242)
	at androidx.compose.ui.ComposeScene.render(ComposeScene.skiko.kt:346)
	at androidx.compose.ui.awt.ComposeLayer$1.onRender(ComposeLayer.desktop.kt:194)
	at org.jetbrains.skiko.SkiaLayer.update$skiko(SkiaLayer.jvm.kt:435)
	at org.jetbrains.skiko.redrawer.LinuxOpenGLRedrawer.update(LinuxOpenGLRedrawer.kt:86)
	at org.jetbrains.skiko.redrawer.LinuxOpenGLRedrawer.access$update(LinuxOpenGLRedrawer.kt:9)
	at org.jetbrains.skiko.redrawer.LinuxOpenGLRedrawer$Companion$frameDispatcher$1.invokeSuspend(LinuxOpenGLRedrawer.kt:112)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: measure() may not be called multiple times on the same Measurable
	at androidx.compose.ui.node.OuterMeasurablePlaceable.remeasure-BRTryo0(OuterMeasurablePlaceable.kt:86)
	at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui(LayoutNode.kt:1254)
	at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui$default(LayoutNode.kt:1250)
	at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure(MeasureAndLayoutDelegate.kt:174)
	at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:232)
	at androidx.compose.ui.node.MeasureAndLayoutDelegate.access$remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:38)
	at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:205)
	at androidx.compose.ui.platform.SkiaBasedOwner.measureAndLayout(SkiaBasedOwner.skiko.kt:276)
	at androidx.compose.ui.node.Owner$DefaultImpls.measureAndLayout$default(Owner.kt:182)
	at androidx.compose.ui.platform.SkiaBasedOwner.processPointerInput-8iAsVTc$ui(SkiaBasedOwner.skiko.kt:385)
	at androidx.compose.ui.ComposeScene.sendPointerEvent-Kr8mkKM(ComposeScene.skiko.kt:419)
	at androidx.compose.ui.ComposeScene.sendPointerEvent-Kr8mkKM$default(ComposeScene.skiko.kt:383)
	at androidx.compose.ui.awt.ComposeLayer_desktopKt.onMouseEvent(ComposeLayer.desktop.kt:318)
	at androidx.compose.ui.awt.ComposeLayer_desktopKt.access$onMouseEvent(ComposeLayer.desktop.kt:1)
	at androidx.compose.ui.awt.ComposeLayer$4$mouseMoved$1.invoke(ComposeLayer.desktop.kt:240)
	at androidx.compose.ui.awt.ComposeLayer$4$mouseMoved$1.invoke(ComposeLayer.desktop.kt:239)
	at androidx.compose.ui.awt.AWTDebounceEventQueue$job$1.invokeSuspend(AWTDebounceEventQueue.desktop.kt:59)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
@HuixingWong
Copy link

+1

@akurasov akurasov added the AK label Dec 7, 2021
@akurasov akurasov added desktop and removed AK labels Dec 7, 2021
@igordmn
Copy link
Collaborator

igordmn commented Dec 7, 2021

Probably it is a commonMain issue: https://issuetracker.google.com/issues/208855077.

Will check the example there

@igordmn igordmn added the crash label Dec 7, 2021
@ialokim
Copy link
Author

ialokim commented Dec 12, 2021

Apparently upstream fixed their issues with https://android-review.googlesource.com/c/platform/frameworks/support/+/1914851/. Would be nice to have a new version of compose MP including these fixes soon.

@akurasov akurasov added this to the 1.1 milestone Dec 13, 2021
@akurasov akurasov added the p:high High priority label Dec 13, 2021
@akurasov akurasov modified the milestones: 1.1, 1.0.1 Dec 13, 2021
@HuixingWong
Copy link

When will 1.01 be released ?

@akurasov
Copy link
Contributor

There is no specific scheduled date yet. Does this issue affect your project severely?

@HuixingWong
Copy link

Thanks,I used a workaround so it's not very serious , But I hope to see the new version soon😄

@ialokim
Copy link
Author

ialokim commented Dec 13, 2021

In our case we just sticked to the last version of Compose that didn't expose this bug. It would be nice to be able to upgrade to a stable version, but we have no particular hurry until, let's say, the first week of January.

@akurasov
Copy link
Contributor

We have created intermediate dev build - 1.0.1-rc1. It contains the fix from the upstream, you could check if it solves your issues.
Please note, that it works with Kotlin 1.6.10-RC and is available only on
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") (plugin is available on GPP)

@akurasov akurasov self-assigned this Dec 14, 2021
@HuixingWong
Copy link

I removed the workaround and It works well, Thanks !

@akurasov
Copy link
Contributor

Fixed in 1.0.1-rc1+

@ialokim
Copy link
Author

ialokim commented Dec 14, 2021

Just tried the new dev version and it works like a charm! Thanks a lot for your fast reaction, that's amazing! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants