Kotlin coroutines library providing comprehensive asynchronous programming support with structured concurrency for iOS x64 platforms
—
Cancellation propagation, exception handling, and timeout management for robust coroutine applications. kotlinx-coroutines provides structured error handling that automatically propagates cancellation and manages exceptions.
Cooperative cancellation mechanism for coroutines.
/**
* Base exception for coroutine cancellation
*/
open class CancellationException : IllegalStateException {
constructor()
constructor(message: String?)
constructor(message: String?, cause: Throwable?)
}
/**
* Check if current coroutine is active
*/
val CoroutineScope.isActive: Boolean
/**
* Ensure coroutine is active, throw CancellationException if cancelled
*/
fun CoroutineScope.ensureActive()
/**
* Yield to other coroutines and check for cancellation
*/
suspend fun yield()Usage Examples:
import kotlinx.coroutines.*
suspend fun cancellableWork() {
repeat(1000) { i ->
// Check for cancellation periodically
ensureActive()
// Or use yield() which also checks cancellation
yield()
// Perform work
processItem(i)
}
}
// Cancelling coroutines
val job = launch {
try {
cancellableWork()
} catch (e: CancellationException) {
println("Work was cancelled")
// Don't suppress CancellationException
throw e
}
}
// Cancel after delay
delay(100)
job.cancel("Timeout reached")Functions for adding timeouts to coroutine operations.
/**
* Execute block with timeout, throw TimeoutCancellationException on timeout
*/
suspend fun <T> withTimeout(timeoutMillis: Long, block: suspend CoroutineScope.() -> T): T
/**
* Execute block with timeout, return null on timeout
*/
suspend fun <T> withTimeoutOrNull(timeoutMillis: Long, block: suspend CoroutineScope.() -> T): T?
/**
* Exception thrown by withTimeout
*/
class TimeoutCancellationException : CancellationException {
val coroutine: Job?
}Usage Examples:
import kotlinx.coroutines.*
// Timeout with exception
try {
val result = withTimeout(1000) {
longRunningOperation()
}
println("Completed: $result")
} catch (e: TimeoutCancellationException) {
println("Operation timed out")
}
// Timeout with null return
val result = withTimeoutOrNull(1000) {
longRunningOperation()
}
if (result != null) {
println("Completed: $result")
} else {
println("Operation timed out")
}
// Network request with timeout
suspend fun fetchDataWithTimeout(url: String): String? {
return withTimeoutOrNull(5000) {
httpClient.get(url)
}
}Structured exception handling in coroutines.
/**
* Exception handler for coroutines
*/
interface CoroutineExceptionHandler : CoroutineContext.Element {
/**
* Handle uncaught exception
*/
fun handleException(context: CoroutineContext, exception: Throwable)
companion object Key : CoroutineContext.Key<CoroutineExceptionHandler>
}
/**
* Create exception handler
*/
fun CoroutineExceptionHandler(handler: (CoroutineContext, Throwable) -> Unit): CoroutineExceptionHandlerUsage Examples:
import kotlinx.coroutines.*
// Global exception handler
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught exception: ${exception.localizedMessage}")
}
// Use with launch (only works with root coroutines)
val scope = CoroutineScope(SupervisorJob() + handler)
scope.launch {
throw RuntimeException("Something went wrong")
}
// Exception handling in async
val deferred = async {
throw RuntimeException("Async error")
}
try {
deferred.await() // Exception thrown here
} catch (e: RuntimeException) {
println("Caught async exception: $e")
}Jobs that don't cancel children when one child fails.
/**
* Create supervisor job where child failures don't affect siblings
*/
fun SupervisorJob(parent: Job? = null): CompletableJob
/**
* Create supervisor scope
*/
suspend fun <R> supervisorScope(block: suspend CoroutineScope.() -> R): RUsage Examples:
import kotlinx.coroutines.*
// Supervisor job - child failures don't cancel siblings
val supervisorJob = SupervisorJob()
val scope = CoroutineScope(supervisorJob + Dispatchers.Default)
// These jobs are independent
scope.launch {
delay(100)
throw RuntimeException("Job 1 failed")
}
scope.launch {
delay(200)
println("Job 2 completed successfully") // Still runs
}
// Supervisor scope
supervisorScope {
launch {
throw RuntimeException("Child 1 failed")
}
launch {
delay(100)
println("Child 2 completed") // Still completes
}
println("Supervisor scope completed")
}Context that prevents cancellation for cleanup operations.
/**
* Non-cancellable job for cleanup operations
*/
object NonCancellable : CoroutineContext.Element, Job {
override val isActive: Boolean get() = true
override val isCompleted: Boolean get() = false
override val isCancelled: Boolean get() = false
// ... other Job methods are no-ops
}Usage Examples:
import kotlinx.coroutines.*
suspend fun operationWithCleanup() {
try {
// Cancellable operation
performWork()
} finally {
// Non-cancellable cleanup
withContext(NonCancellable) {
// This cleanup code will run even if coroutine is cancelled
releaseResources()
saveState()
}
}
}
// File operations with guaranteed cleanup
suspend fun processFile(filename: String) {
val file = openFile(filename)
try {
processFileContent(file)
} finally {
withContext(NonCancellable) {
// Always close file, even if cancelled
file.close()
}
}
}Normal exception handling works in coroutines:
suspend fun handleExceptions() {
try {
val result = riskyOperation()
processResult(result)
} catch (e: NetworkException) {
handleNetworkError(e)
} catch (e: ParseException) {
handleParseError(e)
} finally {
cleanup()
}
}Exceptions in async are stored until await() is called:
suspend fun asyncErrorHandling() {
val deferred1 = async { mayFailOperation1() }
val deferred2 = async { mayFailOperation2() }
// Exceptions thrown here when awaited
try {
val result1 = deferred1.await()
val result2 = deferred2.await()
processResults(result1, result2)
} catch (e: Exception) {
handleError(e)
}
}
// Multiple async with individual error handling
suspend fun individualAsyncErrors() {
val deferred1 = async { mayFailOperation1() }
val deferred2 = async { mayFailOperation2() }
val result1 = try {
deferred1.await()
} catch (e: Exception) {
handleError1(e)
null
}
val result2 = try {
deferred2.await()
} catch (e: Exception) {
handleError2(e)
null
}
processResults(result1, result2)
}Errors in launch propagate to parent unless handled:
// Unhandled exceptions crash the parent
launch {
throw RuntimeException("This will crash parent")
}
// Handle exceptions in launch
launch {
try {
riskyOperation()
} catch (e: Exception) {
handleError(e)
}
}
// Or use exception handler
val handler = CoroutineExceptionHandler { _, exception ->
handleGlobalError(exception)
}
launch(handler) {
throw RuntimeException("Handled by exception handler")
}Implementing retry patterns with exponential backoff:
suspend fun <T> retryWithBackoff(
times: Int = 3,
initialDelay: Long = 100,
maxDelay: Long = 1000,
factor: Double = 2.0,
block: suspend () -> T
): T {
var currentDelay = initialDelay
repeat(times - 1) {
try {
return block()
} catch (e: Exception) {
delay(currentDelay)
currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay)
}
}
return block() // Last attempt
}
// Usage
val result = retryWithBackoff(times = 3) {
fetchDataFromServer()
}class CircuitBreaker(
private val failureThreshold: Int = 5,
private val resetTimeoutMs: Long = 60000
) {
private var failures = 0
private var lastFailureTime = 0L
private var state = State.CLOSED
enum class State { CLOSED, OPEN, HALF_OPEN }
suspend fun <T> execute(block: suspend () -> T): T {
when (state) {
State.OPEN -> {
if (System.currentTimeMillis() - lastFailureTime > resetTimeoutMs) {
state = State.HALF_OPEN
} else {
throw CircuitBreakerOpenException()
}
}
State.HALF_OPEN -> {
// Allow one test call
}
State.CLOSED -> {
// Normal operation
}
}
return try {
val result = block()
onSuccess()
result
} catch (e: Exception) {
onFailure()
throw e
}
}
private fun onSuccess() {
failures = 0
state = State.CLOSED
}
private fun onFailure() {
failures++
lastFailureTime = System.currentTimeMillis()
if (failures >= failureThreshold) {
state = State.OPEN
}
}
}// Wrong - suppresses cancellation
try {
work()
} catch (e: Exception) {
// This catches CancellationException too!
log.error("Error", e)
}
// Correct - let cancellation propagate
try {
work()
} catch (e: CancellationException) {
throw e // Re-throw cancellation
} catch (e: Exception) {
log.error("Error", e)
}
// Or be specific about what you catch
try {
work()
} catch (e: NetworkException) {
handleNetworkError(e)
}// Good - independent tasks
supervisorScope {
launch { backgroundTask1() }
launch { backgroundTask2() }
launch { backgroundTask3() }
}
// Bad - one failure cancels all
coroutineScope {
launch { backgroundTask1() }
launch { backgroundTask2() }
launch { backgroundTask3() }
}// For operations that should timeout
val result = withTimeoutOrNull(5000) {
networkCall()
}
if (result == null) {
// Handle timeout gracefully
showTimeoutMessage()
}
// For critical operations
try {
withTimeout(30000) {
criticalDatabaseUpdate()
}
} catch (e: TimeoutCancellationException) {
// Log and potentially retry
logger.error("Critical operation timed out", e)
throw e
}Exceptions specific to channel operations.
/**
* Exception thrown when attempting to send on a closed channel
*/
class ClosedSendChannelException(message: String? = null) : IllegalStateException(message)
/**
* Exception thrown when attempting to receive from a closed channel
*/
class ClosedReceiveChannelException(message: String? = null) : NoSuchElementException(message)Exceptions related to completion handler execution.
/**
* Exception thrown when a completion handler itself throws an exception
*/
class CompletionHandlerException(
message: String,
cause: Throwable
) : RuntimeException(message, cause)Usage Context:
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
// Channel exception handling
suspend fun handleChannelExceptions() {
val channel = Channel<String>()
channel.close()
try {
channel.send("message")
} catch (e: ClosedSendChannelException) {
println("Cannot send to closed channel: ${e.message}")
}
try {
val message = channel.receive()
} catch (e: ClosedReceiveChannelException) {
println("Cannot receive from closed channel: ${e.message}")
}
}
// Completion handler exception handling
fun handleCompletionExceptions() {
val job = Job()
job.invokeOnCompletion { cause ->
// If this handler throws, it wraps in CompletionHandlerException
throw RuntimeException("Handler error")
}
job.complete()
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlinx--kotlinx-coroutines-core-iosx64