Skip to content

Commit 2d016ae

Browse files
committed
Fix LED-device updates queue up and let Hyperion crash (#1887)
1 parent 8c42e26 commit 2d016ae

File tree

3 files changed

+73
-9
lines changed

3 files changed

+73
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222
- Qt-Grabber (Windows) does not apply pixel ratio (#1882) - _Thanks to @SolberLight_
2323
- LED-devices are not retrying to establish connectivity, if supported by the device
2424
- LED-devices are resolving IP-addresses for API and UDP two times in sequence
25+
- LED-device updates queue up and let Hyperion crash (#1887)
2526

2627
---
2728

include/leddevice/LedDevice.h

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,10 @@ public slots:
209209
///
210210
/// @brief Update the color values of the device's LEDs.
211211
///
212-
/// Handles refreshing of LEDs.
212+
/// Updates are skipped while another update is in progress to avoid queueing.
213213
///
214214
/// @param[in] ledValues The color per LED
215-
/// @return Zero on success else negative (i.e. device is not ready)
215+
/// @return Zero on success else negative
216216
///
217217
virtual int updateLeds(std::vector<ColorRgb> ledValues);
218218

@@ -426,7 +426,7 @@ public slots:
426426
/// @param data ByteArray
427427
/// @param number Number of array items to be converted.
428428
/// @return array as string of hex values
429-
static QString toHex(const QByteArray& data, int number = -1) ;
429+
static QString toHex(const QByteArray& data, qsizetype number = -1) ;
430430

431431
/// Current device's type
432432
QString _activeDeviceType;
@@ -508,13 +508,30 @@ protected slots:
508508

509509
private slots:
510510

511+
///
512+
/// @brief Process LED updates requested.
513+
///
514+
/// @return Zero on success else negative
515+
///
516+
void processLedUpdate();
517+
511518
///
512519
/// @brief Retry to enable the LED-device
513520
///
514521
void retryEnable();
515522

516523
private:
517524

525+
///
526+
/// @brief Update the color values of the device's LEDs.
527+
///
528+
/// Handles refreshing of LEDs.
529+
///
530+
/// @param[in] ledValues The color per LED
531+
/// @return Zero on success else negative (i.e. device is not ready)
532+
///
533+
int writeLedUpdate(const std::vector<ColorRgb>& ledValues);
534+
518535
/// @brief Start a new refresh cycle
519536
void startRefreshTimer();
520537

@@ -542,6 +559,14 @@ private slots:
542559

543560
/// Last LED values written
544561
std::vector<ColorRgb> _lastLedValues;
562+
563+
// Member variables for the new logic
564+
// Use std::atomic for the flag. No mutex needed for it!
565+
std::atomic<bool> _isUpdatePending{ false };
566+
567+
// The mutex now ONLY protects the data buffer.
568+
QMutex _ledBufferMutex;
569+
std::vector<ColorRgb> _ledUpdateBuffer;
545570
};
546571

547572
#endif // LEDEVICE_H

libsrc/leddevice/LedDevice.cpp

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,36 @@ void LedDevice::stopEnableAttemptsTimer()
309309
}
310310

311311
int LedDevice::updateLeds(std::vector<ColorRgb> ledValues)
312+
{
313+
// Take the LED update into a shared buffer and return quickly
314+
{
315+
QMutexLocker locker(&_ledBufferMutex);
316+
_ledUpdateBuffer = std::move(ledValues);
317+
}
318+
319+
// If a frame processing is NOT already scheduled, schedule one.
320+
if (!_isUpdatePending.exchange(true))
321+
{
322+
QTimer::singleShot(0, this, &LedDevice::processLedUpdate);
323+
}
324+
325+
return 0; // Return immediately
326+
}
327+
328+
void LedDevice::processLedUpdate()
329+
{
330+
std::vector<ColorRgb> valuesToProcess;
331+
{
332+
QMutexLocker locker(&_ledBufferMutex);
333+
valuesToProcess = _ledUpdateBuffer;
334+
}
335+
336+
writeLedUpdate(valuesToProcess);
337+
338+
_isUpdatePending.store(false);
339+
}
340+
341+
int LedDevice::writeLedUpdate(const std::vector<ColorRgb>& ledValues)
312342
{
313343
int retval = 0;
314344
if (!_isEnabled || !_isOn || !_isDeviceReady || _isDeviceInError)
@@ -318,7 +348,7 @@ int LedDevice::updateLeds(std::vector<ColorRgb> ledValues)
318348
}
319349
else
320350
{
321-
qint64 elapsedTimeMs = _lastWriteTime.msecsTo(QDateTime::currentDateTime());
351+
qint64 const elapsedTimeMs = _lastWriteTime.msecsTo(QDateTime::currentDateTime());
322352
if (_latchTime_ms == 0 || elapsedTimeMs >= _latchTime_ms)
323353
{
324354
retval = write(ledValues);
@@ -346,6 +376,14 @@ int LedDevice::updateLeds(std::vector<ColorRgb> ledValues)
346376

347377
int LedDevice::rewriteLEDs()
348378
{
379+
bool expected = false;
380+
if (!_isUpdatePending.compare_exchange_strong(expected, true))
381+
{
382+
// The flag was already true, meaning a write from updateLeds/processFrame is currently in progress or scheduled.
383+
// We can safely skip this refresh, as the other write will restart the timer.
384+
return 0;
385+
}
386+
349387
int retval = -1;
350388

351389
if (_isEnabled && _isOn && _isDeviceReady && !_isDeviceInError)
@@ -526,7 +564,7 @@ QJsonObject LedDevice::discover(const QJsonObject& /*params*/)
526564

527565
devicesDiscovered.insert("ledDeviceType", _activeDeviceType);
528566

529-
QJsonArray deviceList;
567+
QJsonArray const deviceList;
530568
devicesDiscovered.insert("devices", deviceList);
531569

532570
Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
@@ -548,7 +586,7 @@ QJsonObject LedDevice::getProperties(const QJsonObject& params)
548586

549587
QJsonObject properties;
550588

551-
QJsonObject deviceProperties;
589+
QJsonObject const deviceProperties;
552590
properties.insert("properties", deviceProperties);
553591

554592
Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData());
@@ -633,7 +671,7 @@ void LedDevice::printLedValues(const std::vector<ColorRgb>& ledValues)
633671
{
634672
std::cout << color;
635673
}
636-
std::cout << "]" << std::endl;
674+
std::cout << "]\n";
637675
}
638676

639677
QString LedDevice::uint8_t_to_hex_string(const uint8_t* data, const int size, int number)
@@ -643,15 +681,15 @@ QString LedDevice::uint8_t_to_hex_string(const uint8_t* data, const int size, in
643681
number = size;
644682
}
645683

646-
QByteArray bytes(reinterpret_cast<const char*>(data), number);
684+
QByteArray const bytes(reinterpret_cast<const char*>(data), number);
647685
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
648686
return bytes.toHex(':');
649687
#else
650688
return bytes.toHex();
651689
#endif
652690
}
653691

654-
QString LedDevice::toHex(const QByteArray& data, int number)
692+
QString LedDevice::toHex(const QByteArray& data, qsizetype number)
655693
{
656694
if (number <= 0 || number > data.size())
657695
{

0 commit comments

Comments
 (0)