I ran into this behavior when I was trying to write up a sample that showed how to interop between Java and Kotlin. It looks like somehow we end up deadlocking our internals when using writeBlocking and Flow. Not 100% sure how it is happening though.
This JVM unit test demonstrates the behavior:
@Test
fun writeBlockingAndFlows() {
val tmpDir = PlatformUtils.createTempDir()
val configuration = RealmConfiguration.Builder(setOf(Parent::class, Child::class))
.directory(tmpDir)
.build()
val realmScope = CoroutineScope(CoroutineName("RealmScope") + Dispatchers.Default)
val realm = Realm.open(configuration)
// Do initial write
realm.writeBlocking {
copyToRealm(Parent())
}
assertEquals(1, realm.query<Parent>().count().find())
println("Wrote 1st data")
// Start notifications
val updateLatch = CountDownLatch(1)
val job = realmScope.launch {
realm.query<Parent>().asFlow().collect {
println(it)
if (it is UpdatedResults) {
updateLatch.countDown()
}
}
}
println("Started observer")
// Do a 2nd write
realm.writeBlocking {
copyToRealm(Parent())
}
println("Wrote 2nd data")
// Wait for the Update notification. This will currently fail, and all Coroutines are in
// the WAITING state.
// Note: If we move the notification block to run as the first thing, the writes do
// not block it.
if (!updateLatch.await(10, TimeUnit.SECONDS)) {
fail("Did not receive update notification in time")
}
job.cancel()
realm.close()
}
I made a mistake in reasoning about the flow of events. The 2nd write could happen before the listener fired the first event, which meant that no update was ever coming.
I ran into this behavior when I was trying to write up a sample that showed how to interop between Java and Kotlin. It looks like somehow we end up deadlocking our internals when using
writeBlocking
andFlow
. Not 100% sure how it is happening though.This JVM unit test demonstrates the behavior:
The thread dump look like this: