Skip to content

Commit 3068e7e

Browse files
committed
Create a layer based cache that does share the data across threads, but delays merging until after (de)serialization. This is still a partial solution as maximum cache size will still need to be handled.
1 parent 5fa8cc0 commit 3068e7e

File tree

19 files changed

+387
-85
lines changed

19 files changed

+387
-85
lines changed

serialization/api/serialization.api

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ public final class nl/adaptivity/xmlutil/serialization/CompactFragmentSerializer
2828
public static final synthetic fun serializer (Lnl/adaptivity/xmlutil/util/CompactFragment$Companion;)Lkotlinx/serialization/KSerializer;
2929
}
3030

31-
public final class nl/adaptivity/xmlutil/serialization/DefaultFormatCache : nl/adaptivity/xmlutil/serialization/FormatCache {
31+
public final class nl/adaptivity/xmlutil/serialization/DefaultFormatCache : nl/adaptivity/xmlutil/serialization/FormatCache, nl/adaptivity/xmlutil/serialization/DelegatableFormatCache {
3232
public fun <init> ()V
3333
public synthetic fun copy$serialization ()Lnl/adaptivity/xmlutil/serialization/FormatCache;
34-
public synthetic fun unsafeCache$serialization ()Lnl/adaptivity/xmlutil/serialization/FormatCache;
3534
}
3635

3736
public final class nl/adaptivity/xmlutil/serialization/DefaultPlatformModuleKt {
@@ -143,7 +142,6 @@ public abstract class nl/adaptivity/xmlutil/serialization/FormatCache {
143142

144143
public final class nl/adaptivity/xmlutil/serialization/FormatCache$Dummy : nl/adaptivity/xmlutil/serialization/FormatCache {
145144
public static final field INSTANCE Lnl/adaptivity/xmlutil/serialization/FormatCache$Dummy;
146-
public synthetic fun unsafeCache$serialization ()Lnl/adaptivity/xmlutil/serialization/FormatCache;
147145
}
148146

149147
public final class nl/adaptivity/xmlutil/serialization/FormatCache_javaSharedKt {
@@ -179,6 +177,10 @@ public final class nl/adaptivity/xmlutil/serialization/KotlinxSerializationProvi
179177
public fun serializer (Lkotlin/reflect/KClass;)Lnl/adaptivity/xmlutil/util/SerializationProvider$XmlSerializerFun;
180178
}
181179

180+
public final class nl/adaptivity/xmlutil/serialization/LayeredCache : nl/adaptivity/xmlutil/serialization/FormatCache {
181+
public fun <init> ()V
182+
}
183+
182184
public final class nl/adaptivity/xmlutil/serialization/NodeSerializer : nl/adaptivity/xmlutil/XmlSerializer {
183185
public static final field INSTANCE Lnl/adaptivity/xmlutil/serialization/NodeSerializer;
184186
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;

serialization/api/serialization.klib.api

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,10 +586,14 @@ final class nl.adaptivity.xmlutil.serialization.structure/XmlTypeDescriptor { //
586586
final fun toString(): kotlin/String // nl.adaptivity.xmlutil.serialization.structure/XmlTypeDescriptor.toString|toString(){}[0]
587587
}
588588

589-
final class nl.adaptivity.xmlutil.serialization/DefaultFormatCache : nl.adaptivity.xmlutil.serialization/FormatCache { // nl.adaptivity.xmlutil.serialization/DefaultFormatCache|null[0]
589+
final class nl.adaptivity.xmlutil.serialization/DefaultFormatCache : nl.adaptivity.xmlutil.serialization/DelegatableFormatCache, nl.adaptivity.xmlutil.serialization/FormatCache { // nl.adaptivity.xmlutil.serialization/DefaultFormatCache|null[0]
590590
constructor <init>() // nl.adaptivity.xmlutil.serialization/DefaultFormatCache.<init>|<init>(){}[0]
591591
}
592592

593+
final class nl.adaptivity.xmlutil.serialization/LayeredCache : nl.adaptivity.xmlutil.serialization/FormatCache { // nl.adaptivity.xmlutil.serialization/LayeredCache|null[0]
594+
constructor <init>() // nl.adaptivity.xmlutil.serialization/LayeredCache.<init>|<init>(){}[0]
595+
}
596+
593597
final class nl.adaptivity.xmlutil.serialization/PolyInfo { // nl.adaptivity.xmlutil.serialization/PolyInfo|null[0]
594598
constructor <init>(nl.adaptivity.xmlutil/QName, kotlin/Int, nl.adaptivity.xmlutil.serialization.structure/XmlDescriptor) // nl.adaptivity.xmlutil.serialization/PolyInfo.<init>|<init>(nl.adaptivity.xmlutil.QName;kotlin.Int;nl.adaptivity.xmlutil.serialization.structure.XmlDescriptor){}[0]
595599

serialization/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ kotlin {
173173
if (this.name == "nativeMain") {
174174
dependencies {
175175
api(projects.core)
176+
implementation(libs.kotlinx.atomicfu)
176177
}
177178
}
178179
if (System.getProperty("idea.active") == "true" && name == "nativeTest") { // Hackery to get at the native source sets that shouldn't be needed

serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/DefaultFormatCache.kt

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import kotlinx.serialization.descriptors.SerialKind
2727
import kotlinx.serialization.descriptors.StructureKind
2828
import nl.adaptivity.xmlutil.Namespace
2929
import nl.adaptivity.xmlutil.QName
30+
import nl.adaptivity.xmlutil.XmlUtilInternal
3031
import nl.adaptivity.xmlutil.namespaceURI
3132
import nl.adaptivity.xmlutil.serialization.XML.XmlCodecConfig
3233
import nl.adaptivity.xmlutil.serialization.structure.*
@@ -37,50 +38,86 @@ import kotlin.jvm.JvmStatic
3738
* intended to be stored on the config, thus reused through multiple serializations.
3839
* Note that this requires the `serialName` attribute of `SerialDescriptor` instances to be unique.
3940
*/
40-
public class DefaultFormatCache : FormatCache() {
41+
public class DefaultFormatCache : FormatCache(), DelegatableFormatCache {
4142
private val typeDescCache = HashMap<TypeKey, XmlTypeDescriptor>()
4243
private val elemDescCache = HashMap<DescKey, XmlDescriptor>()
4344
private val pendingDescs = HashSet<DescKey>()
4445

4546
override fun copy(): DefaultFormatCache = DefaultFormatCache()
4647

47-
override fun unsafeCache(): DefaultFormatCache {
48-
return this
48+
override fun <R> useUnsafe(action: (FormatCache) -> R): R {
49+
return action(this)
4950
}
5051

51-
@OptIn(ExperimentalSerializationApi::class)
52+
@XmlUtilInternal
5253
override fun lookupType(
54+
namespace: Namespace?,
55+
serialDesc: SerialDescriptor
56+
): XmlTypeDescriptor? {
57+
return lookupType(TypeKey(namespace?.namespaceURI, serialDesc), serialDesc.kind)
58+
}
59+
60+
@OptIn(ExperimentalSerializationApi::class)
61+
override fun lookupTypeOrStore(
5362
namespace: Namespace?,
5463
serialDesc: SerialDescriptor,
5564
defaultValue: () -> XmlTypeDescriptor
5665
): XmlTypeDescriptor {
57-
return lookupType(TypeKey(namespace?.namespaceURI, serialDesc), serialDesc.kind, defaultValue)
66+
return lookupTypeOrStore(TypeKey(namespace?.namespaceURI, serialDesc), serialDesc.kind, defaultValue)
67+
}
68+
69+
@XmlUtilInternal
70+
override fun lookupType(
71+
parentName: QName,
72+
serialDesc: SerialDescriptor
73+
): XmlTypeDescriptor? {
74+
return lookupType(TypeKey(parentName.namespaceURI, serialDesc), serialDesc.kind)
5875
}
5976

6077
/**
6178
* Lookup a type descriptor for this type with the given namespace.
6279
* @param parentName A key
6380
*/
6481
@OptIn(ExperimentalSerializationApi::class)
65-
override fun lookupType(
82+
override fun lookupTypeOrStore(
6683
parentName: QName,
6784
serialDesc: SerialDescriptor,
6885
defaultValue: () -> XmlTypeDescriptor
6986
): XmlTypeDescriptor {
70-
return lookupType(TypeKey(parentName.namespaceURI, serialDesc), serialDesc.kind, defaultValue)
87+
return lookupTypeOrStore(TypeKey(parentName.namespaceURI, serialDesc), serialDesc.kind, defaultValue)
7188
}
7289

73-
@OptIn(ExperimentalSerializationApi::class)
74-
private fun lookupType(name: TypeKey, kind: SerialKind, defaultValue: () -> XmlTypeDescriptor): XmlTypeDescriptor {
75-
return when (kind) {
76-
StructureKind.MAP,
77-
StructureKind.LIST -> defaultValue()
90+
private fun lookupType(
91+
name: TypeKey, kind: SerialKind
92+
): XmlTypeDescriptor? = when (kind) {
93+
StructureKind.MAP,
94+
StructureKind.LIST -> null
7895

79-
else -> typeDescCache.getOrPut(name, defaultValue)
80-
}
96+
else -> typeDescCache.get(name)
8197
}
8298

99+
100+
@OptIn(ExperimentalSerializationApi::class)
101+
private fun lookupTypeOrStore(name: TypeKey, kind: SerialKind, defaultValue: () -> XmlTypeDescriptor): XmlTypeDescriptor {
102+
lookupType(name, kind)?.let { return it}
103+
val v = defaultValue()
104+
typeDescCache[name] = v
105+
return v
106+
}
107+
108+
@XmlUtilInternal
83109
override fun lookupDescriptor(
110+
overridenSerializer: KSerializer<*>?,
111+
serializerParent: SafeParentInfo,
112+
tagParent: SafeParentInfo,
113+
canBeAttribute: Boolean
114+
): XmlDescriptor? {
115+
val key =
116+
DescKey(overridenSerializer, serializerParent, tagParent.takeIf { it !== serializerParent }, canBeAttribute)
117+
return elemDescCache[key]
118+
}
119+
120+
override fun lookupDescriptorOrStore(
84121
overridenSerializer: KSerializer<*>?,
85122
serializerParent: SafeParentInfo,
86123
tagParent: SafeParentInfo,
@@ -113,6 +150,12 @@ public class DefaultFormatCache : FormatCache() {
113150
return XmlCompositeDescriptor(codecConfig, serializerParent, tagParent, preserveSpace)
114151
}
115152

153+
internal fun appendFrom(other: DefaultFormatCache) {
154+
check(pendingDescs.isEmpty()) { "This cache is not stable, refusing to add elements" }
155+
typeDescCache.putAll(other.typeDescCache)
156+
elemDescCache.putAll(other.elemDescCache)
157+
}
158+
116159
/**
117160
* @property overridenSerializer If the serializer is different, this changes the key
118161
* @property parentNamespace If the parent has a different namespace, this may change the name

serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/FormatCache.kt

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,26 @@ import kotlinx.serialization.KSerializer
2424
import kotlinx.serialization.descriptors.SerialDescriptor
2525
import nl.adaptivity.xmlutil.Namespace
2626
import nl.adaptivity.xmlutil.QName
27-
import nl.adaptivity.xmlutil.serialization.FormatCache.Dummy
27+
import nl.adaptivity.xmlutil.XmlUtilInternal
2828
import nl.adaptivity.xmlutil.serialization.structure.*
2929

30+
/**
31+
* Interface for format caches that support being delegated to.
32+
*/
33+
@XmlUtilInternal
34+
public interface DelegatableFormatCache {
35+
@XmlUtilInternal
36+
public fun lookupType(namespace: Namespace?, serialDesc: SerialDescriptor): XmlTypeDescriptor?
37+
@XmlUtilInternal
38+
public fun lookupType(parentName: QName, serialDesc: SerialDescriptor): XmlTypeDescriptor?
39+
@XmlUtilInternal
40+
public fun lookupDescriptor(
41+
overridenSerializer: KSerializer<*>?,
42+
serializerParent: SafeParentInfo,
43+
tagParent: SafeParentInfo,
44+
canBeAttribute: Boolean): XmlDescriptor?
45+
}
46+
3047
/**
3148
* The FormatCache caches the calculations needed to determine the correct format for a specific
3249
* serializable tree. There are 3 options provided by default:
@@ -39,20 +56,23 @@ import nl.adaptivity.xmlutil.serialization.structure.*
3956
*
4057
*/
4158
public abstract class FormatCache internal constructor() {
42-
internal abstract fun lookupType(namespace: Namespace?, serialDesc: SerialDescriptor, defaultValue: () -> XmlTypeDescriptor): XmlTypeDescriptor
59+
internal abstract fun lookupTypeOrStore(namespace: Namespace?, serialDesc: SerialDescriptor, defaultValue: () -> XmlTypeDescriptor): XmlTypeDescriptor
4360

4461
internal abstract fun copy(): FormatCache
4562

46-
/** Retrieve a cache implementation that is not thread safe. Used by the format to avoid looking up thread locals. */
47-
internal abstract fun unsafeCache(): FormatCache
63+
/** Perform an operation with a cache implementation that is not thread safe. Used
64+
* by the format to avoid looking up thread locals. It will allow uusing the shared cache
65+
* on completion.
66+
*/
67+
internal abstract fun <R> useUnsafe(action: (FormatCache) -> R): R
4868

4969
/**
5070
* Lookup a type descriptor for this type with the given namespace.
5171
* @param parentName A key
5272
*/
53-
internal abstract fun lookupType(parentName: QName, serialDesc: SerialDescriptor, defaultValue: () -> XmlTypeDescriptor): XmlTypeDescriptor
73+
internal abstract fun lookupTypeOrStore(parentName: QName, serialDesc: SerialDescriptor, defaultValue: () -> XmlTypeDescriptor): XmlTypeDescriptor
5474

55-
internal abstract fun lookupDescriptor(
75+
internal abstract fun lookupDescriptorOrStore(
5676
overridenSerializer: KSerializer<*>?,
5777
serializerParent: SafeParentInfo,
5878
tagParent: SafeParentInfo,
@@ -71,21 +91,23 @@ public abstract class FormatCache internal constructor() {
7191

7292
override fun copy(): FormatCache = this
7393

74-
override fun unsafeCache(): Dummy = this
94+
override fun <R> useUnsafe(action: (FormatCache) -> R): R {
95+
return action(this)
96+
}
7597

76-
override fun lookupType(
98+
override fun lookupTypeOrStore(
7799
namespace: Namespace?,
78100
serialDesc: SerialDescriptor,
79101
defaultValue: () -> XmlTypeDescriptor
80102
): XmlTypeDescriptor = defaultValue()
81103

82-
override fun lookupType(
104+
override fun lookupTypeOrStore(
83105
parentName: QName,
84106
serialDesc: SerialDescriptor,
85107
defaultValue: () -> XmlTypeDescriptor
86108
): XmlTypeDescriptor = defaultValue()
87109

88-
override fun lookupDescriptor(
110+
override fun lookupDescriptorOrStore(
89111
overridenSerializer: KSerializer<*>?,
90112
serializerParent: SafeParentInfo,
91113
tagParent: SafeParentInfo,

0 commit comments

Comments
 (0)