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.
Links
- Watch the full talk on YouTube
- Touchlab
- American Express
- KotlinConf
- JetBrains
- Kotlin Website
- Kotlin/Native Repository
Hashtags: #KevinGalligan #KotlinNative #Concurrency #Touchlab #JetBrains #Multiplatform