Refactor thread-local ScriptEngine
authorLanius Trolling <lanius@laniustrolling.dev>
Sun, 28 Apr 2024 16:00:57 +0000 (12:00 -0400)
committerLanius Trolling <lanius@laniustrolling.dev>
Sun, 28 Apr 2024 16:02:50 +0000 (12:02 -0400)
src/jvmMain/kotlin/info/mechyrdia/lore/ParserPreprocessInclude.kt

index 20904f68d2fc059567c22987801ddc74c5220848..a4309ab40cb880ae16d2135363ac0e297d1b3d34 100644 (file)
@@ -5,16 +5,18 @@ import info.mechyrdia.data.FileStorage
 import info.mechyrdia.data.StoragePath
 import io.ktor.util.*
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runInterruptible
 import kotlinx.coroutines.suspendCancellableCoroutine
-import kotlinx.coroutines.withContext
 import kotlinx.serialization.json.*
 import java.security.MessageDigest
 import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.locks.ReentrantLock
 import java.util.function.Consumer
 import javax.script.Compilable
 import javax.script.CompiledScript
 import javax.script.ScriptEngineManager
 import javax.script.SimpleBindings
+import kotlin.concurrent.withLock
 import kotlin.coroutines.*
 
 object PreProcessorTemplateLoader {
@@ -34,7 +36,8 @@ object PreProcessorTemplateLoader {
 }
 
 object PreProcessorScriptLoader {
-       private val scriptEngine = ThreadLocal.withInitial { ScriptEngineManager().getEngineByExtension("groovy") }
+       private val scriptEngine = ScriptEngineManager().getEngineByExtension("groovy")
+       private val scriptEngineSync = ReentrantLock(true)
        private val hasher = ThreadLocal.withInitial { MessageDigest.getInstance("SHA-256") }
        private val cache = ConcurrentHashMap<String, CompiledScript>()
        
@@ -42,10 +45,13 @@ object PreProcessorScriptLoader {
                val scriptFile = StoragePath.scriptDir / "$name.groovy"
                val script = FileStorage.instance.readFile(scriptFile) ?: return null
                
-               val digest = hex(hasher.get().digest(script))
-               return withContext(Dispatchers.IO) {
+               return runInterruptible(Dispatchers.IO) {
+                       val digest = hex(hasher.get().digest(script))
+                       
                        cache.getOrPut(digest) {
-                               (scriptEngine.get() as Compilable).compile(String(script))
+                               scriptEngineSync.withLock {
+                                       (scriptEngine as Compilable).compile(String(script))
+                               }
                        }
                }
        }
@@ -80,7 +86,9 @@ object PreProcessorScriptLoader {
                        bindings["ctx"] = PreProcessorScriptVarContext { jsonToGroovy(env.context[it].toPreProcessJson()) }
                        bindings["finish"] = Consumer<Any?>(continuation::resume)
                        
-                       script.eval(bindings)
+                       scriptEngineSync.withLock {
+                               script.eval(bindings)
+                       }
                }
        }