OH3 using reentrant lock with timer

I’m trying to tie down an illusive HTTP 415 error in the log which may be down to a rule triggering more than once. in OH2.5 I stopped this with a reentrant lock, but this may not be working in OH3. As a test I’ve created this rule which I think should only start every 2 minutes after the previous timer has finished? In reality the reentrant part doesn’t seem to block the rule whilst the timer is active and the rule triggers every minute. What am I doing wrong?

import java.util.concurrent.locks.ReentrantLock
val ReentrantLock ExperimentalLock = new ReentrantLock()
rule "Lock Test"
when Item CurrentMinute changed then if (!ExperimentalLock.isLocked()) {
ExperimentalLock.lock()
    try {
        logInfo("Experimental", "Start of Timer ")
        createTimer(now.plusSeconds(115))[|
            logInfo("Experimental", "End of Timer ")
            ]
    } catch(Throwable t){
            logInfo("Experimental", "Something bad happened")
            ExperimentalLock.unlock()
    } finally {
            ExperimentalLock.unlock()
            }
}   
end

I just tried changing the rule to mirror the information at Rules | openHAB with the same triggers every minute result

Lots of stuff going on here.

Firstly, I don’t think you need any kind of lock at all. Rule scheduling/queuing is different in OH3 - any given rule can execute only one “copy” at a time. Subsequent re-triggers should I believe be queued. (defer to @rlkoshak on this detail)

It would be nice to see what makes you think that, i.e.logs.
But let’s think it through.

You’ve got a global variable ExperimentalLock and this still work as in OH2 if you are working with xxxx.rules files, i e. persist between rule runs.

The rule WILL trigger every minute, but if/then code may control what gets done inside.
Unless the rule hangs up for over a minute, we should not be dealing with a trigger queue.

Alright, with the body the first thing is to if-test the global isLocked. Looks good, unlike other techniques this is go/nogo and will not queue.
It’d be useful if there was an ‘else’ section that logged if failed the test, otherwise you can’t see, the rule exits silently.

Okay, so we next grab the lock and set up the timer to make a log message in the future.
Then we immediately release the lock again.on the way out of the try-catch block. At the next minute, we’ll run the rule and find the lock unset again.

I don’t think you meant to do that !

But as said, locking a single rule against multithreading is not needed in OH3.

That’s correct, they will be queued in a First In First Out manner so the events will be processed in order, but only one at a time.

As rossko57 correctly identified, you would need to unlock the lock in the timer. As written the lock is unlocked before the rule exists, every time. And because only one instance of a rule can run at a time, you will never have a case where the the lock is locked when the rule runs (unless you have a type error inside the locked code which is not catchable and therefore you are in trouble which is one reasons why using reentrant locks are dangerous to use). In short, the lock isn’t actually doing anything.

But also as explained by rossko57, you really don’t want to be using reentrantlocks in OH 3 anyway so there should be a better way. And indeed there is.

  1. Use a timestamp and change the if statement to check to see if enough time has passed since the last run of the rule instead of trying to use a lock and timer.

  2. Use the existence of the Timer as the lock instead of creating an actual lock.

1 Like

Thanks for the help, it sounds like I need to have a serious rethink. The original rule which this was taken from was a workaround for problems with the Nuki Binding in OH2.x
Whilst looking into this I’ve discovered everytime the binding sends an update it throws

javax.ws.rs.ClientErrorException: HTTP 415 Unsupported Media Type

Into the log so I’ll remove the binding and rewrite the rule to do its functions. Once I have it working I’ll file an issue