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

The coroutine states in the debugger are inaccurate when starts are undispatched #4058

Open
dkhalanskyjb opened this issue Mar 6, 2024 · 1 comment

Comments

@dkhalanskyjb
Copy link
Collaborator

dkhalanskyjb commented Mar 6, 2024

Undispatched starts of coroutines (with CoroutineStart.UNDISPATCHED or unconfined dispatchers (sorry, I myself got confused)) confuse the debugger.

fun weWantToUseSteppingInThisFunction() {
  println("A")
  delay(100)
  println("B")
}

runBlocking {
  launch(start = CoroutineStart.UNDISPATCHED) {
    weWantToUseSteppingInThisFunction()
  }
  println("C")
}

At point A, we'll see that the outer coroutine is considered to be running, whereas the inner coroutine is only "created" and doesn't even have a stacktrace associated with it.

This is not that big of an issue when browsing the list of running coroutines: we'll see that someone executes A, it's not a big deal that the slightly incorrect coroutine is doing that. When introducing stepping, this becomes a problem: given a breakpoint at A, we need a reliable way to say "run to B in the same coroutine." When the coroutine machinery thinks that A is executed by the outer coroutine, this becomes impossible.

Previous discussion: #3987 (comment)

@dkhalanskyjb dkhalanskyjb changed the title The coroutine states in the debugger are strange The coroutine states in the debugger are inaccurate when starts are undispatched Mar 6, 2024
@qwwdfsad qwwdfsad added the debug label Mar 6, 2024
@Kotlin Kotlin deleted a comment from Ahmed30mara2018 Mar 6, 2024
@qwwdfsad
Copy link
Member

qwwdfsad commented Mar 6, 2024

I am not exactly sure what the desired behaviour is.

First things first, let's figure out "undispatched" coroutines behaviour:

suspend fun weWantToUseSteppingInThisFunction() {
    println("A")
    DebugProbes.dumpCoroutinesInfo().map {
        println("State: ${it.state}")
    }
    delay(100)
    println("B")
}

launch(start = CoroutineStart.UNDISPATCHED) { // or launch(Dispatchers.Unconfined)
    weWantToUseSteppingInThisFunction()
}

For the UNDISPATCHED case, it will print

A
State: RUNNING
State: CREATED

while for UNCONFINED it's

State: RUNNING
State: RUNNING

No matter the nesting statuses decision, the UNDISPATCHED behaviour is clearly a bug -- there shouldn't be any reasonable situations when we are within coroutine body but consider coroutine non-running.
The fix is most likely straightforward -- startDirect rightfully notifies the debugger about the creating, but not about the actual start, and it also bypasses coroutine intrinsics that could've done so for it. We can change it by notifying it right in the same place.

When the coroutine machinery thinks that A is executed by the outer coroutine, this becomes impossible.

If this is the root cause of our stepping problem, then we can agree this is a bug and fix it. But, if the problem lies in the fact that we have two nested running coroutines in the same thread, then this is uncharted territory where we'll have to find some solution because, depending on the angle you are looking at that, the outer coroutine might or might not be RUNNING and might or might not be SUSPENDED (note: transition to SUSPENDED is not a side-effect free action and also can confuse users when they expect outer coroutine not to suspend)

dkhalanskyjb added a commit that referenced this issue Apr 3, 2024
Fixes #4058

Additional small fix: the coroutine context injected by `probeCoroutineCreated` is used for `CoroutineStart.UNDISPATCHED` even before the first suspension.
Corje pushed a commit that referenced this issue May 10, 2024
Fixes #4058

Additional small fix: the coroutine context injected by `probeCoroutineCreated` is used for `CoroutineStart.UNDISPATCHED` even before the first suspension.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants