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: use RAII render lock everywhere (#916)

## Summary

Follow-up to
https://github.com/crosspoint-reader/crosspoint-reader/pull/774

---

### 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? **NO**


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **Refactor**
* Modernized internal synchronization mechanisms across multiple
components to improve code reliability and maintainability. All
functionality remains unchanged.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

authored by

Xuan-Son Nguyen and committed by
GitHub
3d47c081 67020609

+149 -124
+4 -3
src/activities/network/WifiSelectionActivity.cpp
··· 244 244 245 245 // Save this as the last connected network - SD card operations need lock as 246 246 // we use SPI for both 247 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 248 - WIFI_STORE.setLastConnectedSsid(selectedSSID); 249 - xSemaphoreGive(renderingMutex); 247 + { 248 + RenderLock lock(*this); 249 + WIFI_STORE.setLastConnectedSsid(selectedSSID); 250 + } 250 251 251 252 // If we entered a new password, ask if user wants to save it 252 253 // Otherwise, immediately complete so parent can start web server
+55 -49
src/activities/reader/EpubReaderActivity.cpp
··· 221 221 222 222 if (skipChapter) { 223 223 // We don't want to delete the section mid-render, so grab the semaphore 224 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 225 - nextPageNumber = 0; 226 - currentSpineIndex = nextTriggered ? currentSpineIndex + 1 : currentSpineIndex - 1; 227 - section.reset(); 228 - xSemaphoreGive(renderingMutex); 224 + { 225 + RenderLock lock(*this); 226 + nextPageNumber = 0; 227 + currentSpineIndex = nextTriggered ? currentSpineIndex + 1 : currentSpineIndex - 1; 228 + section.reset(); 229 + } 229 230 requestUpdate(); 230 231 return; 231 232 } ··· 241 242 section->currentPage--; 242 243 } else { 243 244 // We don't want to delete the section mid-render, so grab the semaphore 244 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 245 - nextPageNumber = UINT16_MAX; 246 - currentSpineIndex--; 247 - section.reset(); 248 - xSemaphoreGive(renderingMutex); 245 + { 246 + RenderLock lock(*this); 247 + nextPageNumber = UINT16_MAX; 248 + currentSpineIndex--; 249 + section.reset(); 250 + } 249 251 } 250 252 requestUpdate(); 251 253 } else { ··· 253 255 section->currentPage++; 254 256 } else { 255 257 // We don't want to delete the section mid-render, so grab the semaphore 256 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 257 - nextPageNumber = 0; 258 - currentSpineIndex++; 259 - section.reset(); 260 - xSemaphoreGive(renderingMutex); 258 + { 259 + RenderLock lock(*this); 260 + nextPageNumber = 0; 261 + currentSpineIndex++; 262 + section.reset(); 263 + } 261 264 } 262 265 requestUpdate(); 263 266 } ··· 325 328 } 326 329 327 330 // Reset state so render() reloads and repositions on the target spine. 328 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 329 - currentSpineIndex = targetSpineIndex; 330 - nextPageNumber = 0; 331 - pendingPercentJump = true; 332 - section.reset(); 333 - xSemaphoreGive(renderingMutex); 331 + { 332 + RenderLock lock(*this); 333 + currentSpineIndex = targetSpineIndex; 334 + nextPageNumber = 0; 335 + pendingPercentJump = true; 336 + section.reset(); 337 + } 334 338 } 335 339 336 340 void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action) { ··· 403 407 break; 404 408 } 405 409 case EpubReaderMenuActivity::MenuAction::DELETE_CACHE: { 406 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 407 - if (epub) { 408 - // 2. BACKUP: Read current progress 409 - // We use the current variables that track our position 410 - uint16_t backupSpine = currentSpineIndex; 411 - uint16_t backupPage = section->currentPage; 412 - uint16_t backupPageCount = section->pageCount; 410 + { 411 + RenderLock lock(*this); 412 + if (epub) { 413 + // 2. BACKUP: Read current progress 414 + // We use the current variables that track our position 415 + uint16_t backupSpine = currentSpineIndex; 416 + uint16_t backupPage = section->currentPage; 417 + uint16_t backupPageCount = section->pageCount; 413 418 414 - section.reset(); 415 - // 3. WIPE: Clear the cache directory 416 - epub->clearCache(); 419 + section.reset(); 420 + // 3. WIPE: Clear the cache directory 421 + epub->clearCache(); 417 422 418 - // 4. RESTORE: Re-setup the directory and rewrite the progress file 419 - epub->setupCacheDir(); 423 + // 4. RESTORE: Re-setup the directory and rewrite the progress file 424 + epub->setupCacheDir(); 420 425 421 - saveProgress(backupSpine, backupPage, backupPageCount); 426 + saveProgress(backupSpine, backupPage, backupPageCount); 427 + } 422 428 } 423 - xSemaphoreGive(renderingMutex); 424 429 // Defer go home to avoid race condition with display task 425 430 pendingGoHome = true; 426 431 break; ··· 458 463 } 459 464 460 465 // Preserve current reading position so we can restore after reflow. 461 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 462 - if (section) { 463 - cachedSpineIndex = currentSpineIndex; 464 - cachedChapterTotalPageCount = section->pageCount; 465 - nextPageNumber = section->currentPage; 466 - } 466 + { 467 + RenderLock lock(*this); 468 + if (section) { 469 + cachedSpineIndex = currentSpineIndex; 470 + cachedChapterTotalPageCount = section->pageCount; 471 + nextPageNumber = section->currentPage; 472 + } 467 473 468 - // Persist the selection so the reader keeps the new orientation on next launch. 469 - SETTINGS.orientation = orientation; 470 - SETTINGS.saveToFile(); 474 + // Persist the selection so the reader keeps the new orientation on next launch. 475 + SETTINGS.orientation = orientation; 476 + SETTINGS.saveToFile(); 471 477 472 - // Update renderer orientation to match the new logical coordinate system. 473 - applyReaderOrientation(renderer, SETTINGS.orientation); 478 + // Update renderer orientation to match the new logical coordinate system. 479 + applyReaderOrientation(renderer, SETTINGS.orientation); 474 480 475 - // Reset section to force re-layout in the new orientation. 476 - section.reset(); 477 - xSemaphoreGive(renderingMutex); 481 + // Reset section to force re-layout in the new orientation. 482 + section.reset(); 483 + } 478 484 } 479 485 480 486 // TODO: Failure handling
+51 -40
src/activities/reader/KOReaderSyncActivity.cpp
··· 51 51 52 52 LOG_DBG("KOSync", "WiFi connected, starting sync"); 53 53 54 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 55 - state = SYNCING; 56 - statusMessage = "Syncing time..."; 57 - xSemaphoreGive(renderingMutex); 54 + { 55 + RenderLock lock(*this); 56 + state = SYNCING; 57 + statusMessage = "Syncing time..."; 58 + } 58 59 requestUpdate(); 59 60 60 61 // Sync time with NTP before making API requests 61 62 syncTimeWithNTP(); 62 63 63 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 64 - statusMessage = "Calculating document hash..."; 65 - xSemaphoreGive(renderingMutex); 64 + { 65 + RenderLock lock(*this); 66 + statusMessage = "Calculating document hash..."; 67 + } 66 68 requestUpdate(); 67 69 68 70 performSync(); ··· 76 78 documentHash = KOReaderDocumentId::calculate(epubPath); 77 79 } 78 80 if (documentHash.empty()) { 79 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 80 - state = SYNC_FAILED; 81 - statusMessage = "Failed to calculate document hash"; 82 - xSemaphoreGive(renderingMutex); 81 + { 82 + RenderLock lock(*this); 83 + state = SYNC_FAILED; 84 + statusMessage = "Failed to calculate document hash"; 85 + } 83 86 requestUpdate(); 84 87 return; 85 88 } 86 89 87 90 LOG_DBG("KOSync", "Document hash: %s", documentHash.c_str()); 88 91 89 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 90 - statusMessage = "Fetching remote progress..."; 91 - xSemaphoreGive(renderingMutex); 92 + { 93 + RenderLock lock(*this); 94 + statusMessage = "Fetching remote progress..."; 95 + } 92 96 requestUpdateAndWait(); 93 97 94 98 // Fetch remote progress ··· 96 100 97 101 if (result == KOReaderSyncClient::NOT_FOUND) { 98 102 // No remote progress - offer to upload 99 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 100 - state = NO_REMOTE_PROGRESS; 101 - hasRemoteProgress = false; 102 - xSemaphoreGive(renderingMutex); 103 + { 104 + RenderLock lock(*this); 105 + state = NO_REMOTE_PROGRESS; 106 + hasRemoteProgress = false; 107 + } 103 108 requestUpdate(); 104 109 return; 105 110 } 106 111 107 112 if (result != KOReaderSyncClient::OK) { 108 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 109 - state = SYNC_FAILED; 110 - statusMessage = KOReaderSyncClient::errorString(result); 111 - xSemaphoreGive(renderingMutex); 113 + { 114 + RenderLock lock(*this); 115 + state = SYNC_FAILED; 116 + statusMessage = KOReaderSyncClient::errorString(result); 117 + } 112 118 requestUpdate(); 113 119 return; 114 120 } ··· 122 128 CrossPointPosition localPos = {currentSpineIndex, currentPage, totalPagesInSpine}; 123 129 localProgress = ProgressMapper::toKOReader(epub, localPos); 124 130 125 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 126 - state = SHOWING_RESULT; 127 - selectedOption = 0; // Default to "Apply" 128 - xSemaphoreGive(renderingMutex); 131 + { 132 + RenderLock lock(*this); 133 + state = SHOWING_RESULT; 134 + selectedOption = 0; // Default to "Apply" 135 + } 129 136 requestUpdate(); 130 137 } 131 138 132 139 void KOReaderSyncActivity::performUpload() { 133 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 134 - state = UPLOADING; 135 - statusMessage = "Uploading progress..."; 136 - xSemaphoreGive(renderingMutex); 140 + { 141 + RenderLock lock(*this); 142 + state = UPLOADING; 143 + statusMessage = "Uploading progress..."; 144 + } 137 145 requestUpdate(); 138 146 requestUpdateAndWait(); 139 147 ··· 149 157 const auto result = KOReaderSyncClient::updateProgress(progress); 150 158 151 159 if (result != KOReaderSyncClient::OK) { 152 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 153 - state = SYNC_FAILED; 154 - statusMessage = KOReaderSyncClient::errorString(result); 155 - xSemaphoreGive(renderingMutex); 160 + { 161 + RenderLock lock(*this); 162 + state = SYNC_FAILED; 163 + statusMessage = KOReaderSyncClient::errorString(result); 164 + } 156 165 requestUpdate(); 157 166 return; 158 167 } 159 168 160 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 161 - state = UPLOAD_COMPLETE; 162 - xSemaphoreGive(renderingMutex); 169 + { 170 + RenderLock lock(*this); 171 + state = UPLOAD_COMPLETE; 172 + } 163 173 requestUpdate(); 164 174 } 165 175 ··· 190 200 auto* self = static_cast<KOReaderSyncActivity*>(param); 191 201 // Sync time first 192 202 syncTimeWithNTP(); 193 - xSemaphoreTake(self->renderingMutex, portMAX_DELAY); 194 - self->statusMessage = "Calculating document hash..."; 195 - xSemaphoreGive(self->renderingMutex); 203 + { 204 + RenderLock lock(*self); 205 + self->statusMessage = "Calculating document hash..."; 206 + } 196 207 self->requestUpdate(); 197 208 self->performSync(); 198 209 vTaskDelete(nullptr);
+4 -3
src/activities/settings/ClearCacheActivity.cpp
··· 118 118 if (state == WARNING) { 119 119 if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { 120 120 LOG_DBG("CLEAR_CACHE", "User confirmed, starting cache clear"); 121 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 122 - state = CLEARING; 123 - xSemaphoreGive(renderingMutex); 121 + { 122 + RenderLock lock(*this); 123 + state = CLEARING; 124 + } 124 125 requestUpdateAndWait(); 125 126 126 127 clearCache();
+19 -16
src/activities/settings/KOReaderAuthActivity.cpp
··· 14 14 exitActivity(); 15 15 16 16 if (!success) { 17 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 18 - state = FAILED; 19 - errorMessage = "WiFi connection failed"; 20 - xSemaphoreGive(renderingMutex); 17 + { 18 + RenderLock lock(*this); 19 + state = FAILED; 20 + errorMessage = "WiFi connection failed"; 21 + } 21 22 requestUpdate(); 22 23 return; 23 24 } 24 25 25 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 26 - state = AUTHENTICATING; 27 - statusMessage = "Authenticating..."; 28 - xSemaphoreGive(renderingMutex); 26 + { 27 + RenderLock lock(*this); 28 + state = AUTHENTICATING; 29 + statusMessage = "Authenticating..."; 30 + } 29 31 requestUpdate(); 30 32 31 33 performAuthentication(); ··· 34 36 void KOReaderAuthActivity::performAuthentication() { 35 37 const auto result = KOReaderSyncClient::authenticate(); 36 38 37 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 38 - if (result == KOReaderSyncClient::OK) { 39 - state = SUCCESS; 40 - statusMessage = "Successfully authenticated!"; 41 - } else { 42 - state = FAILED; 43 - errorMessage = KOReaderSyncClient::errorString(result); 39 + { 40 + RenderLock lock(*this); 41 + if (result == KOReaderSyncClient::OK) { 42 + state = SUCCESS; 43 + statusMessage = "Successfully authenticated!"; 44 + } else { 45 + state = FAILED; 46 + errorMessage = KOReaderSyncClient::errorString(result); 47 + } 44 48 } 45 - xSemaphoreGive(renderingMutex); 46 49 requestUpdate(); 47 50 } 48 51
-1
src/activities/settings/KOReaderSettingsActivity.cpp
··· 121 121 // Authenticate 122 122 if (!KOREADER_STORE.hasCredentials()) { 123 123 // Can't authenticate without credentials - just show message briefly 124 - xSemaphoreGive(renderingMutex); 125 124 return; 126 125 } 127 126 exitActivity();
+16 -12
src/activities/settings/OtaUpdateActivity.cpp
··· 20 20 21 21 LOG_DBG("OTA", "WiFi connected, checking for update"); 22 22 23 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 24 - state = CHECKING_FOR_UPDATE; 25 - xSemaphoreGive(renderingMutex); 23 + { 24 + RenderLock lock(*this); 25 + state = CHECKING_FOR_UPDATE; 26 + } 26 27 requestUpdateAndWait(); 27 28 28 29 const auto res = updater.checkForUpdate(); 29 30 if (res != OtaUpdater::OK) { 30 31 LOG_DBG("OTA", "Update check failed: %d", res); 31 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 32 - state = FAILED; 33 - xSemaphoreGive(renderingMutex); 32 + { 33 + RenderLock lock(*this); 34 + state = FAILED; 35 + } 34 36 requestUpdate(); 35 37 return; 36 38 } 37 39 38 40 if (!updater.isUpdateNewer()) { 39 41 LOG_DBG("OTA", "No new update available"); 40 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 41 - state = NO_UPDATE; 42 - xSemaphoreGive(renderingMutex); 42 + { 43 + RenderLock lock(*this); 44 + state = NO_UPDATE; 45 + } 43 46 requestUpdate(); 44 47 return; 45 48 } 46 49 47 - xSemaphoreTake(renderingMutex, portMAX_DELAY); 48 - state = WAITING_CONFIRMATION; 49 - xSemaphoreGive(renderingMutex); 50 + { 51 + RenderLock lock(*this); 52 + state = WAITING_CONFIRMATION; 53 + } 50 54 requestUpdate(); 51 55 } 52 56