compose-multiplatform icon indicating copy to clipboard operation
compose-multiplatform copied to clipboard

Resizing window leads to threading issue

Open ypujante opened this issue 4 years ago • 16 comments

I have the following test code to demonstrate the issue. Running on macOS 10.14.6 with compose 0.3.0 / kotlin 1.4.30

@ExperimentalTime
fun launchTimer(period: Duration, block: suspend CoroutineScope.() -> Unit): Job {
    return GlobalScope.launch(Dispatchers.Main) {
        while (isActive) {
            delay(period)
            if (isActive)
                block(this)
        }
    }
}

var displayTimer: Job? = null
var start: Long = System.currentTimeMillis()

@ExperimentalTime
@Composable
fun TestResize() {

    val started = remember { mutableStateOf(false) }
    val duration = remember { mutableStateOf("N/A")}

    Column {
        if (!started.value) {
            Button(onClick = {
                started.value = true
                start = System.currentTimeMillis()
                displayTimer = launchTimer(250.milliseconds) {
                    val now = System.currentTimeMillis()
                    duration.value = "${now - start}"
                }
            }) {
                Text("Click to start")
            }
        }
        else
        {
            Text(duration.value)
            Button(onClick = {}) { Text("Test button...")}
        }
    }
}

This small test displays a button to start a timer. After the timer is started, it simply displays the time elapsed since timer was started (in ms) and a dummy button that further demonstrates the issue.

If you resize the window (the number of time the window needs to be resized to trigger the issue is random which seems to point to a race condition sort of bug), then the main UI thread stops refreshing entirely.

This screenshot even captures the button animation stopped in its tracks... The UI is not refreshing anymore

Screen Shot 2021-03-08 at 10 42 28

Although a random issue, it is actually very easy to reproduce...

ypujante avatar Mar 08 '21 18:03 ypujante

Working on another project, I recorded the behavior in action. Clicking on the button does nothing and it requires resizing the window to make anything happen. You can see the animation running as the window is resized...

thread-issue

ypujante avatar Mar 11 '21 15:03 ypujante

We encountered this problem before and thought that we had fixed it.

I was able to reproduce this error again on this example (but only in 2 of 60 runs :) ).

Our OpenGL renderer have some issues which we should fix when we implement Metal renderer.

We are already implementing it and it looks very promising. This issue will most likely be resolved.

igordmn avatar Mar 12 '21 15:03 igordmn

@igordmn is it feasible to fix in OpenGL renderer as well?

olonho avatar Mar 15 '21 06:03 olonho

@igordmn is it feasible to fix in OpenGL renderer as well?

I think it is hard, because OpenGL implementation on macOs has many issues.

OpenGL on macOs is deprecated, maybe we will keep it only for debug purposes?

igordmn avatar Mar 15 '21 08:03 igordmn

FWIW: I was able to reproduce a nearly identical behavior (completely different code) on Linux. This happened after an exception was thrown while DesktopOwners.onFrame was on the stack. I might speculate that the exception caused the thread/coroutine which was driving the fames to be killed, and then subsequent updates only occur when the window is resized which triggers a repaint as driven from a different source.

jimgoog avatar Mar 16 '21 02:03 jimgoog

I might speculate that the exception caused the thread/coroutine which was driving the fames to be killed

I think you are right, it is because of that. But if we don't kill the coroutine then we can spam the console output with exceptions (there will be an exception every 16ms if we show an animation). And the state of the window will be inconsistent.

User can close the window though and open it again.

igordmn avatar Mar 16 '21 03:03 igordmn

I do not have any indication that an exception is raised and this is what triggers the problem in my case. If there is an exception, it is definitely not in my code. Is there something I can add to display when such an event occurs? Or a breakpoint I can put in the code somewhere (in the compose code)?

ypujante avatar Mar 16 '21 13:03 ypujante

Is the issue exist in 0.4.0-build175? We switched to Metal renderer in this version.

igordmn avatar Mar 25 '21 12:03 igordmn

I just wanted to note that with M4 this problem still exists.

ypujante avatar Jun 08 '21 17:06 ypujante

Hmm, what value will be printed in this snippet?

import androidx.compose.desktop.LocalAppWindow
import androidx.compose.desktop.Window

fun main() {
    Window {
        println(LocalAppWindow.current.window.renderApi)
    }
}

If it is Metal then somewhere we have a different bug.

igordmn avatar Jun 08 '21 17:06 igordmn

It says "METAL" if I copy/paste your code

That being said:

a) I am using the new experimental Window API (androidx.compose.ui.window.Window) b) it is definitely less frequent and harder to reproduce, but I saw it several times this morning while working on some new features

ypujante avatar Jun 08 '21 17:06 ypujante

Are there any errors in the console when rendering stops?

We have another issue regarding that

igordmn avatar Jun 08 '21 17:06 igordmn

No there are none unfortunately. I was really hoping to see something too...

ypujante avatar Jun 08 '21 17:06 ypujante

Still the first reproducer should trigger the issue, or there is a new one?

igordmn avatar Jun 08 '21 17:06 igordmn

It's a new app I am working on and does not use GlobalScope.

ypujante avatar Jun 08 '21 17:06 ypujante

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

okushnikov avatar Aug 26 '24 17:08 okushnikov