From d012d36bba9b8a86e5a5725e6576d4178086f579 Mon Sep 17 00:00:00 2001 From: Florian Hotze Date: Sun, 6 Oct 2024 19:20:30 +0200 Subject: [PATCH] [automation] Add support for synchronized execution of compiled scripts (#4402) * [automation] Synchronize execution of compiled scripts if ScriptEngine implements Lock interface Signed-off-by: Florian Hotze --- .../handler/AbstractScriptModuleHandler.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/handler/AbstractScriptModuleHandler.java b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/handler/AbstractScriptModuleHandler.java index 6a695d6e7ba..beb2724d60a 100644 --- a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/handler/AbstractScriptModuleHandler.java +++ b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/handler/AbstractScriptModuleHandler.java @@ -17,6 +17,8 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; import javax.script.Compilable; import javax.script.CompiledScript; @@ -210,15 +212,29 @@ protected void resetExecutionContext(ScriptEngine engine, Map context protected @Nullable Object eval(ScriptEngine engine, String script) { try { if (compiledScript.isPresent()) { + if (engine instanceof Lock lock && !lock.tryLock(1, TimeUnit.MINUTES)) { + logger.error("Failed to acquire lock within one minute for script module of rule with UID '{}'", + ruleUID); + return null; + } logger.debug("Executing pre-compiled script of rule with UID '{}'", ruleUID); - return compiledScript.get().eval(engine.getContext()); + try { + return compiledScript.get().eval(engine.getContext()); + } finally { // Make sure that Lock is unlocked regardless of an exception being thrown or not to avoid + // deadlocks + if (engine instanceof Lock lock) { + lock.unlock(); + } + } } logger.debug("Executing script of rule with UID '{}'", ruleUID); return engine.eval(script); } catch (ScriptException e) { logger.error("Script execution of rule with UID '{}' failed: {}", ruleUID, e.getMessage(), logger.isDebugEnabled() ? e : null); - return null; + } catch (InterruptedException e) { + throw new RuntimeException(e); } + return null; } }