# Litmus-Testing Kotlin's Many Memory Models

DevFeed: [Litmus-Testing Kotlin's Many Memory Models](<https://devfeed.tech/articles/litmus-testing-kotlin-s-many-memory-models-20946.md>)

Original publisher: [Read original article](<https://jakewharton.com/litmus-testing-kotlins-many-memory-models/>)

Published: 2020-04-08T00:00:00Z

Content type: article

Language: en

Sources: [Jake Wharton](<https://devfeed.tech/sources/jake-wharton.md>)

Topics: [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [Kotlin Multiplatform](<https://devfeed.tech/topics/kotlin-multiplatform.md>), [Data structures](<https://devfeed.tech/topics/data-structures.md>), [Concurrency](<https://devfeed.tech/topics/concurrency.md>), [Testing](<https://devfeed.tech/topics/testing.md>)

Tags: [kotlin](<https://devfeed.tech/tags/kotlin.md>), [kotlin-multiplatform](<https://devfeed.tech/tags/kotlin-multiplatform.md>), [main-thread](<https://devfeed.tech/tags/main-thread.md>), [memory](<https://devfeed.tech/tags/memory.md>), [tests](<https://devfeed.tech/tags/tests.md>), [thread](<https://devfeed.tech/tags/thread.md>)

## AI overview

The article examines how Kotlin's JavaScript, JVM, and native compiler backends implement different memory models. It explains a Kotlin/Native issue caused by sharing an unannotated empty array across threads and recommends running tests on both the main and background threads to prevent regressions.

## Source excerpt

When writing multiplatform code, Kotlin's three compiler backends each have different memory models which must be considered. JavaScript is single-threaded so you really can do no wrong. The JVM model is arguably too permissive where you can do incorrect things and have them work 99.9% of the time. When targeting native, Kotlin enforces some invariants which helps prevent you from those 0.1% bugs that crop up in the JVM. I've been porting the AndroidX collection library to Kotlin multiplatform to experiment with binary compatibility, performance, tooling, and the different memory models. The library consists of mutable, single-threaded data structures. This should mean the different memory models never come into play. But weirdly they do, and let's look at how. On Deck The Kotlin standard library contains general-purpose collections like lists, sets, and maps in both mutable and read-only form. Kotlin 1.3.70 added another collection, ArrayDeque, a "double-ended queue" for efficient stacks and queues. During the 1.3.70 EAP, Kevin Galligan opened an issue where ArrayDeque could only be instantiated on the main thread and not a background thread when targeting Kotlin/Native. At the time I didn't read into it, but as I was porting these collections it came to mind. The underlying cause was that the implementation relied on a top-level val for a shared, empty array when the collection was empty. Arrays are fixed-length, so an empty array is effectively immutable and thus can be shared by all empty collections. But that seems fine? It is fine for Kotlin/JS and Kotlin/JVM but Kotlin/Native is different here. By default, Kotlin/Native only allows the main thread to access top-level vals. If you want to access the value from multiple threads (potentially concurrently) you must choose whether you want thread-local or shared-but-immutable behavior with an annotation. ArrayDeque's empty array was missing this annotation. As it turns out, my collections had the exact same issue!