State Variables Randomly Changing?

I've been developing my own PING/PONG for a websocket application and needed to manage the state of whether PONG has been received.

To make sure I don't accidentally change the variable and to avoid name collisions I've encapsulated the variable into get and set methods and made funny changes to make sure it'll not be accidentally changed by other parts of the code.

The main methods I use to modify and access the state is as follows.


void setReceivedPongToFalse() {
    if (enableLogging) log.debug "setReceivedPongToFalse()"
    state.elephant = "FALSE STRING"

    if (enableLogging) log.debug "receivedPong set to: ${state.elephant}"

}

void setReceivedPongToTrue() {
    if (enableLogging) log.debug "setReceivedPongToTrue()"
    state.elephant = "TRUE STRING"

    if (enableLogging) log.debug "receivedPong set to: ${state.elephant}"
    
}

boolean getReceivedPong() {
    if (enableLogging) log.debug "getReceivedPong(): ${state.elephant}"
    return state.elephant == "TRUE STRING"
}

However, in my logs, for some reason the variable was set to false after it was set to true. Could the be caused by some race conditions of event processing? Is there a thread safe way to modify state?

Thanks!

The full code without the funny modifications can be found here: hubitat-remootio/hubitat-remootio.groovy at master · sdachen/hubitat-remootio · GitHub

Might try adding

definition ( // apps
  singleThreaded: true
)
2 Likes

For my own curiosity.... Would that mean any comm's sent to the app would potentially require some kind of wait for the platform while the app deals with the call / data being processed from a previous call?

Should queue up and be processed FIFO if I’m reading it right.

2 Likes

Thanks for the feedback. I tried using both singleThreaded: true and atomicState. The glitch has improved but has not completely gone away.

Any other thoughts welcome!

In the event I can't figure this out, where can I report this potential bug to Hubitat? Thanks!

I think you're probably racing the state caching behavior since you set and query the same state members within the same methods.

Try pulling from state once within the method and operate only with local values, then update state at the end of the method (if needed).

1 Like

By method, do you mean the high level commands defined in my device or functions like

void setReceivedPongToTrue() {
    if (enableLogging) log.debug "setReceivedPongToTrue()"
    state.elephant = "TRUE STRING"

    if (enableLogging) log.debug "receivedPong set to: ${state.elephant}"
    
}

The former is true since the chain of function calls are triggered through a cron scheduled call to ping in installed().

schedule("0 * * ? * *", ping)

I only glanced at the methods you excerpted in your post.

But you set state.elephant and then immediately print its value to the log. I think you are saying that the print doesn't match the value you set it to. Correct? If so, sounds like a caching issue to me.

The value printed matches the values I set.

Here's the problem illustrated

Time 0: setReceivedPongToFalse()
Time 1: setReceivedPongToTrue()
Time 30: getReceivedPong() // returns FALSE instead of TRUE!

I'm guessing for some reason the actual changing of the state occurred in reverse order.

Time 1: setReceivedPongToTrue()
Time 0: setReceivedPongToFalse()
Time 30: getReceivedPong() // returns FALSE instead of TRUE!

You could try this instead of state and atomicState:

@Field static Map sharedState = new java.util.concurrent.ConcurrentHashMap()

You would put values into that just like any map:

sharedState["elephant"] = "TRUE STRING"

and pull them out the obvious way...

You would need this:

import groovy.transform.Field

1 Like

This reminded me of our short conversation here: Dynamic Page updates - which value was changed? - #9 by bravenel

Are you saying (in this current thread) that there may be an issue with State for OP's intended use-case?

No, I haven't delved into his code...

Thanks for the suggestion. I wonder if there is any difference between using @Field vs state? I tried using a regular HashMap and a ConcurrentHashMap and both worked fine. Yet using state doesn't work.

1 Like

@bravenel, one thing I've discovered is that while you can use state and atomicState to hold non-simple data types (like maps or lists), you cannot manipulate them directly. For example:

atomicState.myMap  = new HashMap()
atomicState.myMap.put("myKey", "myVal")
log.debug "myMap = ${atomicState.myMap}" // will be an empty map!!

tmap = atomicState.myMap
tmap.put("myKey", "myVal")
atomicState.myMap = tmap
log.debug "myMap = ${atomicState.myMap}" // will be as expected

Is this a bug, or is it supposed to be like this?

Yeah, this is how it works.

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.