https://kotlinlang.org logo
#multiplatform
Title
# multiplatform
m

Marc Knaup

06/11/2019, 4:06 PM
On iOS, does the following make sense to start coroutines using whatever the current dispatch queue is?
Copy code
private object CurrentQueueDispatcher : CoroutineDispatcher() {

	override fun dispatch(context: CoroutineContext, block: Runnable) {
		dispatch_async(dispatch_get_current_queue()) {
			block.run()
		}
	}
}
Copy code
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:
Copy code
kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared CurrentQueueDispatcher.$dispatch$lambda-0$FUNCTION_REFERENCE$1@39254a8 from other thread
b

basher

06/11/2019, 5:00 PM
dispatch queue doesn't guarantee single thread. it only guarantees synchronization with respect to that queue
m

Marc Knaup

06/11/2019, 5:01 PM
Yup, just noticed. Still trying to find a way to make coroutines work on iOS from a background queue or thread 🤔
b

basher

06/11/2019, 5:05 PM
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
👍 1
m

Marc Knaup

06/11/2019, 5:06 PM
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

06/11/2019, 5:06 PM
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

06/11/2019, 5:11 PM
Alright, next try 😄 Compiling…
Copy code
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

06/11/2019, 5:11 PM
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

06/11/2019, 5:11 PM
Damn…
Hmm, no exception. Nothing is actually happening.
b

basher

06/11/2019, 5:15 PM
possible it logged the exception in the console silently
might happen if exception happens from the Worker thread
m

Marc Knaup

06/11/2019, 5:16 PM
True. It doesn't even say anymore what object is the problem…
kotlin.IllegalStateException: Illegal transfer state
d

Dico

06/12/2019, 5:50 AM
I commonly use
runBlocking
as an event loop
a

Andy Victors

06/12/2019, 7:27 AM
So iOS implementation of coroutines will always run on main thread, do I get it right?
m

Marc Knaup

06/12/2019, 11:00 AM
No, it actually works on the background. You just can't switch between threads which includes using Ktor. There are unsafe workarounds though.
a

Andy Victors

06/12/2019, 11:13 AM
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

06/12/2019, 11:34 AM
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

06/13/2019, 7:51 AM
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

06/13/2019, 12:19 PM
What do you mean by leaking here? I'm not using the Future at all so how can it leak?
b

basher

06/13/2019, 1:38 PM
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

06/13/2019, 1:39 PM
Yeah, but I hold no reference to that
Future
so I don't see the leak 🤔 Maybe an unused Future leaks the result internally?
👍 1
s

svyatoslav.scherbina

06/13/2019, 2:23 PM
Future
is a resource. It leaks if the result is not consumed.
b

basher

06/13/2019, 2:25 PM
Even if the result is Unit?
m

Marc Knaup

06/13/2019, 2:26 PM
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

06/13/2019, 2:31 PM
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

06/13/2019, 2:34 PM
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

06/13/2019, 3:58 PM
That appears to be the case. Dang. I have some cleanup to do then 😕
s

svyatoslav.scherbina

06/14/2019, 8:03 AM
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.
8 Views