Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin coroutines "happens-before" guarantees?

Do Kotlin coroutines provide any "happens-before" guarantees?

For example, is there "happens-before" guarantee between write to mutableVar and subsequent read on (potentially) other thread in this case:

suspend fun doSomething() {
    var mutableVar = 0
    withContext(Dispatchers.IO) {
        mutableVar = 1
    }
    System.out.println("value: $mutableVar")
}

Edit:

Maybe additional example will clarify the question better becuase it's more Kotlin-ish (except for mutability). Is this code thread-safe:

suspend fun doSomething() {
    var data = withContext(Dispatchers.IO) {
        Data(1)
    }
    System.out.println("value: ${data.data}")
}

private data class Data(var data: Int)
like image 479
Vasiliy Avatar asked Oct 23 '19 08:10

Vasiliy


2 Answers

The code you wrote has three accesses to shared state:

var mutableVar = 0                        // access 1, init
withContext(Dispatchers.IO) {
    mutableVar = 1                        // access 2, write
}
System.out.println("value: $mutableVar")  // access 3, read

The three accesses are strictly sequentially ordered, with no concurrency between them, and you can rest assured that Kotlin's infrastructure takes care of establishing a happens-before edge when handing off to the IO thread pool and back to your calling coroutine.

Here's an equivalent example that may perhaps look more convincing:

launch(Dispatchers.Default) {
    var mutableVar = 0             // 1
    delay(1)
    mutableVar = 1                 // 2
    delay(1)
    println("value: $mutableVar")  // 3
}

Since delay is a suspendable function, and since we're using the Default dispatcher that is backed by a thread pool, lines 1, 2 and 3 may each execute on a different thread. Therefore your question about happen-before guarantees applies equally to this example. On the other hand, in this case it is (I would hope) completely obvious that the behavior of this code is consistent with the principles of sequential execution.

like image 151
Marko Topolnik Avatar answered Sep 25 '22 19:09

Marko Topolnik


Coroutines in Kotlin do provide happens before guarantees.

The rule is: inside a coroutine, the code prior to a suspend function call happens before the code after the suspend call.

You should think about coroutines as if they were regular threads:

Even though a coroutine in Kotlin can execute on multiple threads it is just like a thread from a standpoint of mutable state. No two actions in the same coroutine can be concurrent.

Source: https://proandroiddev.com/what-is-concurrent-access-to-mutable-state-f386e5cb8292

Getting back to the code example. Capturing vars in lambda function bodies is not ideal, especially when lambda is a coroutine. In fact,

Capturing mutable variables (var) into the scope of such block is almost always an error

(a statement from KT-15514)

The code prior to a lambda does not happen before the code inside.

See https://youtrack.jetbrains.com/issue/KT-15514

like image 41
Sergei Voitovich Avatar answered Sep 25 '22 19:09

Sergei Voitovich