question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Crash: Crossinline modifier with delay function call causes IllegalStateException on Android

See original GitHub issue

Hey guys!

I’ve encountered a weird crash caused when using the delay function, inside a suspend lambda, passed down to a launch coroutine builder.

My use-case is as follows:

Inside my BaseViewModel (business logic layer), I’ve got an execute function as follows:

protected fun execute(crossinline action: suspend () -> Unit) {
        launch(main) { action() }
}

I use it whenever I have some work to do, which involves coroutines. This way I can hide the launch with the main coroutineContext in the BaseViewModel, while still using suspension…

A sample case of using this would be next:

execute {
    val data = getData { getTopRatedGamesUseCase(page) }
...
}

Where the getData is another base suspend function, which does some work in the background and returns the data.

However, if I proceed with using delay in the getData function, I receive the following error:

java.lang.IllegalStateException: call to 'resume' before 'invoke' with coroutine.

The delay is used for some form of custom retry logic. I’ve pinpointed this error to delay at first, but then, out of curiosity, I’ve tried to postpone the work in retry blocks with Thread.sleep, and the code worked.

Then I tried forcing the launch call, ignoring the execute function, and it worked as well.

Finally I’ve tried removing the crossinline modifier on the action parameter of execute, and the code worked.

I’m not 100% sure why this happens, I realise it’s wanted behaviour, when a parent Job is finished, that a child return from a suspension function will result in an exception. However, not sure if I’m using coroutines in a bad way, the crossinline modifier is the true culprit (some internal bug), or the launch implementation has some un-handled case.

Thanks in advance!

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:2
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
filbabiccommented, Sep 6, 2018

Hey!

This code is sufficient to produce the error:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        execute {
            val crashMessage: String = getData { getSomeString() }

            println(crashMessage)
        }
    }

    private inline fun execute(crossinline action: suspend () -> Unit) {
        launch(UI) { action() }
    }

    private suspend fun <T> getData(dataProvider: suspend () -> T): T = withContext(CommonPool) { dataProvider() }

    private suspend fun getSomeString(): String {
        delay(5, TimeUnit.SECONDS)

        return "Hello crash"
    }
}

Hopefully this clarifies things a bit.

I use the execute and getData functions from my base layer, so that I can simply abstract away the UI and CommonPool contexts, leaving the top implementation unaware of scheduling/threading.

It’s usually a larger chain of functions, through different architecture layers, but the example is good enough. If I try to delay a coroutine, from inside another suspend function, which has been inlined into a launch block, the crash occurs.

The stack trace, when running the top code is as follows:

Process: com.babic.filip.coroutinescrashsample, PID: 5360
    java.lang.IllegalStateException: call to 'resume' before 'invoke' with coroutine
        at com.babic.filip.coroutinescrashsample.MainActivity$onCreate$$inlined$execute$1$lambda$1.doResume(MainActivity.kt:19)
        at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:42)
        at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:41)
        at kotlinx.coroutines.experimental.DispatchedTask$DefaultImpls.run(Dispatched.kt:149)
        at kotlinx.coroutines.experimental.AbstractContinuation.run(AbstractContinuation.kt:19)
        at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1412)
        at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:285)
        at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1152)
        at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1990)
        at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1938)
        at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

I won’t be attaching a project, since I literally opened a new project here, and wrote the code from the first snippet.

0reactions
TAGCcommented, Nov 1, 2018

Yeah I’m seeing something like this too for this code:

private suspend inline fun Iterable<Node>.whileActive(crossinline block: suspend () -> Unit) = coroutineScope {
    val executions = map { async { it.execute() } }
    block()
    executions.awaitAll()
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

get java.lang.IllegalStateException error on google play crash ...
Using custom animation can cause an error. That's why I asked you in the comments section about animation. because a month ago I...
Read more >
Compose UI - Android Developers
Build animations in their Jetpack Compose applications to enrich the user experience. compose.compiler, Transform @Composable functions and enable optimizations ...
Read more >
Diff - HEAD^! - platform/external/kotlinx.coroutines - Google Git
+* Fixed the POM of `kotlinx-coroutines-debug` having an incorrect reference to `kotlinx-coroutines-bom`, which cause the builds of Maven ...
Read more >
Kotlin Language Documentation
Basecamp's Android app is 100% Kotlin code, and they report a huge di erence in ... Coroutines are supported through suspending functions: a...
Read more >
Kotlin for Android App Development - DOKUMEN.PUB
infix modifier for the function. This allows you to call this function as shown in. Listing 2.27, with the function name between the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found