These might be two stupid questions, but i need to be sure I understood correctly.
In Kotlin documentation there is this example:
// This function launches a new counter actor
fun CoroutineScope.counterActor() = actor<CounterMsg> {
var counter = 0 // actor state
for (msg in channel) { // iterate over incoming messages
when (msg) {
is IncCounter -> counter++
is GetCounter -> msg.response.complete(counter)
}
}
}
This works because even if the coroutine gets resumed in different threads (by setting a Dispatcher that uses multiple threads), there is a happens-before relationship, so visibility of counter
is guaranteed.
But does it have anything to do with the actor state being defined inside the actor? Suppose I have the following example:
var counter = 0
// some other stuff
fun CoroutineScope.counterActor() = actor<CounterMsg> {
for (msg in channel) { // iterate over incoming messages
when (msg) {
is IncCounter -> counter++
is GetCounter -> msg.response.complete(counter)
}
}
}
Assume for the moment that we make sure that the counter
variable only gets accessed from the coroutine. Then the happens-before relationship is still guaranteed right? I am asking this because I have a normal Kotlin object, which has some mutable, but private fields. These fields do not get accessed by anything other than a single coroutine that infinitely iterates over multiple channels via select. I cannot define the state inside the coroutine due to inheritance. But I make sure the coroutine is only started once all these fields are initialized.
My second question is regarding the @Volatile
annotation. I also have the following scenario:
// channels that other parts of the program use for communication
val featureChannel = Channel<Feature>()
val dataChannel = Channel<Data>()
// state object that has lots of mutable state, but ONLY accessed from coroutine below
private val state = State()
// other parts of the program might read this. do they see the latest info?
@Volatile var latestInfo = state.getImmutableInfo()
init {
GlobalScope.launch (Dispatchers.Unconfined) {
while (isActive) {
select<Unit> {
featureChannel.onReceive {
state.handleFeature(it)
latestInfo = state.getImmutableInfo()
}
dataChannel.onReceive {
state.handleData(it)
latestInfo = state.getImmutableInfo()
}
}
}
}
}
The field latestInfo
gets only written by this coroutine. But from time to time, other coroutines (that are running simultanously somewhere else) might want to read it. Do they see the latest object? As far as I understood, the @Volatile
keyword should make that sure the variable is copied to main memory, thus other coroutines should, when accessing latestInfo
, always get the newest object.
Is @Volatile
even neccessary here?