Platorm: iOS (mpp), kotlin: `1.3.71`, coroutines: ...
# kotlin-native
p
Platorm: iOS (mpp), kotlin:
1.3.71
, coroutines:
1.3.5-native-mt
, ktor:
1.3.2
. Can someone explain to me, how is this working: For now, this is not possible to do ktor calls in the background. I know that this is ktor's issue, but only when you are calling
HttpClient
from different threads on iOS (according to the github issues), and ktor handles this (does background calls) by itself. In my case, I do such calls only before, but anyway I got
InvalidMutabilityException
. How this is possible? A detailed description is below. I have the code like this:
Copy code
class IssueReproduce: CoroutineScope {
	private val dispatcherUi = // on iOS: Dispatchers.Main
	private val dispatcherDefault = // on iOS: Dispatchers.Default
	private val job = SupervisorJob()
	private val api = SomeApi()
	
	override val coroutineContext = dispatcherUi + job

	/* ... */

    fun checkAuth() {
	    launch(coroutineContext) {
	    	// make some call in a background BEFORE
	        val token = withContext(dispatcherDefault) {
	            repo.getToken()
	        }

	        val response = try {
	            api.doAuth(token)
	        } catch (e: Throwable) {
	            // error: 
	            // kotlin.native.concurrent.InvalidMutabilityException
	            // mutation attempt of frozen io.ktor.client.request.HttpRequestPipeline@838cd448
	        }
	    }
	}
}
Where:
Copy code
class SomeApi {
	private val client = HttpClient(/* ... */)

	suspend fun doAuth(token: String): SomeDataClass {
		return <http://client.post|client.post> {
			url(/* ... */)
			body = /* ... */
		}
	}
}
But, when I change code to this:
Copy code
val token = /* withContext(dispatcherDefault) { */
	repo.getToken()
/* } */
i.e. when I make no calls in the background before calling ktor.HttpClient, all is ok and there are no errors. So, what I'm doing wrong? Removing
withContext(dispatcherDefault)
for precending code does the trick, but wrapping ktor calls with
withContext(coroutineContext)
or
withContext(dispatcherUi)
does not. How preceding code with the call in the background can affect the thread of the following code? And how to fix this? As I mentioned above, I tried to wrap ktor calls with the original thread, but it does not help.
r
can you try to change the client initialization to lazy?
private val client by lazy { HttpClient(/* … */)
?
p
@rudolf.hladik it is lazy already, and it is injected by the Kodein. Removed this to make example simpler. The issue is reproduced in case of lazy client initialization too. Kodein (top level variable) is threadlocal and lazy too.
k
ktor is not compatible with multithreaded coroutines
it does not support freezing
this is why i dropped ktor and rolled my own Http class. it was quite straightforward
p
@Kris Wong it is completely incompatible with all -native-mt versions? But anyway, it is interesting for me, how calls in the background coroutine somewhere in the code can affect the thread of following code inside the “launch” block, and this thread has high priority and coroutineContext is ignored, and even force changing coroutine back is also does not help
k
welcome to K/N concurrency
this won't be the last time you'll be scratching your head
🙂 1
a
Your background block captures
this
and freezes the entire class, and so
SomeApi
and its
HttpClient
. Try moving this call to a separate method and pass
repo
as argument.