Practical Kotlin Native Concurrency Part 2
Tags: kotlin, kotlinmultiplatform, android, ios
Published: 2019-12-16 17:52:55

---
title: Practical Kotlin Native Concurrency Part 2
published: true
description: Hands-on introduction to concurrency and state in Kotlin/Native
tags: kotlin, kotlinmultiplatform, android, ios
series: Practical Kotlin Native Concurrency
---

[Back in part 1](https://dev.to/touchlab/practical-kotlin-native-concurrency-ac7), you learned a bit about KN's state rules, installed and ran Intellij, and ran some sample code. In part 2 we'll dig a little deeper.

First, update the sample! There may have been repo changes. Undo anything local and run `git pull`.

```
git checkout .
git pull
```

# 3) Global State

Before we get into actual concurrent code, lets chat a bit about global state. Some things in Kotlin can be referenced globally, from any thread. As a result, KN treats them with special rules.

These are kind of weird. However, there are only 2 types of global state, so just remember the weirdness.

## *`object` is frozen*

Any global `object` is frozen, but that instance can be shared and referenced from any thread.

Find `3) Global State` in `SampleMacos.kt` and uncomment `cantChangeThis()`.

```kotlin
fun cantChangeThis(){
    println("i ${DefaultGlobalState.i}")
    DefaultGlobalState.i++
    println("i ${DefaultGlobalState.i}") //We won't get here
}

object DefaultGlobalState{
    var i = 5
}
```

This will compile fine, but all global `object` instances are frozen by default, so you'll see your old friend `InvalidMutabilityException`.

If you need that state to be mutable you do have some options. You can use atomics, which we'll talk about later, but for now you can make that object thread local. That means each thread has it's own copy, and according to rule #1, mutable state == 1 thread.

Run `canChangeThreadLocal()`.

```kotlin
fun canChangeThreadLocal(){
    println("i ${ThreadLocalGlobalState.i}")
    ThreadLocalGlobalState.i++
    println("i ${ThreadLocalGlobalState.i}")
}

@ThreadLocal
object ThreadLocalGlobalState{
    var i = 5
}
```

Notice the annotation `@ThreadLocal`. That tells the runtime that each thread has an instance defined. Running `canChangeThreadLocal()` will print

```
i 5
i 6
```

Keep in mind that if you were accessing `ThreadLocalGlobalState` from another thread, the value of `i` would be different.

Just an FYI. *These rules apply to companion objects as well*.

## *properties are main thread only*

This is the "weird" rule. Global properties are neither thread local nor frozen. *They are only visible to the main thread* [^mainthread]. This will seem odd, but just keep it in mind.

To be clear, global *functions* are fine. We're just talking about global *properties*.

Run `globalCounting()`

```kotlin
fun globalCounting(){
    globalCounterData.i++
    println("count ${globalCounterData.i}")
    globalCounterData.i++
    println("count ${globalCounterData.i}")
}

var globalCounterData = SomeMutableData(33)
```

You'll see

```
count 34
count 35
```

We haven't explained how to leave the main thread yet, but as a quick preview to demonstrate that properties are main thread only, run `globalCountingFail()`. I won't show the code, but you'll get an exception:

```
kotlin.native.IncorrectDereferenceException: Trying to access top level value not marked as @ThreadLocal or @SharedImmutable from non-main thread
        at 0   KNConcurrencySamples.kexe   (blah blah blah)
        at 1   KNConcurrencySamples.kexe   (blah blah blah)
(etc)

```

Trying to access the top-level state fails[^til].

You can annotate top-level state with either `@ThreadLocal`, which will give each thread a copy, or with `@SharedImmutable`, which will make that state frozen and shared. We'll skip the code for that, though, and move on.

# Actual Concurrency

We're going to leave the main thread now. In other explanations, I start by going deep on `Worker`. `Worker` is KN's primary concurrency primitive. It's a job queue that has it's own private thread.

We won't be going over `Worker`. Assuming you are using a library to manage concurrency, you won't interact with `Worker` much, if at all. For a deeper understanding of what's happening, again I'd point you at my previous posts and talks, and if you're really curious, the JetBrains docs and videos go way into it.

## Emerging Model

You can technically attempt to pass mutable state to other threads. In practice, KN concurrency libraries will freeze state when it goes between threads. This is true of the libraries we've been using internally, and the soon-to-be-released kotlinx.coroutines implementation.

In this post we'll create a simple background thread processor. In a later post, we'll look at the kotlix.coroutines preview.

## 4) Background

We've created a simple background task processor function to demonstrate crossing threads. The signature is this:

```kotlin
fun background(block: () -> Unit)
```

We're not going to walk through that code because it uses worker and requires understanding a bit more complexity than we need.

The important parts:

1) The argument is a function, which will be executed in another thread
2) That function argument itself will be frozen

Here is a basic example. Run `basicBackground()`

```kotlin
fun basicBackground(){
    println("Is main thread $isMainThread")
    background {
        println("Is main thread $isMainThread")
    }
}
```
We've also `isMainThread`, which will require you're doing this on a Mac unless we get a PR that enables other platform. Anyway, the function prints the following.

```
Is main thread true
Is main thread false
```

That gets us onto another thread to do work, but obviously doesn't do much. This next example will pass some state into the other thread.

*This is very important to understand. If you were skimming the post, now is a good time to s l o w  d o w n.*

Run `captureState()`:

```kotlin
fun captureState(){
    val sd = SomeData("Hello 🥶", 67)
    println("Am I frozen? ${sd.isFrozen}")
    background {
        println("Am I frozen now? ${sd.isFrozen}")
    }
}
```

This will print:

```
Am I frozen? false
Am I frozen now? true
```

***It is very important to understand that function args can capture state, and when they are frozen, that state is also frozen.***

As an alternate visualization, see below. When we call `background`, that function argument is frozen, and since it captures `sd`, which is a local val in the calling function, `sd` is also frozen.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/2tznt8vinpj7spk8jwot.png)

Local values are easy to understand and deal with. Where this becomes more problematic, and triggers more controversy, is when unintended state is captured.

Run `captureTooMuch()`

We'll be using a more complex model class that has a number you can increment, and when you do, we push that value into a "database" in the background:

```kotlin
class CountingModel{
    var count = 0

    fun increment(){
        count++
        background {
            saveToDb(count)
        }
    }

    private fun saveToDb(arg:Int){
        //Do some db stuff
    }
}
```

Our function to use the model looks like this:

```kotlin
fun captureTooMuch(){
    val model = CountingModel()
    model.increment()
    println("I have ${model.count}")

    model.increment()
    println("I have ${model.count}") //We won't get here
}
```

What's the result? Our old friend, `InvalidMutabilityException`!

```
I have 1
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen sample.CountingModel@17e01008
        at 0   KNConcurrencySamples.kexe   (nah)
        at 1   KNConcurrencySamples.kexe   (meh)
(etc)
        
```

To help illustrate the issue here, consider `captureTooMuchAgain()`

```kotlin
fun captureTooMuchAgain(){
    val model = CountingModel()
    println("model is frozen ${model.isFrozen}")
    model.increment()
    println("model is frozen ${model.isFrozen}")
}
```

This prints

```
model is frozen false
model is frozen true
```

`count` is an instance var of `CountingModel`, so capturing `count` in that way will capture the whole model and freeze it.

To help avoid this, it is good practice to move thread-jumping code to another function, and only capture function arguments. Try `captureArgs()`:

```kotlin
fun captureArgs(){
    val model = CountingModelSafer()
    model.increment()
    println("I have ${model.count}")

    model.increment()
    println("I have ${model.count}")
}

class CountingModelSafer{
    var count = 0

    fun increment(){
        count++
        saveToDb(count)
    }

    private fun saveToDb(arg:Int) = background {
        println("Doing db stuff with $arg, in main $isMainThread")
    }
}
```

## Debugging

Going back to `captureTooMuch()`, we get the `InvalidMutabilityException` when we try to increment, but that's not super helpful. We want to know when/how our data got frozen in the first place. Obviously, in this example, it's not hard to track down, but in more complex examples it will be.

You can mark an object that should never be frozen with `ensureNeverFrozen()`. When freeze is happening, the runtime will throw an exception at the point of freezing. Run `captureTooMuchSource()`

```kotlin
fun captureTooMuchSource(){
    val model = CountingModel()
    model.ensureNeverFrozen()
    model.increment()
    println("I have ${model.count}") //We won't even get here
}
```

That will get us an exception that traces right back to the root issue:

```
Uncaught Kotlin exception: kotlin.native.concurrent.FreezingException: freezing of sample.CountingModel.$increment$lambda-0$FUNCTION_REFERENCE$2@7d601058 has failed, first blocker is sample.CountingModel@7d601008
        at 0   KNConcurrencySamples.kexe   (ugh)
        at 1   KNConcurrencySamples.kexe   (stuff)
(skip a few lines...)
        at 13  KNConcurrencySamples.kexe           0x00000001088c570d kfun:sample.background(kotlin.Function0<kotlin.Unit>) + 301 (/Users/kgalligan/temp/KNConcurrencySamples/src/macosMain/kotlin/sample/BackgroundSupport.kt:12:12)
        at 14  KNConcurrencySamples.kexe           0x00000001088c5102 kfun:sample.CountingModel.increment() + 258 (/Users/kgalligan/temp/KNConcurrencySamples/src/macosMain/kotlin/sample/Background.kt:35:9)
        at 15  KNConcurrencySamples.kexe           0x00000001088c547e kfun:sample.captureTooMuchSource() + 286 (/Users/kgalligan/temp/KNConcurrencySamples/src/macosMain/kotlin/sample/Background.kt:77:11)
        
```

The stack trace is a bit verbose, but you'll be able to walk back to the source issue.

# End

That's a wrap for Part 2. In Part 3 we'll wrap up the primitives with atomics, then introduce various libraries you can use for concurrency in you applications.

[^mainthread]: I don't know for sure why they made that decision. However, I suspect it's because you can get into trouble. Early on, global vals were thread-local. If you create a Worker as a global, it'll start a new thread. That thread gets it's own Worker, which starts a new thread. Etc. Good times. KN no longer does this.

[^til]: Sort of? Apparently, simple integer data at least can be shared and mutable. Run `globalCountingTil()`. You'll need to add that method manually to main. It lets you edit a global `Int`. Not sure how why, but investigating.