Skip to content

Commit 07f730a

Browse files
qwwdfsadzeapo
andauthored
Ensure serialization is usable from K/N background thread
* fix concurrency issue when accessing C2TC This bytearray was mutating itself during the init phase. By moving it to an object, the init {} deals with the initialization and makes it thread safe. The same logic was used for ESCAPE_2_CHAR. * Serialization meets K/N background threads * Run all tests from both main and bg threads * Update Gradle distribution from bin to all * Update JsonReader, get rid of companions and late-initialization to make it K/N friendly and produce less code * Update tests to be bg-friendly Co-authored-by: Mohamed Zenadi <[email protected]>
1 parent 2d96a71 commit 07f730a

File tree

16 files changed

+80
-54
lines changed

16 files changed

+80
-54
lines changed

build.gradle

+2-1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ buildscript {
9191

9292
// To make it visible for compiler-version.gradle
9393
ext.compilerVersion = org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION
94+
ext.nativeDebugBuild = org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType.DEBUG
9495
apply plugin: 'binary-compatibility-validator'
9596

9697
apiValidation {
@@ -184,4 +185,4 @@ if (jvm_ir_enabled) {
184185
}
185186
}
186187
}
187-
}
188+
}

core/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package kotlinx.serialization
66

77
import kotlinx.serialization.modules.*
8+
import kotlin.native.concurrent.*
89

910
@Serializable
1011
open class PolyBase(val id: Int) {
@@ -29,6 +30,7 @@ open class PolyBase(val id: Int) {
2930
@Serializable
3031
data class PolyDerived(val s: String) : PolyBase(1)
3132

33+
@SharedImmutable
3234
val BaseAndDerivedModule = SerializersModule {
3335
polymorphic(PolyBase::class, PolyBase.serializer()) {
3436
subclass(PolyDerived.serializer())

core/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt

+2
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@
55
package kotlinx.serialization.test
66

77
import kotlinx.serialization.test.Platform
8+
import kotlin.native.concurrent.SharedImmutable
89

10+
@SharedImmutable
911
public actual val currentPlatform: Platform = Platform.NATIVE

formats/cbor/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package kotlinx.serialization
66

77
import kotlinx.serialization.modules.*
8+
import kotlin.native.concurrent.*
89

910
@Serializable
1011
abstract class SimpleAbstract
@@ -18,6 +19,7 @@ data class SimpleStringInheritor(val s: String, val i: Int) : SimpleAbstract()
1819
@Serializable
1920
data class PolyBox(@Polymorphic val boxed: SimpleAbstract)
2021

22+
@SharedImmutable
2123
val SimplePolymorphicModule = SerializersModule {
2224
polymorphic(SimpleAbstract::class) {
2325
subclass(SimpleIntInheritor.serializer())

formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonReader.kt

+35-30
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
package kotlinx.serialization.json.internal
66

7-
import kotlinx.serialization.json.internal.EscapeCharMappings.ESCAPE_2_CHAR
7+
import kotlinx.serialization.json.internal.CharMappings.C2TC
8+
import kotlinx.serialization.json.internal.CharMappings.ESCAPE_2_CHAR
89
import kotlin.jvm.*
9-
import kotlin.native.concurrent.*
1010

1111
internal const val lenientHint = "Use 'isLenient = true' in 'Json {}` builder to accept non-compliant JSON."
1212
internal const val coerceInputValuesHint = "Use 'coerceInputValues = true' in 'Json {}` builder to coerce nulls to default values."
@@ -51,32 +51,20 @@ private const val CTC_MAX = 0x7e
5151
// mapping from escape chars real chars
5252
private const val ESC2C_MAX = 0x75
5353

54-
@SharedImmutable
55-
internal val C2TC = ByteArray(CTC_MAX).apply {
56-
for (i in 0..0x20) {
57-
initC2TC(i, TC_INVALID)
58-
}
59-
60-
initC2TC(0x09, TC_WS)
61-
initC2TC(0x0a, TC_WS)
62-
initC2TC(0x0d, TC_WS)
63-
initC2TC(0x20, TC_WS)
64-
initC2TC(COMMA, TC_COMMA)
65-
initC2TC(COLON, TC_COLON)
66-
initC2TC(BEGIN_OBJ, TC_BEGIN_OBJ)
67-
initC2TC(END_OBJ, TC_END_OBJ)
68-
initC2TC(BEGIN_LIST, TC_BEGIN_LIST)
69-
initC2TC(END_LIST, TC_END_LIST)
70-
initC2TC(STRING, TC_STRING)
71-
initC2TC(STRING_ESC, TC_STRING_ESC)
72-
}
54+
// object instead of @SharedImmutable because there is mutual initialization in [initC2ESC] and [initC2TC]
55+
internal object CharMappings {
56+
@JvmField
57+
val ESCAPE_2_CHAR = CharArray(ESC2C_MAX)
7358

74-
// object instead of @SharedImmutable because there is mutual initialization in [initC2ESC]
75-
internal object EscapeCharMappings {
7659
@JvmField
77-
public val ESCAPE_2_CHAR = CharArray(ESC2C_MAX)
60+
val C2TC = ByteArray(CTC_MAX)
7861

7962
init {
63+
initEscape()
64+
initCharToToken()
65+
}
66+
67+
private fun initEscape() {
8068
for (i in 0x00..0x1f) {
8169
initC2ESC(i, UNICODE_ESC)
8270
}
@@ -91,19 +79,36 @@ internal object EscapeCharMappings {
9179
initC2ESC(STRING_ESC, STRING_ESC)
9280
}
9381

82+
private fun initCharToToken() {
83+
for (i in 0..0x20) {
84+
initC2TC(i, TC_INVALID)
85+
}
86+
87+
initC2TC(0x09, TC_WS)
88+
initC2TC(0x0a, TC_WS)
89+
initC2TC(0x0d, TC_WS)
90+
initC2TC(0x20, TC_WS)
91+
initC2TC(COMMA, TC_COMMA)
92+
initC2TC(COLON, TC_COLON)
93+
initC2TC(BEGIN_OBJ, TC_BEGIN_OBJ)
94+
initC2TC(END_OBJ, TC_END_OBJ)
95+
initC2TC(BEGIN_LIST, TC_BEGIN_LIST)
96+
initC2TC(END_LIST, TC_END_LIST)
97+
initC2TC(STRING, TC_STRING)
98+
initC2TC(STRING_ESC, TC_STRING_ESC)
99+
}
100+
94101
private fun initC2ESC(c: Int, esc: Char) {
95102
if (esc != UNICODE_ESC) ESCAPE_2_CHAR[esc.toInt()] = c.toChar()
96103
}
97104

98105
private fun initC2ESC(c: Char, esc: Char) = initC2ESC(c.toInt(), esc)
99-
}
100106

101-
private fun ByteArray.initC2TC(c: Int, cl: Byte) {
102-
this[c] = cl
103-
}
107+
private fun initC2TC(c: Int, cl: Byte) {
108+
C2TC[c] = cl
109+
}
104110

105-
private fun ByteArray.initC2TC(c: Char, cl: Byte) {
106-
initC2TC(c.toInt(), cl)
111+
private fun initC2TC(c: Char, cl: Byte) = initC2TC(c.toInt(), cl)
107112
}
108113

109114
internal fun charToTokenClass(c: Char) = if (c.toInt() < CTC_MAX) C2TC[c.toInt()] else TC_OTHER

formats/json/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package kotlinx.serialization
66

77
import kotlinx.serialization.json.*
88
import kotlinx.serialization.modules.*
9+
import kotlin.native.concurrent.*
910

1011
@Serializable
1112
open class PolyBase(val id: Int) {
@@ -34,6 +35,7 @@ data class PolyDefault(val json: JsonElement) : PolyBase(-1)
3435
@Serializable
3536
data class PolyDerived(val s: String) : PolyBase(1)
3637

38+
@SharedImmutable
3739
val BaseAndDerivedModule = SerializersModule {
3840
polymorphic(PolyBase::class, PolyBase.serializer()) {
3941
subclass(PolyDerived.serializer())

formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package kotlinx.serialization.json.polymorphic
77
import kotlinx.serialization.*
88
import kotlinx.serialization.json.*
99
import kotlinx.serialization.modules.*
10+
import kotlin.native.concurrent.*
1011

1112
@Serializable
1213
internal open class InnerBase
@@ -37,7 +38,7 @@ internal data class OuterBox(@Polymorphic val outerBase: OuterBase, @Polymorphic
3738
@Serializable
3839
internal data class OuterNullableBox(@Polymorphic val outerBase: OuterBase?, @Polymorphic val innerBase: InnerBase?)
3940

40-
41+
@SharedImmutable
4142
internal val polymorphicTestModule = SerializersModule {
4243
polymorphic(InnerBase::class) {
4344
subclass(InnerImpl.serializer())
@@ -50,11 +51,13 @@ internal val polymorphicTestModule = SerializersModule {
5051
}
5152
}
5253

54+
@SharedImmutable
5355
internal val polymorphicJson = Json {
5456
serializersModule = polymorphicTestModule
5557
encodeDefaults = true
5658
}
5759

60+
@SharedImmutable
5861
internal val polymorphicRelaxedJson = Json {
5962
isLenient = true
6063
serializersModule = polymorphicTestModule

formats/json/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt

+3
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@
44

55
package kotlinx.serialization.test
66

7+
import kotlin.native.concurrent.SharedImmutable
78

9+
10+
@SharedImmutable
811
public actual val currentPlatform: Platform = Platform.NATIVE

formats/protobuf/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package kotlinx.serialization
66

77
import kotlinx.serialization.modules.*
88
import kotlinx.serialization.protobuf.*
9+
import kotlin.native.concurrent.*
910

1011
@Serializable
1112
open class PolyBase(@ProtoNumber(1) val id: Int) {
@@ -45,6 +46,7 @@ data class SimpleStringInheritor(val s: String, val i: Int) : SimpleAbstract()
4546
@Serializable
4647
data class PolyBox(@Polymorphic val boxed: SimpleAbstract)
4748

49+
@SharedImmutable
4850
val SimplePolymorphicModule = SerializersModule {
4951
polymorphic(SimpleAbstract::class) {
5052
subclass(SimpleIntInheritor.serializer())

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
group=org.jetbrains.kotlinx
66
version=1.1.0-SNAPSHOT
77

8-
kotlin.version=1.4.30-M1
8+
kotlin.version=1.4.30-RC
99

1010
# This version take precedence if 'bootstrap' property passed to project
1111
kotlin.version.snapshot=1.4.255-SNAPSHOT

gradle/configure-source-sets.gradle

+16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ kotlin {
1212
}
1313
}
1414
}
15+
1516
js {
1617
nodejs {}
1718
configure([compilations.main, compilations.test]) {
@@ -87,4 +88,19 @@ kotlin {
8788
sourceSets.matching({ it.name.contains("Main") }).all { srcSet ->
8889
project.ext.set("kotlin.mpp.freeCompilerArgsForSourceSet.${srcSet.name}", "-Xexplicit-api=warning")
8990
}
91+
92+
def targetsWithoutTestRunners = ["linuxArm32Hfp", "linuxArm64", "mingwX86"]
93+
configure(targets) {
94+
// Configure additional binaries to run tests in the background
95+
if (["macos", "linux", "mingw"].any { name.startsWith(it) && !targetsWithoutTestRunners.contains(name) }) {
96+
binaries {
97+
test("background", [nativeDebugBuild]) {
98+
freeCompilerArgs += ["-trw"]
99+
}
100+
}
101+
testRuns {
102+
background { setExecutionSourceFrom(binaries.backgroundDebugTest) }
103+
}
104+
}
105+
}
90106
}

gradle/native-targets.gradle

-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ kotlin {
8888

8989
targets {
9090
if (project.ext.nativeState == NativeState.DISABLED) return
91-
9291
if (project.ext.singleTargetMode) {
9392
fromPreset(project.ext.ideaPreset, 'native')
9493
} else {

gradle/wrapper/gradle-wrapper.jar

293 Bytes
Binary file not shown.
+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +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+
15
distributionBase=GRADLE_USER_HOME
26
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
7+
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
48
zipStoreBase=GRADLE_USER_HOME
59
zipStorePath=wrapper/dists

gradlew

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ fi
130130
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131131
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132132
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133-
133+
134134
JAVACMD=`cygpath --unix "$JAVACMD"`
135135

136136
# We build the pattern for arguments to be converted via cygpath

gradlew.bat

+3-18
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
4040

4141
set JAVA_EXE=java.exe
4242
%JAVA_EXE% -version >NUL 2>&1
43-
if "%ERRORLEVEL%" == "0" goto init
43+
if "%ERRORLEVEL%" == "0" goto execute
4444

4545
echo.
4646
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -54,7 +54,7 @@ goto fail
5454
set JAVA_HOME=%JAVA_HOME:"=%
5555
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
5656

57-
if exist "%JAVA_EXE%" goto init
57+
if exist "%JAVA_EXE%" goto execute
5858

5959
echo.
6060
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -64,29 +64,14 @@ echo location of your Java installation.
6464

6565
goto fail
6666

67-
:init
68-
@rem Get command-line arguments, handling Windows variants
69-
70-
if not "%OS%" == "Windows_NT" goto win9xME_args
71-
72-
:win9xME_args
73-
@rem Slurp the command line arguments.
74-
set CMD_LINE_ARGS=
75-
set _SKIP=2
76-
77-
:win9xME_args_slurp
78-
if "x%~1" == "x" goto execute
79-
80-
set CMD_LINE_ARGS=%*
81-
8267
:execute
8368
@rem Setup the command line
8469

8570
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
8671

8772

8873
@rem Execute Gradle
89-
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
74+
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
9075

9176
:end
9277
@rem End local scope for the variables with windows NT shell

0 commit comments

Comments
 (0)