Kodein + Ktor = 冰冻变异尝试 kotlin.collections.HashMap - 为什么?
Kodein + Ktor = mutation attempt of frozen kotlin.collections.HashMap - why?
最近几天我一直在为这个异常而苦恼。
我有一个具有这些依赖项的 kotlin 多平台项目:
- kotlin=1.5.10
- kodein=7.6.0
- ktor=1.6.0(内部使用 kotlin 协程 1.5.0-native-mt)
而且我在尝试在本机中使用 httpClient 时遇到了异常:
at kotlin.Throwable#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/Throwable.kt:23)
at kotlin.Exception#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/Exceptions.kt:23)
at kotlin.RuntimeException#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/Exceptions.kt:34)
at kotlin.native.concurrent.InvalidMutabilityException#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt:22)
at <global>.ThrowInvalidMutabilityException(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt:93)
at <global>.MutationCheck(Unknown Source)
at kotlin.collections.HashMap.<set-length>#internal(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:16)
at kotlin.collections.HashMap#addKey(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:292)
at kotlin.collections.HashMap#put(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:68)
at org.kodein.di.internal.DITreeImpl#find(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DITreeImpl.kt:132)
at org.kodein.di.DITree#find$default(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DITree.kt:36)
at org.kodein.di.internal.DIContainerImpl#factory(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIContainerImpl.kt:158)
at org.kodein.di.DIContainer#factory$default(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DIContainer.kt:32)
at org.kodein.di.DIContainer#provider(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DIContainer.kt:76)
at org.kodein.di.DIContainer#provider$default(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DIContainer.kt:75)
at org.kodein.di.internal.DirectDIBaseImpl#Instance(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DirectDIImpl.kt:30)
at InvalidMutabilitySampleTest.<init>$lambda-6$lambda-5$lambda-4$lambda-3$lambda-2#internal(/Users/r.juszczyk/StudioProjects/lmhu-multiplatform-app/MultiplatformApp/src/iosTest/kotlin/InvalidMutabilitySampleTest.kt:26)
at InvalidMutabilitySampleTest.$<init>$lambda-6$lambda-5$lambda-4$lambda-3$lambda-2$FUNCTION_REFERENCE.invoke#internal(/Users/r.juszczyk/StudioProjects/lmhu-multiplatform-app/MultiplatformApp/src/iosTest/kotlin/InvalidMutabilitySampleTest.kt:25)
at InvalidMutabilitySampleTest.$<init>$lambda-6$lambda-5$lambda-4$lambda-3$lambda-2$FUNCTION_REFERENCE.$<bridge-UNNN>invoke(/Users/r.juszczyk/StudioProjects/lmhu-multiplatform-app/MultiplatformApp/src/iosTest/kotlin/InvalidMutabilitySampleTest.kt:25)
at io.ktor.client.HttpClientConfig.install$<anonymous>_1#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:69)
at io.ktor.client.HttpClientConfig.$install$<anonymous>_1$FUNCTION_REFERENCE.invoke#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:65)
at io.ktor.client.HttpClientConfig.$install$<anonymous>_1$FUNCTION_REFERENCE.$<bridge-UNNN>invoke(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:65)
at io.ktor.client.features.json.JsonFeature.Feature#prepare(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-features/ktor-client-json/common/src/io/ktor/client/features/json/JsonFeature.kt:129)
at io.ktor.client.HttpClientConfig.install$<anonymous>_1-2#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:77)
at io.ktor.client.HttpClientConfig.$install$<anonymous>_1-2$FUNCTION_REFERENCE.invoke#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:74)
at io.ktor.client.HttpClientConfig.$install$<anonymous>_1-2$FUNCTION_REFERENCE.$<bridge-UNNN>invoke(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:74)
at io.ktor.client.HttpClientConfig#install(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:97)
at io.ktor.client.HttpClient#<init>(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClient.kt:172)
at io.ktor.client.HttpClient#<init>(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClient.kt:81)
at io.ktor.client#HttpClient(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClient.kt:43)
我设法在测试中重现了那个崩溃:
class InvalidMutabilitySampleTest {
val di = DI {
import(DI.Module("Some Module") {
bind<Json>() with provider {
Json {
prettyPrint = true
isLenient = true
}
}
bind<HttpClient>() with provider {
HttpClient{
install(JsonFeature) {
serializer = KotlinxSerializer(instance())
}
}
}
})
}
val httpClient: HttpClient by di.instance()
@Test
fun invalidMutabilityTest() {
println(httpClient)
}
}
我也设法通过更改来修复它:
HttpClient{
install(JsonFeature) {
serializer = KotlinxSerializer(instance())
}
}
至:
val json = instance<Json>()
HttpClient{
install(JsonFeature) {
serializer = KotlinxSerializer(json)
}
}
然后我注意到 HttpClient 做了一件非常具体的事情 - 它在 init 块中冻结了自己。
我设法用这个示例代码重现了它:
class FrozenConstructor(val block: ()->Unit) {
init {
freeze()
}
}
class InvalidMutabilitySampleTest2 {
val di = DI {
import(DI.Module("Some Module") {
bind<String>() with provider {
"lolo"
}
bind<FrozenConstructor>() with provider {
FrozenConstructor{
instance<String>()
}
}
})
}
val frozenConstructor: FrozenConstructor by di.instance()
@Test
fun invalidMutabilityTest() {
println(frozenConstructor)
}
}
所以我的理论如下:
- kodein 尝试提供
FrozenConstructor
class
FrozenConstructor
被创建并冻结自身,以及它引用 kodein 的成员
- kodein 尝试缓存提供的依赖项并尝试改变内部
MutableMap
,它被冻结并且一切都崩溃了
有人可以确认这或多或少是正确的,如果不正确请纠正我?
还有你们能建议最好的处理方法,以及其他等待那里的陷阱吗?
是否是 kodein 错误?
如果我使用 with provider
而不是 with singleton
为什么 kodein 必须在可变映射中存储一些东西?
我想你已经很明白了。 Ktor 冻结了自身及其所有配置,以确保它可以在 Kotlin/Native 中跨线程使用。 Kodein 假设你只会从一个线程中接触它并且冻结是不安全的。 (无论这是限制、错误还是设计缺陷,都可能需要解释。)
要解决这些问题,您需要避免在 HttpClient
配置中意外捕获引用 Kodein 内部结构的 this
引用。正如您所发现的,一个很好的方法是在 HttpClient
lambda 之外的辅助变量中从 DI 中获取实例。
你是对的。
FrozenConstructor
冻结自身,因此其 block
属性,作为 lambda,冻结其所有捕获,包括 DI 容器。
Kodein-DI 不支持冻结(因此不支持原生多线程)。
自 JB 宣布开发新的 GC 以来,已决定不投资使其与本机多线程兼容。
最近几天我一直在为这个异常而苦恼。
我有一个具有这些依赖项的 kotlin 多平台项目:
- kotlin=1.5.10
- kodein=7.6.0
- ktor=1.6.0(内部使用 kotlin 协程 1.5.0-native-mt)
而且我在尝试在本机中使用 httpClient 时遇到了异常:
at kotlin.Throwable#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/Throwable.kt:23)
at kotlin.Exception#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/Exceptions.kt:23)
at kotlin.RuntimeException#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/Exceptions.kt:34)
at kotlin.native.concurrent.InvalidMutabilityException#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt:22)
at <global>.ThrowInvalidMutabilityException(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt:93)
at <global>.MutationCheck(Unknown Source)
at kotlin.collections.HashMap.<set-length>#internal(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:16)
at kotlin.collections.HashMap#addKey(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:292)
at kotlin.collections.HashMap#put(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:68)
at org.kodein.di.internal.DITreeImpl#find(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DITreeImpl.kt:132)
at org.kodein.di.DITree#find$default(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DITree.kt:36)
at org.kodein.di.internal.DIContainerImpl#factory(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIContainerImpl.kt:158)
at org.kodein.di.DIContainer#factory$default(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DIContainer.kt:32)
at org.kodein.di.DIContainer#provider(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DIContainer.kt:76)
at org.kodein.di.DIContainer#provider$default(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DIContainer.kt:75)
at org.kodein.di.internal.DirectDIBaseImpl#Instance(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DirectDIImpl.kt:30)
at InvalidMutabilitySampleTest.<init>$lambda-6$lambda-5$lambda-4$lambda-3$lambda-2#internal(/Users/r.juszczyk/StudioProjects/lmhu-multiplatform-app/MultiplatformApp/src/iosTest/kotlin/InvalidMutabilitySampleTest.kt:26)
at InvalidMutabilitySampleTest.$<init>$lambda-6$lambda-5$lambda-4$lambda-3$lambda-2$FUNCTION_REFERENCE.invoke#internal(/Users/r.juszczyk/StudioProjects/lmhu-multiplatform-app/MultiplatformApp/src/iosTest/kotlin/InvalidMutabilitySampleTest.kt:25)
at InvalidMutabilitySampleTest.$<init>$lambda-6$lambda-5$lambda-4$lambda-3$lambda-2$FUNCTION_REFERENCE.$<bridge-UNNN>invoke(/Users/r.juszczyk/StudioProjects/lmhu-multiplatform-app/MultiplatformApp/src/iosTest/kotlin/InvalidMutabilitySampleTest.kt:25)
at io.ktor.client.HttpClientConfig.install$<anonymous>_1#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:69)
at io.ktor.client.HttpClientConfig.$install$<anonymous>_1$FUNCTION_REFERENCE.invoke#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:65)
at io.ktor.client.HttpClientConfig.$install$<anonymous>_1$FUNCTION_REFERENCE.$<bridge-UNNN>invoke(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:65)
at io.ktor.client.features.json.JsonFeature.Feature#prepare(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-features/ktor-client-json/common/src/io/ktor/client/features/json/JsonFeature.kt:129)
at io.ktor.client.HttpClientConfig.install$<anonymous>_1-2#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:77)
at io.ktor.client.HttpClientConfig.$install$<anonymous>_1-2$FUNCTION_REFERENCE.invoke#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:74)
at io.ktor.client.HttpClientConfig.$install$<anonymous>_1-2$FUNCTION_REFERENCE.$<bridge-UNNN>invoke(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:74)
at io.ktor.client.HttpClientConfig#install(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:97)
at io.ktor.client.HttpClient#<init>(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClient.kt:172)
at io.ktor.client.HttpClient#<init>(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClient.kt:81)
at io.ktor.client#HttpClient(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClient.kt:43)
我设法在测试中重现了那个崩溃:
class InvalidMutabilitySampleTest {
val di = DI {
import(DI.Module("Some Module") {
bind<Json>() with provider {
Json {
prettyPrint = true
isLenient = true
}
}
bind<HttpClient>() with provider {
HttpClient{
install(JsonFeature) {
serializer = KotlinxSerializer(instance())
}
}
}
})
}
val httpClient: HttpClient by di.instance()
@Test
fun invalidMutabilityTest() {
println(httpClient)
}
}
我也设法通过更改来修复它:
HttpClient{
install(JsonFeature) {
serializer = KotlinxSerializer(instance())
}
}
至:
val json = instance<Json>()
HttpClient{
install(JsonFeature) {
serializer = KotlinxSerializer(json)
}
}
然后我注意到 HttpClient 做了一件非常具体的事情 - 它在 init 块中冻结了自己。 我设法用这个示例代码重现了它:
class FrozenConstructor(val block: ()->Unit) {
init {
freeze()
}
}
class InvalidMutabilitySampleTest2 {
val di = DI {
import(DI.Module("Some Module") {
bind<String>() with provider {
"lolo"
}
bind<FrozenConstructor>() with provider {
FrozenConstructor{
instance<String>()
}
}
})
}
val frozenConstructor: FrozenConstructor by di.instance()
@Test
fun invalidMutabilityTest() {
println(frozenConstructor)
}
}
所以我的理论如下:
- kodein 尝试提供
FrozenConstructor
class FrozenConstructor
被创建并冻结自身,以及它引用 kodein 的成员
- kodein 尝试缓存提供的依赖项并尝试改变内部
MutableMap
,它被冻结并且一切都崩溃了
有人可以确认这或多或少是正确的,如果不正确请纠正我?
还有你们能建议最好的处理方法,以及其他等待那里的陷阱吗?
是否是 kodein 错误?
如果我使用 with provider
而不是 with singleton
为什么 kodein 必须在可变映射中存储一些东西?
我想你已经很明白了。 Ktor 冻结了自身及其所有配置,以确保它可以在 Kotlin/Native 中跨线程使用。 Kodein 假设你只会从一个线程中接触它并且冻结是不安全的。 (无论这是限制、错误还是设计缺陷,都可能需要解释。)
要解决这些问题,您需要避免在 HttpClient
配置中意外捕获引用 Kodein 内部结构的 this
引用。正如您所发现的,一个很好的方法是在 HttpClient
lambda 之外的辅助变量中从 DI 中获取实例。
你是对的。
FrozenConstructor
冻结自身,因此其 block
属性,作为 lambda,冻结其所有捕获,包括 DI 容器。
Kodein-DI 不支持冻结(因此不支持原生多线程)。 自 JB 宣布开发新的 GC 以来,已决定不投资使其与本机多线程兼容。