Thanks, but I guess it's not the same as when Hubitat itself calls threadSafeAppend
as a callback?
Per my understanding of atomicState's documentation, atomic state will be loaded from DB before each execution, and written to DB every time it's updated.
In your example, it's a single execution, so per documentation it'll load from DB once, and then you call this one loaded atomicState
object from multiple threads.
In case where Hubitat calls the callbacks, it'll load from DB for every execution, and atomicState
object is going to be potentially different (of course, I'm only making guesses here, I'm probably wrong) for every call.
So here's my attempt (it's quite possible I'm doing something wrong here) to do the same test, but with making Hubitat to call the callback on multiple threads:
def getAppends() { 10 }
def getChars() { 'b' }
void scheduleAllAppends()
{
for (def val in 0..getAppends()) {
runInMillis(1, "appendData${val}")
}
runInMillis(2000, checkData)
}
void appendImpl(def num)
{
for (def c in 'a'..getChars()) {
threadSafeAppend("${c}${num}");
}
}
void appendData0() { appendImpl(0) }
void appendData1() { appendImpl(1) }
void appendData2() { appendImpl(2) }
void appendData3() { appendImpl(3) }
void appendData4() { appendImpl(4) }
void appendData5() { appendImpl(5) }
void appendData6() { appendImpl(6) }
void appendData7() { appendImpl(7) }
void appendData8() { appendImpl(8) }
void appendData9() { appendImpl(9) }
void appendData10(){ appendImpl(10) }
void checkData()
{
log.info("Checking atomicState.queuedData...")
def data = atomicState.queuedData
for (def val in 0..getAppends()) {
for (def c in 'a'..getChars()) {
def stringToFind = "${c}${val}"
if (!data.contains(stringToFind)) {
log.info("queuedData is incomplete - '${stringToFind}' not found in ${data}")
}
}
}
log.info("Done checking atomicState.queuedData...")
}
def threadSafeAppend(String data) {
String oldData
String updatedData
synchronized(this) {
oldData = atomicState.queuedData
atomicState.queuedData = oldData + data
updatedData = atomicState.queuedData
}
log.info("Appended '${data}'. '${oldData}' => '${updatedData}'")
updatedData
}
private def update()
{
unschedule()
atomicState.queuedData = ""
scheduleAllAppends()
}
def installed()
{
update()
}
def updated()
{
update()
}
The output I get:
23:08:18.147 info Done checking atomicState.queuedData...
23:08:18.146 info queuedData is incomplete - 'a10' not found in a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9b10
23:08:18.141 info queuedData is incomplete - 'a1' not found in a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9b10
23:08:18.139 info Checking atomicState.queuedData...
23:08:16.232 info Appended 'b9'. 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9' => 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9' ## Or maybe this one overwrites a10
23:08:16.229 info Appended 'b10'. 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9' => 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9b10' ## Overwrites a10
23:08:16.221 info Appended 'a10'. 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8' => 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a10'
23:08:16.217 info Appended 'a9'. 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8' => 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9'
23:08:16.214 info Appended 'b8'. 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8' => 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8'
23:08:16.208 info Appended 'a8'. 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7' => 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7a8'
23:08:16.152 info Appended 'b7'. 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7' => 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7b7'
23:08:16.145 info Appended 'a7'. 'a0b0b1a2b2a3b3a4b4a5b5a6b6' => 'a0b0b1a2b2a3b3a4b4a5b5a6b6a7'
23:08:16.137 info Appended 'b6'. 'a0b0b1a2b2a3b3a4b4a5b5a6' => 'a0b0b1a2b2a3b3a4b4a5b5a6b6'
23:08:16.129 info Appended 'a6'. 'a0b0b1a2b2a3b3a4b4a5b5' => 'a0b0b1a2b2a3b3a4b4a5b5a6'
23:08:16.106 info Appended 'b5'. 'a0b0b1a2b2a3b3a4b4a5' => 'a0b0b1a2b2a3b3a4b4a5b5'
23:08:16.100 info Appended 'a5'. 'a0b0b1a2b2a3b3a4b4' => 'a0b0b1a2b2a3b3a4b4a5'
23:08:16.088 info Appended 'b4'. 'a0b0b1a2b2a3b3a4' => 'a0b0b1a2b2a3b3a4b4'
23:08:16.083 info Appended 'a4'. 'a0b0b1a2b2a3b3' => 'a0b0b1a2b2a3b3a4'
23:08:16.078 info Appended 'b3'. 'a0b0b1a2b2a3' => 'a0b0b1a2b2a3b3'
23:08:16.073 info Appended 'a3'. 'a0b0b1a2b2' => 'a0b0b1a2b2a3'
23:08:16.045 info Appended 'b2'. 'a0b0b1a2' => 'a0b0b1a2b2'
23:08:16.038 info Appended 'a2'. 'a0b0b1' => 'a0b0b1a2'
23:08:15.990 info Appended 'b1'. 'a0b0' => 'a0b0b1'
23:08:15.988 info Appended 'b0'. 'a0' => 'a0b0' ## It sees only 'a0'
23:08:15.985 info Appended 'a1'. 'a0' => 'a0a1'
23:08:15.982 info Appended 'a0'. '' => 'a0'
So I still have a question if there's a good way to accumulate data in a thread-safe way, but I guess not.