Cancel blocking function after timeout without 'select'

I think it would be more flexible to not close the resource on timeout, but on cancellation. There may be more reasons to stop the blocking operation, not only timeout.

Utility could look something like:

suspend fun <T : Closeable, R> T.closeOnCancel(block: suspend (T) -> R): R = coroutineScope {
    try {
        async { block(this@closeOnCancel) }.await()
    } catch (e: CancellationException) {
        close()
        throw e
    }
}

Then we can use it like this:

withContext(Dispatchers.IO) {
    val server = ServerSocket(2000)

    val sock = withTimeout(5000) {
        server.closeOnCancel {
            server.accept()
        }
    }

    val bytes = withTimeout(5000) {
        sock.closeOnCancel {
            sock.getInputStream().readNBytes(4)
        }
    }

    println(bytes.contentToString())
}

This is just an example/POC, it may need some improvements.

Also, I think you should not be afraid of using experimental features of coroutines. There are a lot components that are marked as experimental, but in fact they are pretty much stable.

2 Likes