Coroutines and cancelling blocking code

#1

I have to interact with a blocking library from coroutine code and want to be able to cancel the blocking call. The situation is something similar to the following

import kotlinx.coroutines.*

fun main() {
  val jobA = GlobalScope.launch {
    someSuspendedFun()
    println("Done A")
  }
  val jobB = GlobalScope.launch(Dispatchers.IO) {
    someLibraryBlockingFun()
    println("Done B")
  }

  runBlocking {
    delay(500)
    jobA.cancel()
    jobB.cancel()

    joinAll(jobA, jobB)
  }
}

suspend fun someSuspendedFun() {
  delay(1_000)
}

fun someLibraryBlockingFun() {
  Thread.sleep(1_000)
}

here jobB.cancel() doesn’t work because it will not Thread#interrupt the Thread.sleep(...) call, so Done B gets printed. What should I do in this kind of situation?

Thanks

#2

I thought I should also post what I’m doing right now, which is something similar to this


import kotlinx.coroutines.*
import java.util.concurrent.Executors
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

val exec = Executors.newCachedThreadPool()

fun main() {
  val job = GlobalScope.launch {
    runCancellableBlocking {
      Thread.sleep(1_000)
    }
    println("Done")
  }

  runBlocking {
    delay(500)
    job.cancelAndJoin()
  }

  exec.shutdown()
}

suspend fun <E> runCancellableBlocking(block: () -> E): E {
  return suspendCancellableCoroutine { cont ->
    val f = exec.submit {
      try {
        cont.resume(block())
      }
      catch (e: InterruptedException) {
        cont.cancel()
      }
      catch(e: Exception) {
        cont.resumeWithException(e)
      }
    }
    cont.invokeOnCancellation {
      f.cancel(true)
    }
  }
}

but it doesn’t seem very idiomatic, so I was wondering if there is a better way.