Skip to content

Commit 45cdee0

Browse files
committed
Make DescriptorSchemaCache in Json thread-local on Native to avoid freezing problems in custom Json instances
Fixes #1450
1 parent f314e7f commit 45cdee0

File tree

5 files changed

+79
-1
lines changed

5 files changed

+79
-1
lines changed

formats/json/commonMain/src/kotlinx/serialization/json/Json.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public sealed class Json(
5656
override val serializersModule: SerializersModule
5757
) : StringFormat {
5858

59-
internal val schemaCache: DescriptorSchemaCache = DescriptorSchemaCache()
59+
internal val _schemaCache: DescriptorSchemaCache = DescriptorSchemaCache()
6060

6161
/**
6262
* The default instance of [Json] with default configuration.
@@ -291,5 +291,10 @@ private class JsonImpl(configuration: JsonConfiguration, module: SerializersModu
291291
}
292292
}
293293

294+
/**
295+
* Emulate thread-locality of cache on Native
296+
*/
297+
internal expect val Json.schemaCache: DescriptorSchemaCache
298+
294299
private const val defaultIndent = " "
295300
private const val defaultDiscriminator = "type"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.serialization.json
6+
7+
import kotlinx.serialization.json.internal.*
8+
9+
internal actual val Json.schemaCache: DescriptorSchemaCache get() = this._schemaCache
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.serialization.json
6+
7+
import kotlinx.serialization.json.internal.*
8+
9+
internal actual val Json.schemaCache: DescriptorSchemaCache get() = this._schemaCache
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.serialization.json
6+
7+
import kotlinx.serialization.json.internal.*
8+
9+
@ThreadLocal
10+
private val jsonToCache: MutableMap<Json, DescriptorSchemaCache> = mutableMapOf()
11+
12+
/**
13+
* Emulate thread locality of cache on Native
14+
*/
15+
internal actual val Json.schemaCache: DescriptorSchemaCache
16+
get() = jsonToCache.getOrPut(this) { DescriptorSchemaCache() }
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.serialization.json
6+
7+
import kotlinx.serialization.*
8+
import kotlin.native.concurrent.*
9+
import kotlin.test.*
10+
11+
class MultiWorkerJsonTest {
12+
@Serializable
13+
data class PlainOne(val one: Int)
14+
15+
@Serializable
16+
data class PlainTwo(val two: Int)
17+
18+
19+
@Test
20+
fun testJsonIsFreezeSafe() {
21+
val json = Json {
22+
isLenient = true
23+
ignoreUnknownKeys = true
24+
useAlternativeNames = true
25+
}
26+
val worker = Worker.start()
27+
val operation = {
28+
for (i in 0..999) {
29+
assertEquals(PlainOne(42), json.decodeFromString("""{"one":42,"two":239}"""))
30+
}
31+
}
32+
worker.executeAfter(1000, operation.freeze())
33+
for (i in 0..999) {
34+
assertEquals(PlainTwo(239), json.decodeFromString("""{"one":42,"two":239}"""))
35+
}
36+
worker.requestTermination()
37+
// Should be completed successfully
38+
}
39+
}

0 commit comments

Comments
 (0)