fix false warnings when temporarily exceeding MAX_SEGMENT_DATA

The segment copy constructors can temporarily exceed the budget of MAX_SEGMENT_DATA. The original segment will be de-allocated a few milliseconds later.
This change introduced an "overdraft" budget of 50% that can be used for temporary segment copies.
This commit is contained in:
Frank
2025-11-06 22:09:11 +01:00
parent a956b22a16
commit 5177ebd271
2 changed files with 26 additions and 12 deletions

View File

@@ -87,6 +87,8 @@ extern BusManager busses; // same as wled.h
#endif
#endif
#define MAX_SEGMENT_OVERDATA ((MAX_SEGMENT_DATA) + (MAX_SEGMENT_DATA)/2) // WLEDMM 50% extra overdraft budget
/* How much data bytes each segment should max allocate to leave enough space for other segments,
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / strip.getMaxSegments())
@@ -611,7 +613,7 @@ typedef struct Segment {
inline uint8_t getLightCapabilities(void) const { return _capabilities; }
static size_t getUsedSegmentData(void) { return _usedSegmentData; } // WLEDMM size_t
static void addUsedSegmentData(int len) { _usedSegmentData += len; }
static void addUsedSegmentData(int len) { _usedSegmentData = max(0, int(_usedSegmentData + len)); } // WLEDMM prevent negative alloc
void allocLeds(); //WLEDMM
inline static const CRGBPalette16 &getCurrentPalette(void) { return Segment::_currentPalette; }
@@ -628,7 +630,7 @@ typedef struct Segment {
// runtime data functions
inline size_t dataSize(void) const { return _dataLen; }
bool allocateData(size_t len);
bool allocateData(size_t len, bool allowOverdraft = false);
void deallocateData(void);
void resetIfRequired(void);
void startFrame(void); // cache a few values that don't change while an effect is drawing

View File

@@ -94,7 +94,7 @@ Segment::Segment(const Segment &orig) {
_t = nullptr;
if (ledsrgb && !Segment::_globalLeds) {ledsrgb = nullptr; ledsrgbSize = 0;} // WLEDMM
if (orig.name) { name = new(std::nothrow) char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig.data) { if (allocateData(orig._dataLen, true)) memcpy(data, orig.data, orig._dataLen); }
//if (orig._t) { _t = new(std::nothrow) Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
//else markForReset(); // WLEDMM
// if (orig.ledsrgb && !Segment::_globalLeds) { allocLeds(); if (ledsrgb) memcpy(ledsrgb, orig.ledsrgb, sizeof(CRGB)*length()); } // WLEDMM
@@ -179,7 +179,7 @@ Segment& Segment::operator= (const Segment &orig) {
if (!Segment::_globalLeds) {ledsrgb = nullptr; ledsrgbSize = 0;}; // WLEDMM copy has no buffers (yet)
// copy source data
if (orig.name) { name = new(std::nothrow) char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig.data) { if (allocateData(orig._dataLen, true)) memcpy(data, orig.data, orig._dataLen); }
//if (orig._t) { _t = new(std::nothrow) Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
//else markForReset(); // WLEDMM
//if (orig.ledsrgb && !Segment::_globalLeds) { allocLeds(); if (ledsrgb) memcpy(ledsrgb, orig.ledsrgb, sizeof(CRGB)*length()); } // WLEDMM don't copy old buffer
@@ -218,7 +218,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
return *this;
}
bool Segment::allocateData(size_t len) {
bool Segment::allocateData(size_t len, bool allowOverdraft) { // WLEDMM allowOverdraft for temporary overdraft by segment copy constructor
// WLEDMM
if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation)
if ((call == 0) && (len > 0)) memset(data, 0, len); // erase buffer if called during effect initialisation
@@ -228,9 +228,11 @@ bool Segment::allocateData(size_t len) {
deallocateData();
if (len == 0) return false; // nothing to do
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) {
//USER_PRINTF("Segment::allocateData: Segment data quota exceeded! used:%u request:%u max:%d\n", Segment::getUsedSegmentData(), len, MAX_SEGMENT_DATA);
if (len > 0) errorFlag = ERR_LOW_SEG_MEM; // WLEDMM raise errorflag
return false; //not enough memory
if (!allowOverdraft || (Segment::getUsedSegmentData() + len > MAX_SEGMENT_OVERDATA)) { // WLEDMM 50% overdraft allowed temporarily
//USER_PRINTF("Segment::allocateData: Segment data quota exceeded! used:%u request:%u max:%d\n", Segment::getUsedSegmentData(), len, MAX_SEGMENT_DATA);
if (len > 0) errorFlag = ERR_LOW_SEG_MEM; // WLEDMM raise errorflag
return false; //not enough memory
}
}
// do not use SPI RAM on ESP32 since it is slow
//#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM)
@@ -241,12 +243,15 @@ bool Segment::allocateData(size_t len) {
data = (byte*) malloc(len);
if (!data) {
_dataLen = 0; // WLEDMM reset dataLen
if ((errorFlag != ERR_LOW_MEM) && (errorFlag != ERR_LOW_SEG_MEM)) { // spam filter
USER_PRINT(F("Segment::allocateData: FAILED to allocate "));
USER_PRINT(len); USER_PRINTLN(F(" bytes."));
}
errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag
USER_PRINT(F("Segment::allocateData: FAILED to allocate "));
USER_PRINT(len); USER_PRINTLN(F(" bytes."));
return false;
} //allocation failed
Segment::addUsedSegmentData(len);
DEBUG_PRINTF("Segment::allocateData: %u bytes allocated (%u used)\n", len, Segment::getUsedSegmentData());
_dataLen = len;
memset(data, 0, len);
if ((errorFlag == ERR_LOW_SEG_MEM) || (errorFlag == ERR_LOW_MEM) || (errorFlag == ERR_NORAM_PX)) errorFlag = ERR_NONE; // WLEDMM reset errorflag on success
@@ -254,10 +259,17 @@ bool Segment::allocateData(size_t len) {
}
void Segment::deallocateData() {
if (!data) {_dataLen = 0; return;} // WLEDMM reset dataLen
if (!data) {
if (_dataLen>0) {
Segment::addUsedSegmentData(-_dataLen); // WLEDMM fix housekeeping
DEBUG_PRINTF("Segment::deallocateData unregistering %u bytes as unused.", _dataLen);
}
_dataLen = 0;
return;
} // WLEDMM reset dataLen
free(data);
data = nullptr;
//USER_PRINTF("Segment::deallocateData: free'd %d bytes.\n", _dataLen);
DEBUG_PRINTF("Segment::deallocateData: free'd %d bytes.\n", _dataLen);
Segment::addUsedSegmentData(-_dataLen);
_dataLen = 0;
}