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:
@@ -87,6 +87,8 @@ extern BusManager busses; // same as wled.h
|
|||||||
#endif
|
#endif
|
||||||
#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,
|
/* 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. */
|
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())
|
#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; }
|
inline uint8_t getLightCapabilities(void) const { return _capabilities; }
|
||||||
|
|
||||||
static size_t getUsedSegmentData(void) { return _usedSegmentData; } // WLEDMM size_t
|
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
|
void allocLeds(); //WLEDMM
|
||||||
inline static const CRGBPalette16 &getCurrentPalette(void) { return Segment::_currentPalette; }
|
inline static const CRGBPalette16 &getCurrentPalette(void) { return Segment::_currentPalette; }
|
||||||
@@ -628,7 +630,7 @@ typedef struct Segment {
|
|||||||
|
|
||||||
// runtime data functions
|
// runtime data functions
|
||||||
inline size_t dataSize(void) const { return _dataLen; }
|
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 deallocateData(void);
|
||||||
void resetIfRequired(void);
|
void resetIfRequired(void);
|
||||||
void startFrame(void); // cache a few values that don't change while an effect is drawing
|
void startFrame(void); // cache a few values that don't change while an effect is drawing
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ Segment::Segment(const Segment &orig) {
|
|||||||
_t = nullptr;
|
_t = nullptr;
|
||||||
if (ledsrgb && !Segment::_globalLeds) {ledsrgb = nullptr; ledsrgbSize = 0;} // WLEDMM
|
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.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); }
|
//if (orig._t) { _t = new(std::nothrow) Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
|
||||||
//else markForReset(); // WLEDMM
|
//else markForReset(); // WLEDMM
|
||||||
// if (orig.ledsrgb && !Segment::_globalLeds) { allocLeds(); if (ledsrgb) memcpy(ledsrgb, orig.ledsrgb, sizeof(CRGB)*length()); } // 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)
|
if (!Segment::_globalLeds) {ledsrgb = nullptr; ledsrgbSize = 0;}; // WLEDMM copy has no buffers (yet)
|
||||||
// copy source data
|
// copy source data
|
||||||
if (orig.name) { name = new(std::nothrow) char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
|
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); }
|
//if (orig._t) { _t = new(std::nothrow) Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
|
||||||
//else markForReset(); // WLEDMM
|
//else markForReset(); // WLEDMM
|
||||||
//if (orig.ledsrgb && !Segment::_globalLeds) { allocLeds(); if (ledsrgb) memcpy(ledsrgb, orig.ledsrgb, sizeof(CRGB)*length()); } // WLEDMM don't copy old buffer
|
//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;
|
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
|
// WLEDMM
|
||||||
if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation)
|
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
|
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();
|
deallocateData();
|
||||||
if (len == 0) return false; // nothing to do
|
if (len == 0) return false; // nothing to do
|
||||||
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) {
|
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 (!allowOverdraft || (Segment::getUsedSegmentData() + len > MAX_SEGMENT_OVERDATA)) { // WLEDMM 50% overdraft allowed temporarily
|
||||||
if (len > 0) errorFlag = ERR_LOW_SEG_MEM; // WLEDMM raise errorflag
|
//USER_PRINTF("Segment::allocateData: Segment data quota exceeded! used:%u request:%u max:%d\n", Segment::getUsedSegmentData(), len, MAX_SEGMENT_DATA);
|
||||||
return false; //not enough memory
|
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
|
// do not use SPI RAM on ESP32 since it is slow
|
||||||
//#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM)
|
//#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);
|
data = (byte*) malloc(len);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
_dataLen = 0; // WLEDMM reset dataLen
|
_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
|
errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag
|
||||||
USER_PRINT(F("Segment::allocateData: FAILED to allocate "));
|
|
||||||
USER_PRINT(len); USER_PRINTLN(F(" bytes."));
|
|
||||||
return false;
|
return false;
|
||||||
} //allocation failed
|
} //allocation failed
|
||||||
Segment::addUsedSegmentData(len);
|
Segment::addUsedSegmentData(len);
|
||||||
|
DEBUG_PRINTF("Segment::allocateData: %u bytes allocated (%u used)\n", len, Segment::getUsedSegmentData());
|
||||||
_dataLen = len;
|
_dataLen = len;
|
||||||
memset(data, 0, 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
|
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() {
|
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);
|
free(data);
|
||||||
data = nullptr;
|
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);
|
Segment::addUsedSegmentData(-_dataLen);
|
||||||
_dataLen = 0;
|
_dataLen = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user