diff --git a/wled00/presets.cpp b/wled00/presets.cpp index b8d121d0..644b031d 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -38,7 +38,14 @@ static void doSaveState() { bool persist = (presetToSave < 251); const char *filename = getFileName(persist); - if (!requestJSONBufferLock(10)) return; // will set fileDoc + if (!requestJSONBufferLock(10)) return; // will set fileDoc // async write + + // WLEDMM Acquire file mutex before writing presets.json or tmp.json + if (esp32SemTake(presetFileMux, 2500) != pdTRUE) { + USER_PRINTLN(F("doSaveState(): preset file busy, cannot write")); + releaseJSONBufferLock(); + return; + } initPresetsFile(); // just in case if someone deleted presets.json using /edit JsonObject sObj = doc.to(); @@ -82,6 +89,8 @@ static void doSaveState() { writeObjectToFileUsingId(filename, presetToSave, fileDoc); if (persist) presetsModifiedTime = toki.second(); //unix time + + esp32SemGive(presetFileMux); // Release file mutex releaseJSONBufferLock(); updateFSInfo(); @@ -316,7 +325,14 @@ void savePreset(byte index, const char* pname, JsonObject sObj) } else { // this is a playlist or API call if (sObj[F("playlist")].isNull()) { - // we will save API call immediately (often causes presets.json corruption) + // we will save API call immediately (often causes presets.json corruption in the past) + + // WLEDMM Acquire file mutex before writing presets.json, to prevent presets.json corruption + if (esp32SemTake(presetFileMux, 2500) != pdTRUE) { + USER_PRINTLN(F("doSaveState(): preset file busy, cannot write")); + return; // early exit, no change + } + presetToSave = 0; if (index > 250 || !fileDoc) return; // cannot save API calls to temporary preset (255) sObj.remove("o"); @@ -325,8 +341,11 @@ void savePreset(byte index, const char* pname, JsonObject sObj) sObj.remove(F("error")); sObj.remove(F("psave")); if (sObj["n"].isNull()) sObj["n"] = saveName; + initPresetsFile(); // just in case if someone deleted presets.json using /edit writeObjectToFileUsingId(getFileName(index<255), index, fileDoc); + + esp32SemGive(presetFileMux); // Release file mutex presetsModifiedTime = toki.second(); //unix time updateFSInfo(); } else { diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 789f7ce3..2c2b18d3 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -480,11 +480,14 @@ void WLED::setup() busDrawMux = xSemaphoreCreateRecursiveMutex(); // WLEDMM prevent concurrent running of strip.show and strip.service segmentMux = xSemaphoreCreateRecursiveMutex(); // WLEDMM prevent segment changes while effects are running jsonBufferLockMutex = xSemaphoreCreateRecursiveMutex(); // WLEDMM prevent concurrent JSON buffer writing - if ((busDrawMux == nullptr) || (segmentMux == nullptr) || (jsonBufferLockMutex == nullptr)) + presetFileMux = xSemaphoreCreateRecursiveMutex(); // WLEDMM prevent concurrent presets.json file writing + if ((busDrawMux == nullptr) || (segmentMux == nullptr) || (jsonBufferLockMutex == nullptr) || (presetFileMux == nullptr)) { USER_PRINTLN(F("setup error: xSemaphoreCreateRecursiveMutex failed.")); // should never happen. + } xSemaphoreGiveRecursive(busDrawMux); // init semaphores to initially allow drawing xSemaphoreGiveRecursive(segmentMux); xSemaphoreGiveRecursive(jsonBufferLockMutex); + xSemaphoreGiveRecursive(presetFileMux); #endif #ifdef ARDUINO_ARCH_ESP32 diff --git a/wled00/wled.h b/wled00/wled.h index 22a26e92..07f5cac3 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -7,7 +7,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2512291 +#define VERSION 2512292 // WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED. #define _MoonModules_WLED_ @@ -771,6 +771,7 @@ WLED_GLOBAL volatile bool OTAisRunning _INIT(false); // WLEDMM temporaril WLED_GLOBAL SemaphoreHandle_t busDrawMux _INIT(nullptr); WLED_GLOBAL SemaphoreHandle_t segmentMux _INIT(nullptr); WLED_GLOBAL SemaphoreHandle_t jsonBufferLockMutex _INIT(nullptr); +WLED_GLOBAL SemaphoreHandle_t presetFileMux _INIT(nullptr); // Protects presets.json file writes #define esp32SemTake(mux,timeout) xSemaphoreTakeRecursive(mux, pdMS_TO_TICKS(timeout)) // convenience macro that expands to xSemaphoreTakeRecursive - timeout is in milliseconds #define esp32SemGive(mux) xSemaphoreGiveRecursive(mux) // convenience macro that expands to xSemaphoreGiveRecursive #define WLED_create_spinlock(theSname) static portMUX_TYPE theSname = portMUX_INITIALIZER_UNLOCKED