Andy Victors

    Andy Victors

    3 years ago
    Any ideas why
    AtomicReference
    is not available for me in common code? According to documentation is should be. Kotlin '1.3.30'
    s

    svyatoslav.scherbina

    3 years ago
    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?
    Andy Victors

    Andy Victors

    3 years ago
    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

    svyatoslav.scherbina

    3 years ago
    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
    .
    Andy Victors

    Andy Victors

    3 years ago
    May be a bit more info from my side. Situation as simple as that - I have such a class in common 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 nativecode 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

    svyatoslav.scherbina

    3 years ago
    By “no ultimate solution” I mean that the solution depends on your requirements. Is using main-thread-only top-level
    val
    suitable for you?
    Andy Victors

    Andy Victors

    3 years ago
    Is it possible to define this val in the common code? You mean like this:
    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

    svyatoslav.scherbina

    3 years ago
    Is it possible to define this val in the common code?
    You mean like this:
    Yes. Alternatively consider using https://github.com/touchlab/Stately
    Andy Victors

    Andy Victors

    3 years ago
    Thanks, that is usefull info, we will try ..
    olonho

    olonho

    3 years ago
    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.
    Andy Victors

    Andy Victors

    3 years ago
    That is I'm aware of. In my case I see no way of implementing it differently.
    olonho

    olonho

    3 years ago
    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
    kpgalligan

    kpgalligan

    3 years ago
    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
    Andy Victors

    Andy Victors

    3 years ago
    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.
    kpgalligan

    kpgalligan

    3 years ago
    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
    Andy Victors

    Andy Victors

    3 years ago
    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?
    kpgalligan

    kpgalligan

    3 years ago
    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
    Andy Victors

    Andy Victors

    3 years ago
    @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.
    kpgalligan

    kpgalligan

    3 years ago
    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
    Andy Victors

    Andy Victors

    3 years ago
    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 DataStorageclasses, 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?
    Arkadii Ivanov

    Arkadii Ivanov

    3 years ago
    Try to annotate your Singleton object with @SharedImmutable and define AtomicReference of immutable list inside.
    Andy Victors

    Andy Victors

    3 years ago
    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 😕
    Arkadii Ivanov

    Arkadii Ivanov

    3 years ago
    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)
    kpgalligan

    kpgalligan

    3 years ago
    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)
    Arkadii Ivanov

    Arkadii Ivanov

    3 years ago
    Nice trick with worker! but might be a bottle neck if many threads are reading/writing
    kpgalligan

    kpgalligan

    3 years ago
    Yeah, it’ll depend on what you’re doing. Just good to think of alternatives
    Arkadii Ivanov

    Arkadii Ivanov

    3 years ago
    Totally agree