A fork of https://github.com/crosspoint-reader/crosspoint-reader
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: properly implement requestUpdateAndWait() (#1218)

## Summary

Properly implement `requestUpdateAndWait()` using freeRTOS direct task
notification.

FWIW, I think most of the current use cases of `requestUpdateAndWait()`
are redundant, better to be replaced by `requestUpdate(true)`. But just
keeping them in case we can find a proper use case for it in the future.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? **YES**, it's trivial, so
I asked an AI to write the code

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

authored by

Xuan-Son Nguyen
coderabbitai[bot]
and committed by
GitHub
a57c62f0 3da2cd3c

+49 -6
+1 -5
src/activities/Activity.cpp
··· 8 8 9 9 void Activity::requestUpdate(bool immediate) { activityManager.requestUpdate(immediate); } 10 10 11 - void Activity::requestUpdateAndWait() { 12 - // FIXME @ngxson : properly implement this using freeRTOS notification 13 - activityManager.requestUpdate(true); 14 - delay(100); 15 - } 11 + void Activity::requestUpdateAndWait() { activityManager.requestUpdateAndWait(); } 16 12 17 13 void Activity::onGoHome() { activityManager.goHome(); } 18 14
+39
src/activities/ActivityManager.cpp
··· 38 38 HalPowerManager::Lock powerLock; // Ensure we don't go into low-power mode while rendering 39 39 currentActivity->render(std::move(lock)); 40 40 } 41 + // Notify any task blocked in requestUpdateAndWait() that the render is done. 42 + TaskHandle_t waiter = nullptr; 43 + taskENTER_CRITICAL(nullptr); 44 + waiter = waitingTaskHandle; 45 + waitingTaskHandle = nullptr; 46 + taskEXIT_CRITICAL(nullptr); 47 + if (waiter) { 48 + xTaskNotify(waiter, 1, eIncrement); 49 + } 41 50 } 42 51 } 43 52 ··· 225 234 requestedUpdate = true; 226 235 } 227 236 } 237 + void ActivityManager::requestUpdateAndWait() { 238 + if (!renderTaskHandle) { 239 + return; 240 + } 241 + 242 + // Atomic section to perform checks 243 + taskENTER_CRITICAL(nullptr); 244 + auto currTaskHandler = xTaskGetCurrentTaskHandle(); 245 + auto mutexHolder = xSemaphoreGetMutexHolder(renderingMutex); 246 + bool isRenderTask = (currTaskHandler == renderTaskHandle); 247 + bool alreadyWaiting = (waitingTaskHandle != nullptr); 248 + bool holdingRenderLock = (mutexHolder == currTaskHandler); 249 + if (!alreadyWaiting && !isRenderTask && !holdingRenderLock) { 250 + waitingTaskHandle = currTaskHandler; 251 + } 252 + taskEXIT_CRITICAL(nullptr); 253 + 254 + // Render task cannot call requestUpdateAndWait() or it will cause a deadlock 255 + assert(!isRenderTask && "Render task cannot call requestUpdateAndWait()"); 256 + 257 + // There should never be the case where 2 tasks are waiting for a render at the same time 258 + assert(!alreadyWaiting && "Already waiting for a render to complete"); 259 + 260 + // Cannot call while holding RenderLock or it will cause a deadlock 261 + assert(!holdingRenderLock && "Cannot call requestUpdateAndWait() while holding RenderLock"); 262 + 263 + xTaskNotify(renderTaskHandle, 1, eIncrement); 264 + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 265 + } 266 + 228 267 // RenderLock 229 268 230 269 RenderLock::RenderLock() {
+8
src/activities/ActivityManager.h
··· 50 50 static void renderTaskTrampoline(void* param); 51 51 [[noreturn]] virtual void renderTaskLoop(); 52 52 53 + // Set by requestUpdateAndWait(); read and cleared by the render task after render completes. 54 + // Note: only one waiting task is supported at a time 55 + TaskHandle_t waitingTaskHandle = nullptr; 56 + 53 57 // Mutex to protect rendering operations from race conditions 54 58 // Must only be used via RenderLock 55 59 SemaphoreHandle_t renderingMutex = nullptr; ··· 98 102 // If immediate is true, the update will be triggered immediately. 99 103 // Otherwise, it will be deferred until the end of the current loop iteration. 100 104 void requestUpdate(bool immediate = false); 105 + 106 + // Trigger a render and block until it completes. 107 + // Must NOT be called from the render task or while holding a RenderLock. 108 + void requestUpdateAndWait(); 101 109 }; 102 110 103 111 extern ActivityManager activityManager; // singleton, to be defined in main.cpp
+1 -1
src/activities/network/WifiSelectionActivity.cpp
··· 201 201 state = WifiSelectionState::NETWORK_LIST; 202 202 } else { 203 203 enteredPassword = std::get<KeyboardResult>(result.data).text; 204 + // state will be updated in next loop iteration 204 205 } 205 206 }); 206 207 } else { ··· 465 466 // Don't render if we're in PASSWORD_ENTRY state - we're just transitioning 466 467 // from the keyboard subactivity back to the main activity 467 468 if (state == WifiSelectionState::PASSWORD_ENTRY) { 468 - requestUpdateAndWait(); 469 469 return; 470 470 } 471 471