From 182bb4c8ba030137b0e52650f63b8fe2ac051e07 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 18 Aug 2023 13:59:57 +0700 Subject: [PATCH] Make Codec2RecipeSerializer thread safe --- .../mc/otm/data/Codec2RecipeSerializer.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/Codec2RecipeSerializer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/Codec2RecipeSerializer.kt index bd43dd751..16e1830f7 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/Codec2RecipeSerializer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/Codec2RecipeSerializer.kt @@ -18,16 +18,25 @@ import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.core.util.readBinaryJsonWithCodecIndirect import ru.dbotthepony.mc.otm.core.util.writeBinaryJsonWithCodec import kotlin.collections.ArrayDeque +import kotlin.concurrent.getOrSet class Codec2RecipeSerializer> private constructor( val empty: S?, - private val id: ArrayDeque, + + /** + * [ThreadLocal] because optimization mods can (and probably should) parallelize recipe deserialization, + * since RecipeSerializers are expected to be stateless. [Codec2RecipeSerializer], however, is stateful (threading PoV). + * To make it stateless, [ThreadLocal] is used. + */ + private val idStack: ThreadLocal>, codec: (Codec2RecipeSerializer.Context) -> Codec, ) : Codec, RecipeSerializer { - constructor(empty: S?, codec: (Codec2RecipeSerializer.Context) -> Codec) : this(empty, ArrayDeque(), codec) + constructor(empty: S?, codec: (Codec2RecipeSerializer.Context) -> Codec) : this(empty, ThreadLocal(), codec) constructor(supplier: (Codec2RecipeSerializer.Context) -> Codec) : this(null, supplier) private val codec = codec.invoke(Context()) + private val id: ArrayDeque + get() = idStack.getOrSet { ArrayDeque() } inner class Context() { val id: ResourceLocation @@ -65,7 +74,7 @@ class Codec2RecipeSerializer> private constructor( } fun > xmap(to: (S) -> O, from: (O) -> S): Codec2RecipeSerializer { - return Codec2RecipeSerializer(empty?.let(to), id) { _ -> + return Codec2RecipeSerializer(empty?.let(to), idStack) { _ -> codec.xmap(to, from) } }