From 85ded6e5008efafffcb8e29935e84887920d51a4 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Fri, 12 Nov 2021 23:33:10 +0100 Subject: [PATCH] Advanced locking with time-out. Bugfixes. --- wled00/FX_fcn.cpp | 11 ++++++----- wled00/cfg.cpp | 44 +++++++++++++++++++++++------------------- wled00/fcn_declare.h | 2 ++ wled00/ir.cpp | 18 +++++++++-------- wled00/json.cpp | 9 +++++---- wled00/mqtt.cpp | 18 ++++++++--------- wled00/presets.cpp | 24 +++++++++++++---------- wled00/set.cpp | 9 +++++---- wled00/util.cpp | 24 +++++++++++++++++++++++ wled00/wled.h | 2 +- wled00/wled_eeprom.cpp | 9 +++++---- wled00/wled_serial.cpp | 16 ++++++++------- wled00/wled_server.cpp | 14 ++++++++------ wled00/ws.cpp | 28 +++++++++++++++------------ wled00/xml.cpp | 12 +++++++----- 15 files changed, 145 insertions(+), 95 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 175b769b..d4a1345b 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1092,16 +1092,17 @@ void WS2812FX::deserializeMap(uint8_t n) { #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif DEBUG_PRINT(F("Reading LED map from ")); DEBUG_PRINTLN(fileName); if (!readObjectFromFile(fileName, nullptr, &doc)) { - jsonBufferLock = false; + releaseJSONBufferLock(); return; //if file does not exist just exit } @@ -1121,7 +1122,7 @@ void WS2812FX::deserializeMap(uint8_t n) { } } - jsonBufferLock = false; + releaseJSONBufferLock(); } //gamma 2.8 lookup table used for color correction diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index b831407b..5fda9207 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -410,12 +410,12 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { DEBUG_PRINTLN(F("Starting usermod config.")); JsonObject usermods_settings = doc["um"]; if (!usermods_settings.isNull()) { - needsSave = usermods.readFromConfig(usermods_settings); + needsSave = !usermods.readFromConfig(usermods_settings); } if (fromFS) return needsSave; doReboot = doc[F("rb")] | doReboot; - return (doc["sv"] | needsSave); + return (doc["sv"] | true); } void deserializeConfigFromFS() { @@ -428,9 +428,10 @@ void deserializeConfigFromFS() { #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif DEBUG_PRINTLN(F("Reading settings from /cfg.json...")); @@ -438,14 +439,14 @@ void deserializeConfigFromFS() { success = readObjectFromFile("/cfg.json", nullptr, &doc); if (!success) { //if file does not exist, try reading from EEPROM deEEPSettings(); - jsonBufferLock = false; + releaseJSONBufferLock(); return; } // NOTE: This routine deserializes *and* applies the configuration // Therefore, must also initialize ethernet from this function bool needsSave = deserializeConfig(doc.as(), true); - jsonBufferLock = false; + releaseJSONBufferLock(); if (needsSave) serializeConfig(); // usermods required new prameters } @@ -458,9 +459,10 @@ void serializeConfig() { #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif JsonArray rev = doc.createNestedArray("rev"); @@ -768,7 +770,7 @@ void serializeConfig() { File f = WLED_FS.open("/cfg.json", "w"); if (f) serializeJson(doc, f); f.close(); - jsonBufferLock = false; + releaseJSONBufferLock(); } //settings in /wsec.json, not accessible via webserver, for passwords and tokens @@ -778,14 +780,15 @@ bool deserializeConfigSec() { #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return false; + } #endif bool success = readObjectFromFile("/wsec.json", nullptr, &doc); if (!success) { - jsonBufferLock = false; + releaseJSONBufferLock(); return false; } @@ -819,7 +822,7 @@ bool deserializeConfigSec() { CJSON(wifiLock, ota[F("lock-wifi")]); CJSON(aOtaEnabled, ota[F("aota")]); - jsonBufferLock = false; + releaseJSONBufferLock(); return true; } @@ -829,9 +832,10 @@ void serializeConfigSec() { #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif JsonObject nw = doc.createNestedObject("nw"); @@ -867,5 +871,5 @@ void serializeConfigSec() { File f = WLED_FS.open("/wsec.json", "w"); if (f) serializeJson(doc, f); f.close(); - jsonBufferLock = false; + releaseJSONBufferLock(); } diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index b995f31e..3edccb5f 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -272,6 +272,8 @@ void sappends(char stype, const char* key, char* val); void prepareHostname(char* hostname); void _setRandomColor(bool _sec, bool fromButton); bool isAsterisksOnly(const char* str, byte maxLen); +bool requestJSONBufferLock(); +void releaseJSONBufferLock(); //wled_eeprom.cpp void applyMacro(byte index); diff --git a/wled00/ir.cpp b/wled00/ir.cpp index f5de7710..0cdca96a 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -575,12 +575,14 @@ void decodeIRJson(uint32_t code) JsonObject fdo; JsonObject jsonCmdObj; + DEBUG_PRINTLN(F("IR JSON buffer requested.")); #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif sprintf_P(objKey, PSTR("\"0x%lX\":"), (unsigned long)code); @@ -601,6 +603,9 @@ void decodeIRJson(uint32_t code) cmdStr = fdo["cmd"].as();; jsonCmdObj = fdo["cmd"]; //object + // command is JSON object + //allow applyPreset() to reuse JSON buffer, or it would alloc. a second buffer and run out of mem. + //fileDoc = &doc; // used for applying presets (presets.cpp) if (!cmdStr.isEmpty()) { if (cmdStr.startsWith("!")) { @@ -635,13 +640,10 @@ void decodeIRJson(uint32_t code) } colorUpdated(CALL_MODE_BUTTON); } else if (!jsonCmdObj.isNull()) { - // command is JSON object - //allow applyPreset() to reuse JSON buffer, or it would alloc. a second buffer and run out of mem. - fileDoc = &doc; deserializeState(jsonCmdObj, CALL_MODE_BUTTON); - fileDoc = nullptr; } - jsonBufferLock = false; + //fileDoc = nullptr; + releaseJSONBufferLock(); } void initIR() diff --git a/wled00/json.cpp b/wled00/json.cpp index c7defbe5..f130d409 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -920,9 +920,10 @@ void serveJson(AsyncWebServerRequest* request) #ifdef WLED_USE_DYNAMIC_JSON AsyncJsonResponse* response = new AsyncJsonResponse(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } AsyncJsonResponse *response = new AsyncJsonResponse(&doc); #endif @@ -956,7 +957,7 @@ void serveJson(AsyncWebServerRequest* request) response->setLength(); request->send(response); - jsonBufferLock = false; + releaseJSONBufferLock(); } #define MAX_LIVE_LEDS 180 diff --git a/wled00/mqtt.cpp b/wled00/mqtt.cpp index 78614f1c..3b608df0 100644 --- a/wled00/mqtt.cpp +++ b/wled00/mqtt.cpp @@ -90,26 +90,26 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties colorFromDecOrHexString(col, (char*)payloadStr); colorUpdated(CALL_MODE_DIRECT_CHANGE); } else if (strcmp_P(topic, PSTR("/api")) == 0) { - if (payload[0] == '{') { //JSON API + DEBUG_PRINTLN(F("MQTT JSON buffer requested.")); #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif - + if (payload[0] == '{') { //JSON API deserializeJson(doc, payloadStr); - fileDoc = &doc; + //fileDoc = &doc; // used for applying presets (presets.cpp) deserializeState(doc.as()); - fileDoc = nullptr; - - jsonBufferLock = false; + //fileDoc = nullptr; } else { //HTTP API String apireq = "win&"; apireq += (char*)payloadStr; handleSet(nullptr, apireq); } + releaseJSONBufferLock(); } else if (strlen(topic) != 0) { // non standard topic, check with usermods usermods.onMqttMessage(topic, payloadStr); diff --git a/wled00/presets.cpp b/wled00/presets.cpp index 6ddccbf3..580dc44a 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -4,6 +4,8 @@ * Methods to handle saving and loading presets to/from the filesystem */ +// called from: handleSet(), deserializeState(), applyMacro(), handlePlaylist(), checkCountdown(), checkTimers(), handleNightlight(), presetFallback() +// shortPressAction(), longPressAction(), doublePressAction(), handleSwitch(), onAlexaChange() bool applyPreset(byte index, byte callMode) { if (index == 0) return false; @@ -19,13 +21,14 @@ bool applyPreset(byte index, byte callMode) #endif deserializeState(fdo, callMode, index); } else { - DEBUGFS_PRINTLN(F("Make read buf")); + DEBUG_PRINTLN(F("Apply preset JSON buffer requested.")); #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return false; + } #endif errorFlag = readObjectFromFileUsingId(filename, index, &doc) ? ERR_NONE : ERR_FS_PLOAD; @@ -35,7 +38,7 @@ bool applyPreset(byte index, byte callMode) serializeJson(doc, Serial); #endif deserializeState(fdo, callMode, index); - jsonBufferLock = false; + releaseJSONBufferLock(); } if (!errorFlag) { @@ -53,13 +56,14 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj) const char *filename = persist ? "/presets.json" : "/tmp.json"; if (!fileDoc) { - DEBUGFS_PRINTLN(F("Allocating saving buffer")); + DEBUG_PRINTLN(F("Save preset JSON buffer requested.")); #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif sObj = doc.to(); @@ -71,7 +75,7 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj) writeObjectToFileUsingId(filename, index, &doc); - jsonBufferLock = false; + releaseJSONBufferLock(); } else { //from JSON API (fileDoc != nullptr) DEBUGFS_PRINTLN(F("Reuse recv buffer")); sObj.remove(F("psave")); diff --git a/wled00/set.cpp b/wled00/set.cpp index 99429e1d..e97974ee 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -418,9 +418,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif JsonObject um = doc.createNestedObject("um"); @@ -497,7 +498,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) usermods.readFromConfig(um); // force change of usermod parameters } - jsonBufferLock = false; + releaseJSONBufferLock(); if (subPage != 2 && (subPage != 6 || !doReboot)) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init) if (subPage == 4) alexaInit(); diff --git a/wled00/util.cpp b/wled00/util.cpp index 8909fe0b..e9af648e 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -129,3 +129,27 @@ bool isAsterisksOnly(const char* str, byte maxLen) //at this point the password contains asterisks only return (str[0] != 0); //false on empty string } + + +bool requestJSONBufferLock() +{ + unsigned long now = millis(); + + while (jsonBufferLock && millis()-now < 1000) delay(1); // wait for a second for buffer lock + + if (millis()-now >= 1000) return false; // waiting time-outed + + jsonBufferLock = true; + fileDoc = &doc; // used for applying presets (presets.cpp) + doc.clear(); + return true; +} + + +void releaseJSONBufferLock() +{ +#ifndef WLED_USE_DYNAMIC_JSON + fileDoc = nullptr; + jsonBufferLock = false; +#endif +} diff --git a/wled00/wled.h b/wled00/wled.h index 59a8aa87..d21dff4e 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2111091 +#define VERSION 2111121 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp index f4b4e2ae..03d0a806 100644 --- a/wled00/wled_eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -385,9 +385,10 @@ void deEEP() { #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif JsonObject sObj = doc.to(); @@ -473,7 +474,7 @@ void deEEP() { serializeJson(doc, f); f.close(); - jsonBufferLock = false; + releaseJSONBufferLock(); DEBUG_PRINTLN(F("deEEP complete!")); } diff --git a/wled00/wled_serial.cpp b/wled00/wled_serial.cpp index 3a1adc3a..5e250818 100644 --- a/wled00/wled_serial.cpp +++ b/wled00/wled_serial.cpp @@ -43,22 +43,24 @@ void handleSerial() } else if (next == '{') { //JSON API bool verboseResponse = false; + DEBUG_PRINTLN(F("Serial JSON buffer requested.")); #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif Serial.setTimeout(100); DeserializationError error = deserializeJson(doc, Serial); if (error) { - jsonBufferLock = false; + releaseJSONBufferLock(); return; } - fileDoc = &doc; + //fileDoc = &doc; // used for applying presets (presets.cpp) verboseResponse = deserializeState(doc.as()); - fileDoc = nullptr; + //fileDoc = nullptr; //only send response if TX pin is unused for other purposes if (verboseResponse && !pinManager.isPinAllocated(1)) { @@ -70,7 +72,7 @@ void handleSerial() serializeJson(doc, Serial); } - jsonBufferLock = false; + releaseJSONBufferLock(); } break; case AdaState::Header_d: diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index fc5b5100..07ff2107 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -110,12 +110,14 @@ void initServer() bool verboseResponse = false; bool isConfig = false; { //scope JsonDocument so it releases its buffer + DEBUG_PRINTLN(F("HTTP JSON buffer requested.")); #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif DeserializationError error = deserializeJson(doc, (uint8_t*)(request->_tempObject)); @@ -131,13 +133,13 @@ void initServer() serializeJson(root,Serial); DEBUG_PRINTLN(); #endif - fileDoc = &doc; // used for applying presets (presets.cpp) + //fileDoc = &doc; // used for applying presets (presets.cpp) verboseResponse = deserializeState(root); - fileDoc = nullptr; + //fileDoc = nullptr; } else { verboseResponse = deserializeConfig(root); //use verboseResponse to determine whether cfg change should be saved immediately } - jsonBufferLock = false; + releaseJSONBufferLock(); } if (verboseResponse) { if (!isConfig) { diff --git a/wled00/ws.cpp b/wled00/ws.cpp index 022d4e2e..5722a328 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -36,18 +36,20 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp } bool verboseResponse = false; { //scope JsonDocument so it releases its buffer + DEBUG_PRINTLN(F("WS JSON receive buffer requested.")); #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif DeserializationError error = deserializeJson(doc, data, len); JsonObject root = doc.as(); if (error || root.isNull()) { - jsonBufferLock = false; + releaseJSONBufferLock(); return; } /* @@ -64,15 +66,15 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp { wsLiveClientId = root["lv"] ? client->id() : 0; } else { - fileDoc = &doc; + //fileDoc = &doc; // used for applying presets (presets.cpp) verboseResponse = deserializeState(root); - fileDoc = nullptr; + //fileDoc = nullptr; if (!interfaceUpdateCallMode) { //special case, only on playlist load, avoid sending twice in rapid succession if (millis() - lastInterfaceUpdate > 1700) verboseResponse = false; } } - jsonBufferLock = false; + releaseJSONBufferLock(); } //update if it takes longer than 300ms until next "broadcast" if (verboseResponse && (millis() - lastInterfaceUpdate < 1700 || !interfaceUpdateCallMode)) sendDataWs(client); @@ -114,12 +116,14 @@ void sendDataWs(AsyncWebSocketClient * client) AsyncWebSocketMessageBuffer * buffer; { //scope JsonDocument so it releases its buffer + DEBUG_PRINTLN(F("WS JSON send buffer requested.")); #ifdef WLED_USE_DYNAMIC_JSON DynamicJsonDocument doc(JSON_BUFFER_SIZE); #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif JsonObject state = doc.createNestedObject("state"); @@ -129,7 +133,7 @@ void sendDataWs(AsyncWebSocketClient * client) size_t len = measureJson(doc); buffer = ws.makeBuffer(len); if (!buffer) { - jsonBufferLock = false; + releaseJSONBufferLock(); return; //out of memory } /* @@ -140,7 +144,7 @@ void sendDataWs(AsyncWebSocketClient * client) #endif */ serializeJson(doc, (char *)buffer->get(), len +1); - jsonBufferLock = false; + releaseJSONBufferLock(); } DEBUG_PRINT(F("Sending WS data ")); if (client) { diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 2b3e42b0..a1bca6c7 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -254,17 +254,19 @@ void getSettingsJS(byte subPage, char* dest) oappend(SET_F("d.um_p=[6,7,8,9,10,11")); #ifdef WLED_USE_DYNAMIC_JSON - DynamicJsonDocument doc(JSON_BUFFER_SIZE); + DynamicJsonDocument doc(2048); // 2k is enough for usermods #else - while (jsonBufferLock) delay(1); - jsonBufferLock = true; - doc.clear(); + if (!requestJSONBufferLock()) { + DEBUG_PRINTLN(F("ERROR: Locking JSON buffer failed!")); + return; + } #endif JsonObject mods = doc.createNestedObject(F("um")); usermods.addToConfig(mods); if (!mods.isNull()) fillUMPins(mods); - jsonBufferLock = false; + + releaseJSONBufferLock(); #ifdef WLED_ENABLE_DMX oappend(SET_F(",2")); // DMX hardcoded pin