Skip to content

CancellableContinuation doesn't ignore resumeWithException #712

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

Closed
paulblessing opened this issue Oct 16, 2018 · 1 comment
Closed

CancellableContinuation doesn't ignore resumeWithException #712

paulblessing opened this issue Oct 16, 2018 · 1 comment
Labels
docs KDoc and API reference enhancement

Comments

@paulblessing
Copy link

The docs for CancellableContinuation state:

* Invocation of [resume] or [resumeWithException] in _resumed_ state produces [IllegalStateException]
* but is ignored in _cancelled_ state.

The part about "ignored in cancelled state" doesn't seem to be true in practice. Running the below code allows me to catch the JobCancellationException but still prints a stack trace of the IOException thrown.

fun main(args: Array<String>) {
  val client = OkHttpClient()
  val request = Request.Builder()
      .url("http://httpbin.org/get")
      .build()
  val call = client.newCall(request)

  try {
    val inResponse = CountDownLatch(1)
    val continueReading = CountDownLatch(1)

    val job = GlobalScope.launch(Dispatchers.Default) {
      val result: String = try {
        call.await {
          inResponse.countDown()
          continueReading.await()
          throw IOException("IO")
        }
      } catch (e: Exception) {
        "${e.javaClass} was caught"
      }

      println("result is [$result]")
    }

    inResponse.await()
    job.cancel()
    continueReading.countDown()

    Thread.sleep(5000)
  } finally {
    client.dispatcher().executorService().shutdown()
  }
}

suspend fun <T> Call.await(responseConverter: (Response) -> T): T {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      this@await.cancel()
    }

    enqueue(object : Callback {
      override fun onResponse(call: Call, response: Response) {
        try {
          val result = response.use(responseConverter)
          continuation.resume(result)
        } catch (e: Exception) {
          println("Calling resumeWithException with ${e.javaClass}")
          continuation.resumeWithException(e)
        }
      }

      override fun onFailure(call: Call, e: IOException) {
        continuation.resumeWithException(e)
      }
    })
  }
}

Following the path from resumeWithException leads to this code in AbstractContinuation:

is CancelledContinuation -> {
/*
* Continuation is complete, invoke directly.
* NOTE: multiple invokeOnCancellation calls with different handlers are allowed on cancelled continuation.
* It's inconsistent with running continuation, but currently, we have no mechanism to check
* whether any handler was registered during continuation lifecycle without additional overhead.
* This may be changed in the future.
*
* :KLUDGE: We have to invoke a handler in platform-specific way via `invokeIt` extension,
* because we play type tricks on Kotlin/JS and handler is not necessarily a function there
*/
handler.invokeIt((state as? CompletedExceptionally)?.cause)

Are the docs incorrect? Am I doing something wrong? I see in https://github.com/gildor/kotlin-coroutines-okhttp/blob/master/src/main/kotlin/ru/gildor/coroutines/okhttp/CallAwait.kt#L23 they check cancellation before calling resumeWithException. Is that something that always needs to be done?

@elizarov
Copy link
Contributor

The docs are incorrect. Whether it has to be done depends on on what kind of API you are integrating with. In case of Retrofit, it invoke callback when you cancel it and reports IOException, so this check is needed. We'll improve docs and we also plan to provide some better ways to do in the future.

qwwdfsad added a commit that referenced this issue Nov 6, 2018
@qwwdfsad qwwdfsad added the docs KDoc and API reference label Nov 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs KDoc and API reference enhancement
Projects
None yet
Development

No branches or pull requests

3 participants