I had some state inconsistencies (too complicated to explain, and doesn’t really matter), so I decided to try and switch to atomicState instead. I simply replaced “state” with “atomicState” through my application (Not device), as suggested in the documentation.
To illustrate the problem, try this code assuming you have some switches:
atomicState.switches = [:]
switches.each {
// Set ready state for each switch
atomicState.switches["$it.id"] = "ready"
}
atomicState.switches.each {
log.debug "$it.key = $it.value"
}
RBoy
(www.rboyapps.com - Making SmartThings Easy!)
3
hmm same issue here, am trying to use atomicState to track something which can change very rapidly due to multiple events being fired. I can’t seem to use an array/list with atomicState.
@Jim any insight into restrictions on atomicState (this is with a SmartApp)
RBoy
(www.rboyapps.com - Making SmartThings Easy!)
4
EDIT: Sorry, it doesn’t work even with integers and strings. This is a simple array like:
initialize:
atomicState.locks =
add:
atomicState.locks.add(lock.id) // lock.id is an integer or when using lock object itself it through an exception
I believe that the atomicState is only persisted when something on the root object changes. Since this has to happen immediately, and it could be expensive to watch every single child property/object. So for instance in your situation you need to reassign the property on atomicState to the value you changed it to.
This is how I got it to work in a SmartApp I was working on that needed to aggregate events into 2 second chunks - I would store the events in atomicState until a schedule runs to process them. using atomicState was necessary because sometimes things wouldn’t persist properly because they were being overwritten by another execution running concurrently.
This is why the docs use:
atomicState.counter = atomicState.counter + 1
As it is retrieving the original value, modifying it and reassigning it back to atomicState, but this isn’t obvious and should probably be made clear in the docs.
This is necessary on state because at the end of execution the entire object is persisted regardless, it doesn’t need a trigger like atomicState.
4 Likes
RBoy
(www.rboyapps.com - Making SmartThings Easy!)
7
Genius @Kriskit - thanks that worked perfectly. @Jim I also recommend that the docs be updated to clarify the same. (BTW there is no documentation for atomicState under API Documentation -> Smart App (state is there but there is no atomicState)
However I do have a question, the whole point of atomicState is to have it happen in a single instruction to avoid race conditions. By assigning to an object, modifying and reassigning back to atomicState doesn’t that defeat the purpose?
1 Like
RBoy
(www.rboyapps.com - Making SmartThings Easy!)
8
Did something change with atomicState? Suddenly it no longer works. The code was working till a few hours ago and now suddenly I’m getting a null returned from atomicState.
RBoy
(www.rboyapps.com - Making SmartThings Easy!)
9
So it started when I decided to use 3 atomicState variables. All of a sudden it just stopped! I would only get null’s back from all 3 atomicState variables (EXACT same copy paste code of the working code posted above by @Kriskit which I verified was working), I just changed the names.
Then I went back and reverted to the old code with just 1 atomicState variable (which was working). Now even that returns a null!!
I can’t explain it, working code no longer works after introducing 3 atomicState variables. Anyone any clue? This is very funky!
RBoy
(www.rboyapps.com - Making SmartThings Easy!)
10
This is what I’m using and it’s not working:
In installed()/udpated():
12:37:23 AM: error java.lang.NullPointerException: Cannot invoke method contains() on null object @ line 463
If I remove the check for the contains and just do what I did yesterday, it errors out the next line:
12:44:26 AM: error java.lang.NullPointerException: Cannot invoke method add() on null object @ line 465
What could I be doing wrong? (this was working yesterday)
RBoy
(www.rboyapps.com - Making SmartThings Easy!)
11
So all I could figure out is that atomicState does not save the state of an empty object (e.g. empty array), it discards it. Unlike state where it does save the state of an empty object, hence yesterday in my app I started with a state object and then moved to a atomicState object which is why it probably worked, today starting afresh I didnt’ because when you allocate an empty list to the atomicState ibject, it discards it so it keeps returning null.
The only workaround for this problem is to reallocate a new list if the atomicState object is null (empty).
Hello Everyone,
Just throwing my 2cents in. I noticed that the atomicState object does not handle objectRefs very well. Maps in particular. Just like the thread creator I was trying to use a map and noticed my changes are not sticking so i did the work around where I temporarily cache it out and throw it back in when done.
But in hindsight, that means that you are no longer getting an atomic operation by default if you do something like this.
get map
alter map
put map
If another trigger comes in at that time it will get a version of a map that is not update and then you will have some issues. Something that is not fast but slightly better would be
while(atomicState.lockFlag)
wait
atomicState.lockFlag = true
get Map
alter Map
put Map
atomicState.lockFlag = false
Now that is not an ideal solution, but that’s one solution.
Another solution would be to flatten your object straight into top level atomicState so that it sees changes.
Without some sort of atomic check and set operation it seems like the atomicState property is useless. Even if you implement a mutex like so:
while(atomicState.lockFlag)
wait
atomicState.lockFlag = true
get Map
alter Map
put Map
atomicState.lockFlag = false
you could still get two threads who both read the lockFlag as true and then enter the critical section. Without atomic operations atomic state is useless and doesn’t solve the concurrency problem. We need something like java.util.concurrent.atomic.AtomicBoolean's compareAndSet method to make it work.
We just need someway to manage concurrency for these apps that need to batch events to send to external systems, otherwise you can’t manage the buffer. We need either atomic operations on AtomicState booleans so we can implement our own locks, or native support for locks, or critical sections (synchronized blocks). Really anything to manage concurrency would work.
3 Likes
RBoy
(www.rboyapps.com - Making SmartThings Easy!)
14
@Jim is atomicState broken or am I doing something wrong here? This just won’t work
Just refuses to execute it. If I comment out the line it goes one but if I don’t it just hangs there. I’m using a Service Manager SmartApp if that makes any difference.
Is this broken or am I doing something wrong?
RBoy
(www.rboyapps.com - Making SmartThings Easy!)
15
This is straight from the documentation. Broken or misused?
EDIT: The same command with state instead of atomicState works perfectly!
tgauchat
(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy)
16
SmartThings might be able to get a detailed stack trace from their servers. You may need someone live to get it right while it’s hanging or force it to throw an exception or something.
atomicState has to write immediately to permanent storage … my guess is that there’s a bug serializing the data. Replace the line with some sort of really simple data structure and see if it works, and then work your way up?
The other possibility is that you’re running into deadlock somehow? atomicState must have a mutex record lock around it, right? … to prevent the same data block from being overwritten concurrently by another processs … of course, I presume you’re testing with only one concurrent process; just grasping at possibilities here. There’s certainly enough complexity under the covers of atomicState that it’s prone for a bug to creep in that’s hard to test for.
1 Like
RBoy
(www.rboyapps.com - Making SmartThings Easy!)
17
you’re onto it, it just could be because I’m using the parent child relationship it’s causing a deadlock.
Well the good news is that I’ve given the code to reproduce it consistently I hope @jody.albritton@slagle or someone can check it out and let us know what’s going on.