CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-kotlinx--kotlinx-coroutines-core

Coroutines support libraries for Kotlin providing structured concurrency primitives, Flow API for reactive streams, channels for communication, and synchronization utilities across all Kotlin platforms

Pending
Overview
Eval results
Files

dispatchers.mddocs/

Dispatchers

Thread management and execution contexts for controlling where coroutines run. Dispatchers provide the threading policy for coroutine execution and are a key component of the coroutine context.

Capabilities

CoroutineDispatcher Base Class

The foundation class for all coroutine dispatchers, providing thread management and execution control.

/**
 * Base class to be extended by all coroutine dispatcher implementations.
 */
abstract class CoroutineDispatcher : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
    /**
     * Dispatches execution of a runnable block onto another thread in the given context.
     * This method should not be used directly.
     */
    abstract fun dispatch(context: CoroutineContext, block: Runnable)
    
    /**
     * Returns true if the execution of the coroutine should be performed with dispatch method.
     * The default behavior for most dispatchers is to return true.
     */
    open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
    
    /**
     * Creates a view of the current dispatcher that limits the parallelism to the given value.
     */
    fun limitedParallelism(parallelism: Int): CoroutineDispatcher
    
    /**
     * Dispatches execution of a block onto another thread, but immediately returns
     * without waiting for the completion.
     */
    operator fun plus(other: CoroutineContext): CoroutineContext
}

Standard Dispatchers

The built-in dispatcher implementations provided by kotlinx-coroutines.

/**
 * Groups various implementations of CoroutineDispatcher.
 */
object Dispatchers {
    /**
     * The default CoroutineDispatcher that is used by all standard builders
     * if no dispatcher is specified in their context.
     */
    val Default: CoroutineDispatcher
    
    /**
     * A coroutine dispatcher that is confined to the Main thread operating with UI objects.
     */
    val Main: MainCoroutineDispatcher
    
    /**
     * A coroutine dispatcher that is not confined to any specific thread.
     */
    val Unconfined: CoroutineDispatcher
    
    /**
     * The IO dispatcher that is designed for offloading blocking IO tasks to a shared pool of threads.
     * Available on JVM and Native targets.
     */
    val IO: CoroutineDispatcher  // Platform-specific availability
}

/**
 * A CoroutineDispatcher for UI components that operate on the main/UI thread.
 */
abstract class MainCoroutineDispatcher : CoroutineDispatcher() {
    /**
     * Returns dispatcher that executes coroutines immediately when it is already
     * in the right context (on main thread) instead of dispatching them.
     */
    abstract val immediate: MainCoroutineDispatcher
}

Usage Examples:

import kotlinx.coroutines.*

val scope = MainScope()

// Default dispatcher - for CPU-intensive work
scope.launch(Dispatchers.Default) {
    val result = heavyComputation()
    println("Computation result: $result")
}

// Main dispatcher - for UI operations (iOS main queue)
scope.launch(Dispatchers.Main) {
    updateUI("Loading...")
    
    // Switch to background for work
    val data = withContext(Dispatchers.Default) {
        fetchAndProcessData()
    }
    
    // Back to main for UI update
    updateUI("Data: $data")
}

// Main.immediate - avoids dispatch if already on main
scope.launch(Dispatchers.Main.immediate) {
    // This runs immediately if already on main thread
    println("Quick UI update")
}

// Unconfined dispatcher - not recommended for general use
scope.launch(Dispatchers.Unconfined) {
    println("Starts in calling thread: ${Thread.currentThread().name}")
    delay(100)
    println("Resumes in different thread: ${Thread.currentThread().name}")
}

// IO dispatcher (JVM/Native specific)
scope.launch(Dispatchers.IO) {
    val fileContent = readFile("data.txt")
    val networkResponse = makeNetworkCall()
    processIOResults(fileContent, networkResponse)
}

Dispatcher Configuration

Customize dispatcher behavior and create limited parallelism views.

/**
 * Creates a view of the current dispatcher that limits the parallelism to the given value.
 * The resulting dispatcher shares threads with the original dispatcher.
 */
fun CoroutineDispatcher.limitedParallelism(parallelism: Int): CoroutineDispatcher

Usage Examples:

import kotlinx.coroutines.*

val scope = MainScope()

// Limit parallelism for resource-constrained operations
val limitedDispatcher = Dispatchers.Default.limitedParallelism(2)

scope.launch {
    // Only 2 coroutines can run concurrently on this dispatcher
    repeat(10) { i ->
        launch(limitedDispatcher) {
            println("Task $i starting on ${Thread.currentThread().name}")
            delay(1000)  // Simulate work
            println("Task $i completed")
        }
    }
}

// Create a dispatcher for database operations (limit to 1 for SQLite)
val databaseDispatcher = Dispatchers.IO.limitedParallelism(1)

suspend fun performDatabaseOperation(operation: String) {
    withContext(databaseDispatcher) {
        println("Performing $operation on ${Thread.currentThread().name}")
        // Database operation here
        delay(100)
    }
}

scope.launch {
    // All database operations will be serialized
    repeat(5) { i ->
        launch {
            performDatabaseOperation("Operation $i")
        }
    }
}

// Network requests with limited concurrency
val networkDispatcher = Dispatchers.IO.limitedParallelism(4)

suspend fun makeNetworkRequest(url: String): String {
    return withContext(networkDispatcher) {
        println("Fetching $url on ${Thread.currentThread().name}")
        delay(500)  // Simulate network delay
        "Response from $url"
    }
}

Context Switching

Change execution context during coroutine execution.

/**
 * Calls the specified suspending block with a given coroutine context,
 * suspends until it completes, and returns the result.
 */
suspend fun <T> withContext(
    context: CoroutineContext,
    block: suspend CoroutineScope.() -> T
): T

Usage Examples:

import kotlinx.coroutines.*

val scope = MainScope()

scope.launch {
    println("Starting on: ${Thread.currentThread().name}")
    
    // Switch to Default dispatcher for CPU work
    val result = withContext(Dispatchers.Default) {
        println("Computing on: ${Thread.currentThread().name}")
        expensiveComputation()
    }
    
    println("Back on: ${Thread.currentThread().name}")
    
    // Switch to Main for UI update
    withContext(Dispatchers.Main) {
        println("Updating UI on: ${Thread.currentThread().name}")
        displayResult(result)
    }
}

// Chain context switches
scope.launch(Dispatchers.Main) {
    val userData = withContext(Dispatchers.IO) {
        // Fetch from network
        fetchUserData()
    }
    
    val processedData = withContext(Dispatchers.Default) {
        // Process data
        processUserData(userData)
    }
    
    // Back to main for UI
    updateUserProfile(processedData)
}

// Preserve context elements while switching dispatcher
scope.launch(Dispatchers.Main + CoroutineName("UserLoader")) {
    val result = withContext(Dispatchers.Default) {
        // CoroutineName is preserved, only dispatcher changes
        println("Processing in coroutine: ${coroutineContext[CoroutineName]?.name}")
        heavyProcessing()
    }
    
    displayResult(result)
}

Platform-Specific Dispatchers

iOS ARM64 specific dispatcher behavior and characteristics.

iOS Main Dispatcher:

// On iOS, Dispatchers.Main is backed by Darwin's main queue
scope.launch(Dispatchers.Main) {
    // Executes on the main queue
    // Safe for UIKit operations
    updateUILabel("Hello iOS")
}

// Main.immediate avoids queue dispatch when already on main
scope.launch(Dispatchers.Main.immediate) {
    // If already on main queue, executes immediately
    // Otherwise dispatches to main queue
    performImmediateUIUpdate()
}

iOS Threading Characteristics:

import kotlinx.coroutines.*

// Default dispatcher uses background queues
scope.launch(Dispatchers.Default) {
    // Uses global concurrent queue with QOS_CLASS_DEFAULT
    println("Running on background thread: ${Thread.currentThread().name}")
}

// For iOS-specific threading needs
suspend fun performIOSSpecificWork() {
    withContext(Dispatchers.Default) {
        // CPU-intensive work on background queue
        val result = processLargeDataSet()
        
        // Switch to main for UI updates
        withContext(Dispatchers.Main) {
            updateProgressIndicator(result.progress)
        }
    }
}

Dispatcher Testing and Debugging

Tools and techniques for testing and debugging dispatcher behavior.

import kotlinx.coroutines.*
import kotlinx.coroutines.test.*

// Testing with TestDispatchers (from kotlinx-coroutines-test)
@Test
fun testWithTestDispatcher() = runTest {
    val testDispatcher = TestCoroutineScheduler()
    
    launch(testDispatcher) {
        delay(1000)
        println("Task completed")
    }
    
    // Advance time in test
    advanceTimeBy(1000)
}

// Debug dispatcher behavior
scope.launch {
    println("Initial context: ${coroutineContext}")
    
    withContext(Dispatchers.Default) {
        println("In Default: ${coroutineContext[CoroutineDispatcher]}")
        
        withContext(Dispatchers.Main) {
            println("In Main: ${coroutineContext[CoroutineDispatcher]}")
        }
    }
}

// Monitor dispatcher performance
fun monitorDispatcherUsage() {
    val dispatcher = Dispatchers.Default.limitedParallelism(2)
    
    repeat(10) { i ->
        scope.launch(dispatcher) {
            val startTime = System.currentTimeMillis()
            println("Task $i started at $startTime")
            
            delay(1000)
            
            val endTime = System.currentTimeMillis()
            println("Task $i finished at $endTime, duration: ${endTime - startTime}ms")
        }
    }
}

Best Practices

Guidelines for effective dispatcher usage in iOS applications.

Choosing the Right Dispatcher:

// UI operations - always use Main
scope.launch(Dispatchers.Main) {
    updateLabel(text)
    animateView()
}

// CPU-intensive work - use Default
scope.launch(Dispatchers.Default) {
    val result = complexCalculation()
    
    // Switch back to Main for UI updates
    withContext(Dispatchers.Main) {
        displayResult(result)
    }
}

// File I/O and network calls - use IO (when available) or limited Default
val ioDispatcher = Dispatchers.Default.limitedParallelism(4)
scope.launch(ioDispatcher) {
    val data = readFromFile()
    val response = makeNetworkCall()
    processResults(data, response)
}

Avoiding Common Pitfalls:

// DON'T: Unnecessary context switching
scope.launch(Dispatchers.Main) {
    withContext(Dispatchers.Main) {  // Redundant
        updateUI()
    }
}

// DO: Use Main.immediate when appropriate
scope.launch(Dispatchers.Main.immediate) {
    updateUI()  // Executes immediately if already on main
}

// DON'T: Blocking operations on Main
scope.launch(Dispatchers.Main) {
    Thread.sleep(1000)  // Blocks main thread - BAD!
}

// DO: Switch context for blocking operations
scope.launch(Dispatchers.Main) {
    val result = withContext(Dispatchers.Default) {
        blockingOperation()  // Runs on background thread
    }
    updateUI(result)  // Back on main thread
}

// DON'T: Excessive context switching
scope.launch {
    withContext(Dispatchers.Default) {
        withContext(Dispatchers.Main) {  // Unnecessary switching
            withContext(Dispatchers.Default) {
                computeValue()
            }
        }
    }
}

// DO: Minimize context switches
scope.launch {
    val result = withContext(Dispatchers.Default) {
        computeValue()
    }
    withContext(Dispatchers.Main) {
        updateUI(result)
    }
}

iOS-Specific Considerations:

// Handle iOS app lifecycle
class iOSCoroutineManager {
    private val scope = MainScope()
    
    fun startBackgroundWork() {
        scope.launch(Dispatchers.Default) {
            while (isActive) {
                performBackgroundTask()
                delay(30000)  // 30 seconds
            }
        }
    }
    
    fun cleanup() {
        scope.cancel()
    }
}

// Coordinate with iOS async APIs
suspend fun bridgeToiOSAPI(): String = suspendCancellableCoroutine { continuation ->
    iOSAsyncFunction { result, error ->
        DispatchQueue.main.async {
            if let error = error {
                continuation.resumeWithException(error)
            } else {
                continuation.resume(result)
            }
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-kotlinx--kotlinx-coroutines-core

docs

channels.md

coroutine-builders.md

dispatchers.md

exception-handling.md

flow-api.md

index.md

jobs-deferreds.md

structured-concurrency.md

synchronization.md

tile.json