Closed sbdevelops closed 2 years ago
Yes, sprinkling synchronized
here and there hardly wards off data races. The blast radius would be pretty small if configRoot
were not blatantly leaked outside FtcDashboard
. I'd love to make config trees immutable, but that's a significant change. I think the minimally invasive patch I can stomach is deprecating getConfigRoot()
and adding a withConfigRoot()
method that executes a lambda with the config root under the mutex (and trusts the user to keep the object inside the closure). And then synchronizing inside updateConfig()
of course... slightly embarrassed I forgot that.
Thanks for the quick response. What's the timeline on a fix being pushed to the maven repo?
I (hopefully) pushed a fix. I'd like to get #72 in the next release as well. In the meantime, you can clone, run ./gradlew publishToMavenLocal
in the dashboard root dir, add mavenLocal()
to the RC project repositories, and bump the dash version from 0.4.3
to 0.4.4-SNAPSHOT
.
A ConcurrentModificationException can occur when adding config variables to FtcDashboard during OpMode instantiation / initialization. Our team has been able to repeatedly reproduce this issue, which causes the FtcRobotController app to crash.
Details:
The issue appears to be due to
FtcDashboard.configRoot
variable not being used in a thread-safe manner. When an OpMode (including the default OpMode) is ended,FtcDashboard.onOpModePostStop()
is called as it is a registered listener. The following code block shows a shortened version ofonOpModePostStop()
.Once the new thread is instantiated and begins to run,
onOpModePostStop()
returns and the event loop thread continues to its next step, instantiating and initializing the new OpMode. Our OpMode that causes the crash then makes a call toFtcDashboard.addConfigVariable(...)
, which writes to the HashMap contained in configRoot - the same HashMap that theupdateConfig()
method is currently iterating over in the separate thread! The modification to a HashMap that another thread is iterating over causes the ConcurrentModificationException and subsequent crash.This is the OpMode in which we experience the aforementioned behavior: TeleOpCore.kt - EagleRobotics7373:FtcRobotController7373:t/dashboard-debug The class referenced in our OpMode that actually calls
addConfigVariable()
: DashboardVar.kt - EagleRobotics7373:FtcRobotController7373:t/dashboard-debugSuggestions:
I would imagine some options for potential fixes being:
onOpModePostStop()
, and just letupdateConfig()
briefly block the event loop thread. TheupdateConfig()
method is called in a blocking manner withinaddConfigVariable()
, so it's not clear why a separate thread is needed anyway inonOpModePostStop()
.updateConfig()
call (specifically the one shown above) inside asynchronized(configLock) { ... }
structure, and do similar forconfigRoot.putVariable(...)
callsStack trace: