what is the proper way to syncronize suspend code ...
# coroutines
m
what is the proper way to syncronize suspend code with non suspend code ?
Mutex in suspend code + runBlocking in non-suspend ?
d
That’s one way to do it, for sure.
x
If you can, it's always better to make the non-suspend code execute in the same
CoroutineScope
as the suspend code. That will allow you to use all the power of APIs lile
Mutex::withLock
m
Ya. The usecase is this: i need to halt some method from returning until an internal condition is met. I ended up using a syncronized block. Not sure if it is the right approach, but it seems to be working. I could not get it to work with a mutex
t
With the mutex, how were you trying to use it? (Can you provide some code snippets?)
I am not an expert. But I think a mutex should be able to work for that. I'd imagine it would go: • Top level: create Mutex(locked = true) • Pass mutex to subroutine, which attempts to acquire() the lock, and thus suspends. • Top level release()s the mutex once it's ready for the subroutine to proceed.
x
I need to halt some method from returning until an internal condition is met
It doesn't seem that you need synchronisation or
Mutex
. What I understand from your saying here is that you need sequential code (you are talking about one method waiting, not concurrent processes trying to read the same thing) If sequential code is what you need, then just convert the regular non-suspend function into a
suspend
function. Coroutines execute the code sequentially by default
m
I don’t need sequencial code. As i explained (badly), i need to synchronise access between suspend and non-suspend. The use case is this (SurfaceView game loop):
Copy code
fun onSurfaceDestroyed(){
	
}

suspend fun loop(){
	while(running){
		...
		...
		canvas.lockCanvas
		..
		canvas.unlockCanvas
		yield()
	}
}
The onSurfaceDestroyed callback is called by the system, and dictates that, after that method returns, you can no longer use the canvas. So, since the game loop runs in a background coroutine(for obvious reasons), you need to make sure that onSurfaceDestroyed cannot return between lock and unlock canvas.
that is the usecase
it is ok to call lockCanvas after onSurfaceDestroyed tho. It won’t do anything but it is not problematic. You cannot, however, have onSurfaceDestroyed returning between those two
x
That's useful info to better understand your case. I was thinking that you were owner of the non-suspend function (
onSurfaceDestroyed
) but not being that the case, then I'd use a
runBlocking
I still not see the necessity of
Mutex
or
synchronized
from the code above. There's only an async process, but there's not concurrency
m
there is concurrency. since the callback and the loop do not happen in the same thread, you can have a scenario in which the onSurfaceDestroyed method returns between lock and unlock
x
Oh, my bad then. So then you're right on that you need some sort of synchronization. Have you tried creating a
CoroutineScope
using the
CoroutineScope()
factory and then using
Mutex
?
d
Copy code
fun onSurfaceDestroyed() = runBlocking {
   mutex.withLock {
      running = false
   }
}

suspend fun loop() {
   while (true) {
      mutex.withLock {
         if (!running) return
         canvas.lockCanvas
         ... 
         canvas.unlockCanvas
      }
   }
}
Would something like that work for you? (note, untested)
m
not rly. My implementation is slightly different. onSurfaceDestroyed just updates some mutableState, and then the loop is dependent on that state and will break out. I just needed to avoid returning until some boolean condition changes. I ended up using a synchronised block
d
You must not synchronize in a suspend method. It breaks coroutines.
m
well, i cannot use mutex in non suspend code, can i ?
it doesn’t break. Its just there are weird edge cases. I ended up using runBlocking mutex, as suggested above
d
runBlocking is the bridge between non-suspend and suspend code.