From 6a10f77c15e4be47c792b89f1a3cc4e992389684 Mon Sep 17 00:00:00 2001 From: Adam Honse Date: Thu, 15 Oct 2026 11:55:15 -0500 Subject: [PATCH] Wait for device updates to complete in cli.cpp rather than just a fixed 1 second wait --- RGBController/RGBController.cpp | 52 +++++++++++++++++++++++++++++++++ RGBController/RGBController.h | 11 +++++++ cli.cpp | 11 ++++++- 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/RGBController/RGBController.cpp b/RGBController/RGBController.cpp index 206ec5621..a97b894d6 100644 --- a/RGBController/RGBController.cpp +++ b/RGBController/RGBController.cpp @@ -16,6 +16,11 @@ #include "RGBController.h" #include "StringUtils.h" +/*---------------------------------------------------------*\ +| Upper bound for WaitForDeviceUpdate(), in milliseconds | +\*---------------------------------------------------------*/ +#define DEVICE_UPDATE_WAIT_TIMEOUT_MS 60000 + /*---------------------------------------------------------*\ | Macros for copying data fields from set descriptor buffer | | while ensuring we don't access out of bounds | @@ -91,6 +96,7 @@ RGBController::RGBController() /*-----------------------------------------------------*\ | Initialize device thread | \*-----------------------------------------------------*/ + DeviceCallActive = false; DeviceThreadRunning = true; DeviceCallThread = new std::thread(&RGBController::DeviceCallThreadFunction, this); } @@ -1994,6 +2000,27 @@ void RGBController::Shutdown() SignalCallsDone.wait(wait_lock, [this]{ return SignalCalls == 0; }); } +void RGBController::WaitForDeviceUpdate() +{ + /*-----------------------------------------------------*\ + | Wait until the device thread has no queued updates | + | left to write and is not writing to the device. | + | Bounded so that an update which can never complete | + | cannot hang shutdown forever. | + \*-----------------------------------------------------*/ + std::unique_lock wait_lock(DeviceCallMutex); + + bool update_complete = DeviceCallDone.wait_for(wait_lock, std::chrono::milliseconds(DEVICE_UPDATE_WAIT_TIMEOUT_MS), [this] + { + return(!CallFlag_UpdateLEDs.load() && !CallFlag_UpdateMode.load() && !DeviceCallActive); + }); + + if(!update_complete) + { + LOG_WARNING("[RGBController] %s: Timed out waiting for pending device updates", name.c_str()); + } +} + void RGBController::UpdateLEDs() { CallFlag_UpdateLEDs = true; @@ -2068,6 +2095,18 @@ void RGBController::DeviceCallThreadFunction() bool update_mode = CallFlag_UpdateMode.load(); bool update_leds = CallFlag_UpdateLEDs.load(); + /*-------------------------------------------------*\ + | Mark the device thread busy before starting the | + | queued updates so that WaitForDeviceUpdate() | + | cannot return while a write is in progress. | + \*-------------------------------------------------*/ + if(update_mode || update_leds) + { + DeviceCallMutex.lock(); + DeviceCallActive = true; + DeviceCallMutex.unlock(); + } + if(update_mode == true) { if(flags & CONTROLLER_FLAG_RESET_BEFORE_UPDATE) @@ -2114,6 +2153,19 @@ void RGBController::DeviceCallThreadFunction() { std::this_thread::sleep_for(1ms); } + + /*-------------------------------------------------*\ + | Mark the device thread idle once the queued | + | updates are written and wake any waiter | + \*-------------------------------------------------*/ + if(update_mode || update_leds) + { + DeviceCallMutex.lock(); + DeviceCallActive = false; + DeviceCallMutex.unlock(); + + DeviceCallDone.notify_all(); + } } } diff --git a/RGBController/RGBController.h b/RGBController/RGBController.h index 4f52a918e..5fc737769 100644 --- a/RGBController/RGBController.h +++ b/RGBController/RGBController.h @@ -178,6 +178,8 @@ public: \*-----------------------------------------------------*/ void Shutdown(); + void WaitForDeviceUpdate(); + void UpdateLEDs(); void UpdateZoneLEDs(int zone); void UpdateSingleLED(int led); @@ -324,6 +326,15 @@ private: std::atomic CallFlag_UpdateMode; std::atomic DeviceThreadRunning; + /*-----------------------------------------------------*\ + | Device update wait variables | + | WaitForDeviceUpdate() blocks until the device thread | + | has written every queued update to the device | + \*-----------------------------------------------------*/ + std::mutex DeviceCallMutex; + std::condition_variable DeviceCallDone; + bool DeviceCallActive; + /*-----------------------------------------------------*\ | Update callback variables | \*-----------------------------------------------------*/ diff --git a/cli.cpp b/cli.cpp index 1ec4bccd0..e7e6d616d 100644 --- a/cli.cpp +++ b/cli.cpp @@ -1968,7 +1968,16 @@ unsigned int cli_post_detection() } } - std::this_thread::sleep_for(1s); + /*-----------------------------------------------------*\ + | Wait for each controller's device thread to finish | + | applying queued updates to hardware before returning | + | so that the updates are not dropped when the | + | controllers shut down | + \*-----------------------------------------------------*/ + for(std::size_t controller_idx = 0; controller_idx < rgb_controllers.size(); controller_idx++) + { + rgb_controllers[controller_idx]->WaitForDeviceUpdate(); + } return 0; }