Thread
#multiplatform
    m

    Marc Knaup

    3 years ago
    On iOS, does the following make sense to start coroutines using whatever the current dispatch queue is?
    private object CurrentQueueDispatcher : CoroutineDispatcher() {
    
    	override fun dispatch(context: CoroutineContext, block: Runnable) {
    		dispatch_async(dispatch_get_current_queue()) {
    			block.run()
    		}
    	}
    }
    GlobalScope.launch(CurrentQueueDispatcher) { … }
    Also,
    dispatch_async
    or
    dispatch_sync
    ? The documentation doesn't state whether it's okay to block here or not.
    So much for that:
    kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared CurrentQueueDispatcher.$dispatch$lambda-0$FUNCTION_REFERENCE$1@39254a8 from other thread
    b

    basher

    3 years ago
    dispatch queue doesn't guarantee single thread. it only guarantees synchronization with respect to that queue
    m

    Marc Knaup

    3 years ago
    Yup, just noticed. Still trying to find a way to make coroutines work on iOS from a background queue or thread 🤔
    b

    basher

    3 years ago
    At the moment, I don't think it's possible to write a multi-threaded dispatcher in native because of native concurrency/mutability rules + coroutine internals not using frozen/thread-safe state/structures. Generally you have 2 options when trying to pull things across threads: freeze them or transfer them. 1. *Transfer*: You can't transfer objects passed to you in dispatcher overrides because they're owned by coroutine internals (will crash because transferring requires detaching the object from that thread, which can't happen if the objects are still owned) 2. *Freeze*: Can't freeze them because nearly all of them have internal mutable state, so an exception will be thrown as soon as their internals make a call that attempts a mutation
    m

    Marc Knaup

    3 years ago
    Thank you for the insight! I don't need a multi-threaded dispatcher though. Just single-threaded in the background.
    Main thread is too sensitive for JSON parsing and Kotlin/Native bridging.
    b

    basher

    3 years ago
    The 3rd option is thread confinement: use
    Worker
    +
    runBlocking
    to do coroutine-based work in a
    Worker
    , being careful not to allow coroutine things to leave the worker and then transfer (or freeze) the result of that work back when you're done
    m

    Marc Knaup

    3 years ago
    Alright, next try 😄 Compiling…
    private object BackgroundWorkerDispatcher : CoroutineDispatcher() {
    
    	private val worker = Worker.start()
    
    
    	override fun dispatch(context: CoroutineContext, block: Runnable) {
    		worker.execute(TransferMode.SAFE, { block }, { it.run() })
    	}
    }
    b

    basher

    3 years ago
    The
    block
    there is owned by the calling thread and coroutine internals. Passing it to the producer there will attempt to transfer it to the worker thread and crash (i expect)
    m

    Marc Knaup

    3 years ago
    Damn…
    Hmm, no exception. Nothing is actually happening.
    b

    basher

    3 years ago
    possible it logged the exception in the console silently
    might happen if exception happens from the Worker thread
    m

    Marc Knaup

    3 years ago
    True. It doesn't even say anymore what object is the problem…
    kotlin.IllegalStateException: Illegal transfer state
    Dico

    Dico

    3 years ago
    I commonly use
    runBlocking
    as an event loop
    Andy Victors

    Andy Victors

    3 years ago
    So iOS implementation of coroutines will always run on main thread, do I get it right?
    m

    Marc Knaup

    3 years ago
    No, it actually works on the background. You just can't switch between threads which includes using Ktor. There are unsafe workarounds though.
    Andy Victors

    Andy Victors

    3 years ago
    Right, that was my case - I wanted to ask "if using Ktor with coroutines on IOS will run in main thread?" and it looks like answer is still yes.
    m

    Marc Knaup

    3 years ago
    Well for me, with some UNSAFE involved, Ktor now runs on a background thread except for a brief timespan between
    NSURLSession
    delegate invocation and ktor coroutine invocation.
    s

    svyatoslav.scherbina

    3 years ago
    https://kotlinlang.slack.com/archives/C3PQML5NU/p1560273060108300?thread_ts=1560269181.105800&cid=C3PQML5NU This is not entirely correct since you have
    worker.execute
    resulting
    Future
    leaking.
    m

    Marc Knaup

    3 years ago
    What do you mean by leaking here? I'm not using the Future at all so how can it leak?
    b

    basher

    3 years ago
    Ah well one thing to remember (which I often forget) is the final statement in the worker execute block will be returned as the result of the future returned by execute, which result in another object transfer across threads.
    Maybe that’s what is being referred to
    m

    Marc Knaup

    3 years ago
    Yeah, but I hold no reference to that
    Future
    so I don't see the leak 🤔 Maybe an unused Future leaks the result internally?
    s

    svyatoslav.scherbina

    3 years ago
    Future
    is a resource. It leaks if the result is not consumed.
    b

    basher

    3 years ago
    Even if the result is Unit?
    m

    Marc Knaup

    3 years ago
    There should be a warning in the IDE about that 🤔 So how do I free a Future's resources without blocking the thread?
    s

    svyatoslav.scherbina

    3 years ago
    Even if the result is Unit?
    AFAIK yes.
    So how do I free a Future’s resources without blocking the thread?
    Once
    future.state
    is
    COMPUTED
    you can free it with
    .consume {}
    or
    .value
    . So you can add future to some registry which gets polled for finished futures. See also https://github.com/JetBrains/kotlin-native/pull/2971 (will be included to 1.3.50).
    m

    Marc Knaup

    3 years ago
    So I would basically use
    executeAfter
    with a time interval of zero and don't need to take care of consuming the future anymore?
    b

    basher

    3 years ago
    That appears to be the case. Dang. I have some cleanup to do then 😕
    s

    svyatoslav.scherbina

    3 years ago
    So I would basically use
    executeAfter
    with a time interval of zero and don’t need to take care of consuming the future anymore?
    Exactly.