-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switching from ArduinoJson 6 to 7 leads to a random assertion failure at runtime: const VariantPoolList.hpp:98 (poolIndex < count_
#2034
Comments
Hi @mathieucarbou, This looks like a corrupted The stack trace shows a Best regards, |
Hello, Yes that's what I am thinking: I discovered yesterday that the library I use is not thread safe and is calling this |
@bblanchon : I have to re-open it... I have removed any potential concurrent access: now, the generateLayout method is only called once from the async_http task (websocket handler) and nowhere else.. Also, the json document reads data that is initialised once, stays as references in private fields in a static class, and this data is not modified concrurently. While the json document is building, I have this error again, and constantly:
It also also fails at the place:
This line matches JsonObject doc = root.to<JsonObject>();
doc["command"] = changes_only ? "update:components" : "update:layout:next";
for (int i = 0; i < cards.Size(); i++) { So it is like a document becomes unusable after being cleared. If I do not use We can see in the logs above that the first call to send() works, then the document is cleared for re-use and it crashes. Here is part of the code: // generates the layout JSON string to the frontend
void ESPDash::generateLayoutJSON(AsyncWebSocketClient* client, bool changes_only, Card* onlyCard) {
#if ARDUINOJSON_VERSION_MAJOR == 6
DynamicJsonDocument root(DASH_JSON_SIZE);
#else
JsonDocument root;
#endif
// preparing layout
{
JsonObject doc = root.to<JsonObject>();
if (!changes_only) {
doc["command"] = "update:layout:begin";
// OTHER DOC SETTERS
} else {
doc["command"] = "update:components";
}
// Generate Tab JSON
for (int i = 0; i < tabs.Size(); i++) {
// [...]
doc["tabs"][i]["id"] = t->_id;
// [...]
// OTHER DOC SETTERS
}
if (!changes_only || doc["tabs"].as<JsonArray>().size() > 0) {
send(client, doc);
}
}
// Generate JSON for all Cards
{
JsonObject doc = root.to<JsonObject>();
doc["command"] = changes_only ? "update:components" : "update:layout:next";
for (int i = 0; i < cards.Size(); i++) {
// [...]
// Generate JSON
#if ARDUINOJSON_VERSION_MAJOR == 6
JsonObject obj = doc["cards"].createNestedObject();
#else
JsonObject obj = doc["cards"].add<JsonObject>();
#endif
generateComponentJSON(obj, c, changes_only);
if (overflowed(root)) {
doc["cards"].as<JsonArray>().remove(doc["cards"].as<JsonArray>().size() - 1);
send(client, doc);
doc = root.to<JsonObject>();
doc["command"] = changes_only ? "update:components" : "update:layout:next";
i--;
continue;
}
// [...]
}
if (doc["cards"].as<JsonArray>().size() > 0)
send(client, doc);
}
// Generate JSON for all Charts
{
JsonObject doc = root.to<JsonObject>();
doc["command"] = changes_only ? "update:components" : "update:layout:next";
for (int i = 0; i < charts.Size(); i++) {
// [...]
// Generate JSON
#if ARDUINOJSON_VERSION_MAJOR == 7
JsonObject obj = doc["charts"].add<JsonObject>();
#else
JsonObject obj = doc["charts"].createNestedObject();
#endif
generateComponentJSON(obj, c, changes_only);
if (overflowed(root)) {
doc["charts"].as<JsonArray>().remove(doc["charts"].as<JsonArray>().size() - 1);
send(client, doc);
doc = root.to<JsonObject>();
doc["command"] = changes_only ? "update:components" : "update:layout:next";
i--;
continue;
}
// [...]
}
if (doc["charts"].as<JsonArray>().size() > 0)
send(client, doc);
}
// Generate JSON for all Statistics
{
JsonObject doc = root.to<JsonObject>();
doc["command"] = changes_only ? "update:components" : "update:layout:next";
int idx = 0;
// Check if default statistics are needed
if (default_stats_enabled) {
if (!changes_only) {
// OTHER DOC SETTERS
}
// Loop through user defined stats
for (int i = 0; i < statistics.Size(); i++, idx++) {
// [...]
// OTHER DOC SETTERS
if (overflowed(root)) {
doc["stats"].as<JsonArray>().remove(idx);
send(client, doc);
doc = root.to<JsonObject>();
doc["command"] = changes_only ? "update:components" : "update:layout:next";
i--;
idx = 0;
continue;
}
// Clear change flags
if (changes_only) {
s->_changed = false;
}
}
if (doc["stats"].as<JsonArray>().size() > 0)
send(client, doc);
}
}
void ESPDash::send(AsyncWebSocketClient* client, JsonObject& doc) {
const size_t len = measureJson(doc);
auto buffer = std::make_shared<std::vector<uint8_t>>(len);
if (!buffer) {
doc.clear();
return;
}
serializeJson(doc, buffer->data(), len);
#ifdef DASH_DEBUG
Serial.printf("T %12u %-10.10s (%u) ESP-DASH client=%u, measureJson=%u\r\n", static_cast<uint32_t>(esp_timer_get_time() >> 10), pcTaskGetName(NULL), xPortGetCoreID(), (client == nullptr ? -1 : client->id()), len);
#endif
if (client != nullptr) {
client->text(buffer);
} else {
_ws->textAll(buffer);
}
doc.clear();
}
bool ESPDash::overflowed(JsonDocument& json) {
return json.overflowed() || measureJson(json.as<JsonObject>()) > DASH_JSON_SIZE;
} |
Ok so there is definitely something wrong with reusing a Json doc after a clear(). I've implemented a new version which is creating a new JsonDocument each time, and it works. |
Could you provide an MCVE? |
We have a big internet outage here but as soon as I can, yes. |
Here is a simple #include <ArduinoJson.h>
void fill(JsonObject &obj) {
obj["foo-bar-baz"] = "foo-bar-baz-foo-bar-baz";
}
void send(JsonObject &obj) {
serializeJson(obj, Serial);
Serial.println("");
obj.clear();
}
void setup() {
Serial.begin(115200);
JsonDocument doc;
{
JsonObject obj = doc.to<JsonObject>();
obj["command"] = "update:components";
obj["tabs"][0]["id"] = "0";
send(obj);
}
{
JsonObject obj = doc.to<JsonObject>();
obj["command"] = "update:components";
JsonObject card = doc["cards"].add<JsonObject>();
fill(card);
send(obj);
}
}
void loop() { } Please note that if I comment out the void send(JsonObject &obj) {
serializeJson(obj, Serial);
Serial.println("");
//obj.clear();
} logs:
What is the recommandation for such usage: use only one My assumption was that destroying and creating a new JsonDocument each time would be slower, so that is why I've tried to reuse it with |
Thank you very much for this code. ❤️
I recommend short lifetimes to release memory as soon as possible and reduce the cognitive load.
Calling |
Thanks for the advices, and glad I was able to help :-) |
The fix was published in ArduinoJson 7.0.2. |
I cannot use the the Arduino Json Troubleshooter since the issue happens when building a json document, when allocating memory.
I do not use any custom allocator.
Board is the standard ESP32 NodePCU Dev Kit from AZ-Delivery.
I have no idea what to do or how to troubleshoot this...
How I am using the JsonDocument:
Stack trace:
The text was updated successfully, but these errors were encountered: