[Solved] Concurrent Timers

Hi,

I am not sure about the restrictions of createTimer and thread safety in openhab.

I am implementing an alarm. As soon as the alarm gets triggered I want some lights to flash and some smoke alarm to ring after a while until either the alarm is turned of or the real alarm goes off after a defined time frame (ALARM_ACTIVE2ALERT).

The first two timers get called but the last one never does. What do I miss?

	// light blinking after a while
	createTimer(now.plusSeconds(ALARM_ACTIVE2LIGHT)) [|
			while(Alarm_Scharf.state == ON && Alarm_An.state == ON && Alarm_Ringing.state == OFF) {
    				blink.apply( Alarm_Licht_Warn , BLINK_MIDDLE , BLINK_BREAK )
			}
		]

	// acoustic info that the alarm is about to go off
	createTimer(now.plusSeconds(ALARM_ACTIVE2SOUND)) [|
			while(Alarm_Scharf.state == ON && Alarm_An.state == ON && Alarm_Ringing.state == OFF) {
				try {
					RM_OG_Buero_Test.sendCommand(ON)
					Thread::sleep(RM_WARN_SOUND * 1000)  
					RM_OG_Buero_Test.sendCommand(OFF)
	   				Thread::sleep(RM_WARN_BREAK * 1000)
	   			} catch (java.lang.InterruptedException ex) {
	   				// NOP	
	   			}
   			}
	]

	// delayed real alarm after certain time frame
	logInfo("Alarm", "Pre-Hot 1")
	createTimer(now.plusSeconds(ALARM_ACTIVE2ALERT)) [|
		logInfo("Alarm", "**Pre-Hot 2**")
		if (Alarm_Scharf.state == ON && Alarm_An.state == ON) {
			logInfo("Alarm", "Pre-Hot 3")
			Alarm_Ringing.sendCommand(ON)
		}
	]

It reaches Pre-Hot 1 but never Pre-Hot 2 or 3.

Thank you
Karl

What’s the definition of ALARM_ACTIVE2ALERT (and ALARM_ACTIVE2SOUND and ALARM_ACTIVE2LIGHT)?

Hi Udo,

val int ALARM_ACTIVE2LIGHT = 5 // value in seconds
val int ALARM_ACTIVE2SOUND = 15 // value in seconds
val int ALARM_ACTIVE2ALERT = 60 // value in seconds

I recommend that you save your timers in a global var in your rules file. Because you don’t retain a reference to the timers after you create them it is possible that the garbage collector things the Timer has been abandoned and reclaims the object.

It is also good practice to keep a handle on your timers so you can cancel them later if the state changes to warrant it.

Interesting idea. I totally forgot about garbage collection.

I now save the 3 timers in global variables t1,t2,t3 inside the rule script:

var Timer t1
var Timer t2
var Timer t3

t1 = createTimer(…
t2 = createTimer(…
t3 = createTimer(…

Still only the first two timers are started. But as soon as I turn off the alarm manually I get the following stacktrace and at the end I can see Pre-Hot 2 is called.

2016-01-14 10:03:43.571 [ERROR] [org.quartz.core.JobRunShell   ] - Job DEFAULT.2016-01-14T10:03:27.231+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: org.eclipse.xtext.xbase.impl.XClosureImplCustom@1980028 (explicitSyntax: true) threw an unhandled Exception:
java.lang.RuntimeException: The name '<unkown>' cannot be resolved to an item or type.
        at org.openhab.model.script.interpreter.ScriptInterpreter.internalFeatureCallDispatch(ScriptInterpreter.java:67) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateAbstractFeatureCall(XbaseInterpreter.java:658) ~[na:na]
        at sun.reflect.GeneratedMethodAccessor41.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
        at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateMemberFeatureCall(XbaseInterpreter.java:545) ~[na:na]
        at sun.reflect.GeneratedMethodAccessor39.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
        at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateAbstractFeatureCall(XbaseInterpreter.java:657) ~[na:na]
        at sun.reflect.GeneratedMethodAccessor41.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
        at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateIfExpression(XbaseInterpreter.java:327) ~[na:na]
        at sun.reflect.GeneratedMethodAccessor49.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
        at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateBlockExpression(XbaseInterpreter.java:321) ~[na:na]
        at sun.reflect.GeneratedMethodAccessor40.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
        at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:204) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:28) ~[na:na]
        at com.sun.proxy.$Proxy85.apply(Unknown Source) ~[na:na]
        at org.openhab.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:44) ~[na:na]
        at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-all-2.1.7.jar:na]
        at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:557) [quartz-all-2.1.7.jar:na]
2016-01-14 10:03:43.592 [ERROR] [org.quartz.core.ErrorLogger   ] - Job (DEFAULT.2016-01-14T10:03:27.231+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: org.eclipse.xtext.xbase.impl.XClosureImplCustom@1980028 (explicitSyntax: true) threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
        at org.quartz.core.JobRunShell.run(JobRunShell.java:224) ~[quartz-all-2.1.7.jar:na]
        at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:557) [quartz-all-2.1.7.jar:na]
Caused by: java.lang.RuntimeException: The name '<unkown>' cannot be resolved to an item or type.
        at org.openhab.model.script.interpreter.ScriptInterpreter.internalFeatureCallDispatch(ScriptInterpreter.java:67) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateAbstractFeatureCall(XbaseInterpreter.java:658) ~[na:na]
        at sun.reflect.GeneratedMethodAccessor41.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
        at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateMemberFeatureCall(XbaseInterpreter.java:545) ~[na:na]
        at sun.reflect.GeneratedMethodAccessor39.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
        at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateAbstractFeatureCall(XbaseInterpreter.java:657) ~[na:na]
        at sun.reflect.GeneratedMethodAccessor41.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
        at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateIfExpression(XbaseInterpreter.java:327) ~[na:na]
        at sun.reflect.GeneratedMethodAccessor49.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
        at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateBlockExpression(XbaseInterpreter.java:321) ~[na:na]
        at sun.reflect.GeneratedMethodAccessor40.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
        at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:204) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[na:na]
        at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:28) ~[na:na]
        at com.sun.proxy.$Proxy85.apply(Unknown Source) ~[na:na]
        at org.openhab.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:44) ~[na:na]
        at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-all-2.1.7.jar:na]
        ... 1 common frames omitted
2016-01-14 10:03:44.406 [INFO ] [org.openhab.model.script.Alarm] -** Pre-Hot 2**

So maybe the concurrent access to the Switch Alarm_Ringing is a problem?

I’d suggest de-complexifying the createTimer [ …] expressions. Set up straightforward timer that flips a logical (aka PROXY) switch when it expires. Monitor the change in the PROXY in rule(s). When CHANGED from OFF to ON. (or ON to OFF ) , do the logical "if xx.state == " etc in the THEN clause of the rule and take appropriate actions from there.

2 Likes

Great, this works!!!

Thanks a lot to everybody. :+1:

PS: Off-topic: How to I mark a thread as solved?

Edit the subject

1 Like