Hello team, I’m having a deadlock situation when I call a Kotlin method from java. Method in Kotlin ...
g
Hello team, I’m having a deadlock situation when I call a Kotlin method from java. Method in Kotlin it’s a regular function, but it requires to do some Room Database operation and the way I’m trying to do this is by using the coroutine builder
_runBlocking_(<http://Dispatchers.IO|Dispatchers.IO>)
, not totally sure if this is the way to go, seem like not. Adding a little more explanation: 1. Kotlin creates a couple( 400 ) coroutines with async coroutines builder, which executes some operations in async using async coroutine builder 2. The created coroutine call some complex Java code that requires to do some database operations, that’s why I need to call a regular Kotlin method and that regular Kotlin method calls the room function with a valid coroutine scope create with runBlocking. 3. Doing a Thread Dump visualization I got something like the shared picture. Note: All flow of my program works just fine if I lunch around 50 coroutines (I did a test with that amount), but seems like more than one hundred causes the hang.
x
I’m not sure if I have perfectly understood the situation, but it seems that you could be launching all those coroutines in a non-blocking manner using a regular
CoroutineScope
that you could be generating for that case. As the Kotlin function is not
suspend
, you shouldn’t have any problem on calling it from Java
🙌 1
g
Hi!, I added a little more of information, also adding the Thread Dump Screenshot
// creates around 400 coroutines with async
override suspend fun executeExpressions() {
val expressionSize = this.expressions.size
val expressionsToExecute: List<FxlExpression> = this.expressions.values.toList()
val result: List<Deferred<String>> = expressionsToExecute.mapIndexed { index, expressionObject ->
async {
println("Expression ($index/$expressionSize) ${expressionObject.key} started")
// calls Java code as part of the operation logic.
val result: String? = executeExpression(expressionObject.key, expressionObject.value)
println("Expression ($index/$expressionSize) ${expressionObject.key} finished")
"Expression ($index/$expressionSize) ${expressionObject.key} result $result"
}
}
val result = result.awaitAll()
}
// called from Java code that calls a suspend function
@Synchronized @Expe`rimentalCoroutinesApi`
override fun executeBehavior(evaluator: Evaluator, behaviorKey: String): Boolean {
return _runBlocking_(this.dispatcher) *{*
// suspend Room database methods.
}
}
x
The code pasted here in this format is pretty difficult to read. Can you provide a link to Github/Gitlab/Bitbucket for this code (if it is open-source) or at least a copy of this snippet (if the full code can’t be shared)
If you are calling a normal Kotlin function from Java, you can still let that Kotlin function use the full power of coroutines. If you need to call a Java method that performs blocking operations from Kotlin, then
runBlocking(<http://Dispatchers.IO|Dispatchers.IO>)
is the right choice. If this latter is your case, you can still take a look at the project Loom virtual threads in Java
g
here the first snipped of code.
Method called from java
x
There’s something strange for me in that first screenshot. Where does the
CoroutineScope
context comes from that allows you to call the
async
function? Are you creating your own implementation of
CoroutineScope
extending from the interface?
g
Since executeExpressions() is already a suspend function, as long as I know there is already a coroutine scope.
Sorry, I was wrong, async can be executed since I’m extending CoroutineScope in the class that creates coroutines with async, removing my custom implementation of the coroutine scope doesn’t seems to fix the issue 😕.
I’ll follow the conversation tomorrow if you don’t mean, goin to sleep, it’s pretty late over here, so much thanks for your comments.
❤️ 1
x
You shouldn’t have to implement
CoroutineScope
for a case like this. Using
coroutineScope
as in this last example is the correct approach. That is an improvement but not the root cause of your problem. You should call the
executeExpression
suspend function in a
CoroutineScope
instance instead of inside the
runBlocking
scope. This latter one will be blocking threads and you may not be able to launch 400 blocking coroutines. You could instead use
GlobalScope.launch { }
or create your own
CoroutineScope
instance using the
CoroutineScope()
factory function
g
I just wanted to confirm that I’m using coroutines properly while I’m interoperating between Kotlin and Java.
x
We could continue this conversation by DM tomorrow if you want
🙌 1
g
I implemented instead a custom coroutine scope with:
private val supervisor: CompletableJob = _SupervisorJob_()
private val coroutineScope = _CoroutineScope_(supervisor)