Any ideas why `AtomicReference` is not available f...
# multiplatform
a
Any ideas why
AtomicReference
is not available for me in common code? According to documentation is should be. Kotlin '1.3.30'
s
As far as I see, according to the documentation it is available only in Native. Could you share the link to the documentation you’ve mentioned?
a
Oh yeah, sorry, indeed, I mistook the selection buttons on top of the page for the platform availability indicators https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native.concurrent/-atomic-reference/index.html
Still, what is the KN solution for having a mutable singleton (object) in common code?
s
There is no ultimate solution. If you need it be available only in main thread, then you can use top-level
val
instead of
object
.
a
May be a bit more info from my side. Situation as simple as that - I have such a class in common code:
Copy code
object RegistrationManager{
    var registrations: arrayOf()
}
"No ultimate solution" is different from "no solution". Currently I see only @ThreadLocal but then I have to care about always calling this manager from the same thread, which is quite awkward
I'm trying to understand the situation. I get the "same thread -> no mutability issues" concept but it looks like noone is using [mutable] singletons in shared code or what?
BTW according to docs, @ThreadLocal is also only for `native`code but it's available in
common
The thing is that this problem with mutable singletons in shared code is currently delaying our multiplatform project. According to concept, there is a number of business-logic related singletons in common code and currently we are stuck as it works from android code, but does not from iOS code.
s
By “no ultimate solution” I mean that the solution depends on your requirements. Is using main-thread-only top-level
val
suitable for you?
a
Is it possible to define this val in the common code? You mean like this:
Copy code
val GlobalRegistrationManager = RegistrationManager() 
class RegistrationManager{
    var registrations: arrayOf()
}
Then, still, on order to have no side effects I need to make sure calling it from main thread in all platform code. I just expected more elegant solution from Kotlin
Also it looks to me that it works from Android code (at least there are no exceptions)
s
Is it possible to define this val in the common code?
You mean like this:
Yes. Alternatively consider using https://github.com/touchlab/Stately
a
Thanks, that is usefull info, we will try ..
o
Note that mutable top level singletons are usually call for trouble (i.e. race conditions or deadlocks), so not having them will help your app.
a
That is I'm aware of. In my case I see no way of implementing it differently.
o
People had no idea how to code without GOTO statement some years ago :). In this case, you just need to decide now to propagate concurrent updates - i.e. keep them local, or have atomic reference to frozen list, linked list with head pointed by an atomic reference, whatever else
k
As mentioned, Stately helps with this. As the author I feel inclined to give a healthy warning to not overdo it. The collections especially. I’m rethinking that part. Common atomics https://github.com/touchlab/Stately/tree/master/stately/src/commonMain/kotlin/co/touchlab/stately/concurrency
a
Nice to have the author answering here. 😉 I've read your medium articles on this topic, and indeed, what I need is only a mutable shared collection, will try to utilize one of your suggested methods.
k
Oh, well, they work. Just understand a few things. Everything in them is frozen. Compared to standard collections, performance is not great. Also, AtomicReference in native warns to clear out references, so if the collection doesn’t live forever, clear it when done
If you get really into it, performance discussion https://github.com/square/sqldelight/issues/1226
a
Thanks! Could you maybe explain why our current implementation (object in common code) brings no warnings etc on Android but runtime exception on iOS, when trying to modify the collection in object?
k
If it’s a top level object, they are frozen by default in native. https://github.com/JetBrains/kotlin-native/blob/master/CONCURRENCY.md “singleton objects unless marked with @kotlin.native.ThreadLocal are frozen and shared, lazy values allowed, unless cyclic frozen structures were attempted to be created”
We have been doing little experiments for this kind of stuff lately. For example, for testability, we have a “ServiceRegistry” with frozen delegates. May change this to only do atomics on native, but it’s not really an issue in context. https://github.com/touchlab/DroidconKotlin/blob/master/sessionize/lib/src/commonMain/kotlin/co/touchlab/sessionize/ServiceRegistry.kt
a
@kpgalligan If I declare my mutable list as
val fetchers:MutableList<DataSourceFetcher> = frozenCopyOnWriteList<DataSourceFetcher>()
, will the list items be frozen? Because the mutability is working now but I guess I get an exception while trying to modify an item.
Well, isFrozen() is true for an item. What is the way of keeping the collection frozen but the items not in this concept?
Note that they are frozen only when used from iOS code.
k
Everything in a frozen list is frozen. You would need a different approach if the values needed to remain mutable. Freezing only affects native. Stately defines ‘freeze’ in common code so you can call it from common code, but it does nothing on jvm or js
a
Thanks, it becomes more and more clear. Could you by the chance take a look at my question on particular solution which i posted in the native channel? Crossposting here:
Hello all, I've read all on KN concurrency but still cannot figure out how to model this: • There is an
DataFetcher
and `DataStorage`classes, for getting data from remote to storing it, well, into the storage object • The list of
DataFetchers
is global, but mutable using `frozenCopyOnWriteList<>`from
Stately
lib => therefore all Fetchers are frozen • To tell a Fetcher which storage he needs to store the date into, I pass the Storage object in constructor. • PROBLEM: since Storage is then in the same object graph as the Fetcher, Storage becomes frozen. But it's counter-goal for a storage object. There should be a pattern for that, isn't it?
a
Try to annotate your Singleton object with @SharedImmutable and define AtomicReference of immutable list inside.
a
This is basically exactly what
frozenCopyOnWriteList<>
does, because my singleton is in common code, so AtomicReference is not accessible. Using AtomicReference requires enclosed object to be frozen which leads exactly to the problem I point above 😕
a
You can easily define your own AtomicReference with expect/actual. If you want to access any data from multiple threads, it should be frozen anyway, AFAIK.
Your storage classes should use AtomicReferences under the hood (or Stately collections)
AtomicReference inside a frozen object can still be updated (with new immutable frozen data)
k
We talked about this a bit in the native channel. An alternative is keeping mutable state in it’s own thread and using worker to go back and forth: https://gist.github.com/kpgalligan/43f783d2c69b4a5d6d980b19d0f2ce87
Yeah, that too (AtomicReference can be changed)
a
Nice trick with worker! but might be a bottle neck if many threads are reading/writing
k
Yeah, it’ll depend on what you’re doing. Just good to think of alternatives
a
Totally agree