Search
Calendar
May 2025
S M T W T F S
« Apr    
 123
45678910
11121314151617
18192021222324
25262728293031
Archives

PostHeaderIcon Kotlin Native Concurrency Explained by Kevin Galligan

Navigating Kotlin/Native’s Concurrency Model

At KotlinConf 2019 in Copenhagen, Kevin Galligan, a partner at Touchlab with over 20 years of software development experience, delivered a 39-minute talk on Kotlin/Native’s concurrency model. Kevin Galligan explored the restrictive yet logical rules governing state and concurrency in Kotlin/Native, addressing their controversy among JVM and mobile developers. He explained the model’s mechanics, its rationale, and best practices for multiplatform development. This post covers four key themes: the core rules of Kotlin/Native concurrency, the role of workers, the impact of freezing state, and the introduction of multi-threaded coroutines.

Core Rules of Kotlin/Native Concurrency

Kevin Galligan began by outlining Kotlin/Native’s two fundamental concurrency rules: mutable state is confined to a single thread, and immutable state can be shared across multiple threads. These rules, known as thread confinement, mirror mobile development practices where UI updates are restricted to the main thread. In Kotlin/Native, the runtime enforces these constraints, preventing mutable state changes from background threads to avoid race conditions. Kevin emphasized that while these rules feel restrictive compared to the JVM’s shared-memory model, they align with modern platforms like Go and Rust, which also limit unrestricted shared state.

The rationale behind this model, as Kevin explained, is to reduce concurrency errors by design. Unlike the JVM, which trusts developers to manage synchronization, Kotlin/Native’s runtime verifies state access at runtime, crashing if rules are violated. This strictness, though initially frustrating, encourages intentional state management. Kevin noted that after a year of working with Kotlin/Native, he found the model simple and effective, provided developers embrace its constraints rather than fight them.

Workers as Concurrency Primitives

A central concept in Kevin’s talk was the Worker, a Kotlin/Native concurrency queue similar to Java’s ExecutorService or Android’s Handler and Looper. Workers manage a job queue processed by a private thread, ensuring thread confinement. Kevin illustrated how a Worker executes tasks via the execute function, which takes a producer function to verify state transfer between threads. The execute function supports safe and unsafe transfer modes, with Kevin strongly advising against the unsafe mode due to its bypassing of state checks.

Using a code example, Kevin demonstrated passing a data class to a Worker. The runtime freezes the data—making it immutable—to comply with concurrency rules, preventing illegal state transfers. He highlighted that while Worker is a core primitive, developers rarely use it directly, as higher-level abstractions like coroutines are preferred. However, understanding Worker is crucial for grasping Kotlin/Native’s concurrency mechanics, especially when debugging state-related errors like IllegalStateTransfer.

Freezing State and Its Implications

Kevin Galligan delved into the concept of freezing, a runtime mechanism that designates objects as immutable for safe sharing across threads. Freezing is a one-way operation, recursively applying to an object and its references, with no unfreeze option. This ensures thread safety but introduces challenges, as frozen objects cannot be mutated, leading to InvalidMutabilityException errors if attempted.

In a practical example, Kevin showed how capturing mutable state in a background task can inadvertently freeze an entire object graph, causing runtime failures. He introduced tools like ensureNeverFrozen to debug unintended freezing and stressed intentional mutability—keeping mutable state local to one thread and transforming data into frozen copies for sharing. Kevin also discussed Atomic types, which allow limited mutation of frozen state, but cautioned against overusing them due to performance and memory issues. His experience at Touchlab revealed early missteps with global state and Atomics, leading to a shift toward confined state models.

Multi-Threaded Coroutines and Future Directions

A significant update in Kevin’s talk was the introduction of multi-threaded coroutines, enabled by a draft pull request in 2019. Previously, Kotlin/Native coroutines were single-threaded, limiting concurrency and stunting library development. The new model allows coroutines to switch threads using dispatchers, with data passed between threads frozen to maintain strict mode. Kevin demonstrated replacing a custom background function with a coroutine-based approach, simplifying concurrency while adhering to state rules.

This development clarified the longevity of strict mode, countering speculation about a relaxed mode that would mimic JVM-style shared memory. Kevin noted that multi-threaded coroutines unblocked library development, citing projects like AtomicFu and SQLDelight. He also highlighted Touchlab’s Droidcon app, which adopted multi-threaded coroutines for production, showcasing their practical viability. Looking forward, Kevin anticipated increased community adoption and library growth in 2020, urging developers to explore the model despite its learning curve.

Conclusion

Kevin Galligan’s KotlinConf 2019 talk demystifies Kotlin/Native’s concurrency model, offering a clear path for developers navigating its strict rules. By embracing thread confinement, leveraging workers, managing frozen state, and adopting multi-threaded coroutines, developers can build robust multiplatform applications. This talk is a must for Kotlin/Native enthusiasts seeking to master concurrency in modern mobile development.

Hashtags: #KevinGalligan #KotlinNative #Concurrency #Touchlab #JetBrains #Multiplatform

Leave a Reply