I have the following class:
class SdkWrapper(private val sdk: Sdk) {
private var inited = false
suspend fun doSomething() = withContext(Dispatchers.IO) {
if (inited.not()) init()
useSdk()
}
private fun init() {
// takes a long time
sdk.init()
inited = true
}
// has to be done asynchronously
// sdk.init() has to have been called before using this
private fun useSdk() {
}
}
class Sdk {
// must only be done once
fun init() {}
}
Before I can do useSdk(), I must call sdk.init(), but sdk.init() must only be called once, not more.
With my current solution, if doSomething is called twice quickly (the second time happening while sdk.init() still running), I would call sdk.init() twice, because inited: Boolean is still false.
If I move the assignment of inited up like:
private fun init() {
inited = true
sdk.init()
}
and doSomething() is called twice rapidly, the second call would use the sdk before its' init() has been done.
I tried to solve this with:
suspend fun doSomething() = synchronized(this){
withContext(Dispatchers.IO) {
if (inited.not()) init()
useSdk()
}
}
but receive an error in IntelliJ:
the withContext suspension point is inside a critical section
I assume that synchronized wouldn't work here anyway, because we move off the main thread and doSomething() is completed while the withContext block is still running?
How can I solve the problem at hand which basically is: doSomething() should only run once at a time?
Instead of synchronized {...} you can use a Mutex:
class SdkWrapper(private val sdk: Sdk) {
...
private val mutex = Mutex()
suspend fun doSomething() = mutex.withLock {
withContext(Dispatchers.IO) {
if (inited.not()) init()
useSdk()
}
}
...
}
You can take a look at the official documentation about Coroutines and mutual exclusion here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With