Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InvalidMutabilityException when TestScope is frozen #3195

Closed
rickclephas opened this issue Feb 17, 2022 · 2 comments
Closed

InvalidMutabilityException when TestScope is frozen #3195

rickclephas opened this issue Feb 17, 2022 · 2 comments

Comments

@rickclephas
Copy link

I am trying to update my coroutines tests to use runTest instead of runBlocking.
The code under test causes the TestScope to be frozen (which is expected) resulting in the following InvalidMutabilityException:

kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.internal.ThreadSafeHeap@52a0d8
	at kotlin.Throwable#<init>(/opt/buildAgent/work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24)
	at kotlin.Exception#<init>(/opt/buildAgent/work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:23)
	at kotlin.RuntimeException#<init>(/opt/buildAgent/work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:34)
	at kotlin.native.concurrent.InvalidMutabilityException#<init>(/opt/buildAgent/work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt:24)
	at <global>.ThrowInvalidMutabilityException(/opt/buildAgent/work/6326934d18cfe24e/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt:109)
	at <global>.MutationCheck(Unknown Source)
	at kotlinx.coroutines.internal.ThreadSafeHeap.<set-a>#internal(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/internal/ThreadSafeHeap.kt:26)
	at kotlinx.coroutines.internal.ThreadSafeHeap.realloc#internal(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/internal/ThreadSafeHeap.kt:145)
	at kotlinx.coroutines.internal.ThreadSafeHeap#addImpl(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/native/src/Debug.kt:17)
	at kotlinx.coroutines.internal.ThreadSafeHeap#addLast(/opt/buildAgent/work/88b0986a7186d029/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt:38)
	at kotlinx.coroutines.test.TestCoroutineScheduler#registerEvent(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-test/common/src/TestCoroutineScheduler.kt:72)
	at kotlinx.coroutines.test.StandardTestDispatcherImpl.dispatch#internal(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-test/common/src/TestCoroutineDispatchers.kt:155)
	at kotlinx.coroutines.internal#resumeCancellableWith__at__kotlin.coroutines.Continuation<0:0>(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt:282)
	at kotlinx.coroutines.intrinsics#startCoroutineCancellable__at__kotlin.coroutines.SuspendFunction1<0:0,0:1>(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt:30)
	at kotlinx.coroutines#startCoroutineImpl(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/Builders.common.kt:192)
	at kotlinx.coroutines.startCoroutine#internal(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/native/src/Builders.kt:147)
	at kotlinx.coroutines#startAbstractCoroutine(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/native/src/Builders.kt:118)
	at kotlinx.coroutines.AbstractCoroutine#start(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt:130)
	at kotlinx.coroutines#launch__at__kotlinx.coroutines.CoroutineScope(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/Builders.common.kt:56)
	at kotlinx.coroutines#launch$default__at__kotlinx.coroutines.CoroutineScope(/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/Builders.common.kt:47)

The old code looks like this:

@Test
fun `ensure completion callback is invoked`() = runBlocking {
    val flow = flow<RandomValue> {  }
    val job = Job()
    val nativeFlow = flow.asNativeFlow(CoroutineScope(job))
    val completionCount = AtomicInt(0)
    nativeFlow({ _, _ -> }, { error, _ ->
        assertNull(error, "Flow should complete without an error")
        completionCount.increment()
    })
    job.children.forEach { it.join() } // Waits for the collection to complete
    assertEquals(1, completionCount.value, "Completion callback should be called once")
}

converting it to use runTest like this results in the InvalidMutabilityException:

@Test
fun `ensure completion callback is invoked`() = runTest {
    val flow = flow<RandomValue> {  }
    val nativeFlow = flow.asNativeFlow(this)
    val completionCount = AtomicInt(0)
    nativeFlow({ _, _ -> }, { error, _ ->
        assertNull(error, "Flow should complete without an error")
        completionCount.increment()
    })
    runCurrent()
    assertEquals(1, completionCount.value, "Completion callback should be called once")
}
@ankushg
Copy link

ankushg commented Feb 17, 2022

If it helps to have code to check out and run, you can check out this PR: https://github.com/rickclephas/KMP-NativeCoroutines/pull/43/files

  • NativeFlow.kt is what we end up freezing so it can be freely moved across threads. When migrating to runTest, we this freezes TestCoroutineScope and causes this issue
  • AppleNativeFlowTests.kt use the runBlocking approach and are passing.
  • NativeFlowTests.kt use the runTest approach and are failing on Native platforms due to the freezing issue.

@qwwdfsad
Copy link
Collaborator

Please see #462 (comment)

We do no longer support new coroutines features with old memory model, only with a new one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants