The weird and wonderful world of threading in Kotlin-Native

Ian Alexander
Mobile at Octopus Energy

--

As KMM has been picked up and experimented with by more and more teams there’s one complaint which inevitably gets mentioned time and again — concurrency in Kotlin Native.

Every time a new mobile developer joins our team and gets the ‘welcome to KMM presentation’ it’s the part where I see the most puzzled faces looking back at me — and then get the most questions.

Whats the problem?

Coming from JVM or Swift — or actually most programming languages — we’re used to being able to pass objects between threads. We can create an object on one thread, pass it to another thread, and access any value on the object.

Try and do the same thing with Kotlin Native and at >>runtime<< you’ll experience…

kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared com.example.project.MyClass@397cba8 from other thread

What you need to do is freeze the object before passing it to another thread — psst. coroutines and most other KMP threading libraries do this for you — now you can access any value on the object.

Okay, so you’ve frozen the object (or coroutines has frozen it for you), passed it to another thread, and you want to mutate a var — but as soon as you try you’re hit with…

kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen com.example.project.MyClass@fe10a8

Even more confusing, sometimes you’ll get this exception from a completely unrelated class — as Kotlin Native will deep freeze the entire object hierarchy passed between threads.

How it works

The immediate reaction to both of these is confusion, followed by diving down a rabbit hole of how threading works in Kotlin Native — before arriving back at the surface knowing less than you did before.

There’s plenty of great articles on how kotlin native threading works which I’ll link a few of below. What I want to get into though is not how it works, but how to write your shared code so that you never need to think or worry about how threading works in Kotlin Native.

Adapting to KN threading

Kotlin Native creates a structure which aims to prevent concurrency issues — a commendable aim — by removing mutability of objects moving across threads.

But instead of asking you nicely, it gives you a blindfold, turns out the lights, and hits you with a sledgehammer whenever you take one wrong step. So instead of fighting it, how can we adapt to it?

Tracking whether we’re passing mutable or frozen data between threads is super tricky, and requires a lot of thought as well as the high risk of mistakes which only show at runtime — and only in Kotlin Native targets (i.e. iOS).

But if we follow several simple rules, we can avoid ever experiencing these exceptions. Regardless of how we write shared code.

Rule 1 — InvalidMutabilityException

Want to use a var? There’s usually an immutable alternative, psst copy

Have to use a var? Use an atomic instead

Rule 2IncorrectDeferenceException

Use a library which handles freezing for you — like coroutines

Sending data back to iOS? Freeze it

Follow those rules, and you won’t ever need to understand the ins and outs of the Kotlin native memory model. And you’ll never experience those exceptions.

But no need to worry, all this should be gone away soon, in fact the new memory model for Kotlin Native launched in Experimental with 1.6.0. We’ll all be back to a good old system we know with the lights firmly left on.

The only thing I’ll miss, is that I haven’t come across a concurrency issue since dealing with the weird and wonderful world of Kotlin Native threading.

--

--