Skip to content

fix: prevent infinite render loop in Calibre Wireless after file transfer#1070

Merged
znelson merged 1 commit intocrosspoint-reader:masterfrom
pablohc:fix/calibre-wireless-render-loop
Feb 27, 2026
Merged

fix: prevent infinite render loop in Calibre Wireless after file transfer#1070
znelson merged 1 commit intocrosspoint-reader:masterfrom
pablohc:fix/calibre-wireless-render-loop

Conversation

@pablohc
Copy link
Contributor

@pablohc pablohc commented Feb 21, 2026

Summary

  • What is the goal of this PR?
    Fix an infinite render loop bug in CalibreConnectActivity that caused the e-ink display to refresh continuously every ~421ms after a file transfer completed.

  • What changes are included?

  • Added lastProcessedCompleteAt member variable to track which server completion timestamp has already been processed
  • Modified the completion status update logic to only accept new values from the server, preventing re-processing of old timestamps
  • Added clarifying comments explaining the fix

Problem Description

After receiving a file via Calibre Wireless, the activity displays "Received: [filename]" for 6 seconds, then clears the message. However, the web server's wsLastCompleteAt timestamp persists indefinitely and is never cleared.

This created a race condition:

After 6 seconds, lastCompleteAt is set to 0 (timeout)
In the next loop iteration, status.lastCompleteAt (still has the old timestamp) ≠ lastCompleteAt (0)
The code restores lastCompleteAt from the server value
Immediately, the 6-second timeout condition is met again
This creates an infinite cycle causing unnecessary e-ink refreshes

Solution

The fix introduces lastProcessedCompleteAt to track which server timestamp value has already been processed:

Only accept a new status.lastCompleteAt if it differs from lastProcessedCompleteAt
Update lastProcessedCompleteAt when processing a new value
Do NOT reset lastProcessedCompleteAt when the 6-second timeout clears lastCompleteAt
This prevents re-processing the same old server value after the timeout.

Testing

Tested on device with multiple file transfer scenarios:

✅ File received message appears correctly after transfer
✅ Message clears after 6 seconds as expected
✅ No infinite render loop after timeout
✅ Multiple consecutive transfers work correctly
✅ Exiting and re-entering Calibre Wireless works as expected

Performance Impact

Before: Infinite refreshes every ~421ms after timeout (high battery drain, display wear)
After: 2-3 refreshes after timeout, then stops (normal behavior)

Additional Context

This is a targeted fix that only affects the Calibre Wireless file transfer screen. The root cause is the architectural difference between the persistent web server state (wsLastCompleteAt) and the per-activity display state (lastCompleteAt).

An alternative fix would be to clear wsLastCompleteAt in the web server after some timeout, but that would affect all consumers of the web server status. The chosen solution keeps the fix localized to CalibreConnectActivity.


AI Usage

Did you use AI tools to help write this code? < YES >

…sfer

Fixed infinite render loop in CalibreConnectActivity that occurred after a file transfer completed and the 6-second "File received" message timeout expired.

The web server's wsLastCompleteAt timestamp persists indefinitely. When the 6-second timeout set lastCompleteAt=0, the next loop iteration would restore the old value from the server and immediately trigger the timeout again, creating an infinite cycle.

Added lastProcessedCompleteAt to track which server timestamp has already been processed, preventing re-processing of the same old value after the timeout.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 21, 2026

No actionable comments were generated in the recent review. 🎉

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e32d41a and e30e166.

📒 Files selected for processing (2)
  • src/activities/network/CalibreConnectActivity.cpp
  • src/activities/network/CalibreConnectActivity.h
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: CaptainFrito
Repo: crosspoint-reader/crosspoint-reader PR: 725
File: src/activities/network/CalibreConnectActivity.cpp:218-264
Timestamp: 2026-02-12T06:57:35.955Z
Learning: In src/activities/network/CalibreConnectActivity.cpp, button hints (« Exit) are intentionally omitted from the SERVER_STARTING and ERROR states—only the SERVER_RUNNING state displays navigation hints. This is a deliberate design decision by the author.
📚 Learning: 2026-02-12T06:57:35.955Z
Learnt from: CaptainFrito
Repo: crosspoint-reader/crosspoint-reader PR: 725
File: src/activities/network/CalibreConnectActivity.cpp:218-264
Timestamp: 2026-02-12T06:57:35.955Z
Learning: In src/activities/network/CalibreConnectActivity.cpp, button hints (« Exit) are intentionally omitted from the SERVER_STARTING and ERROR states—only the SERVER_RUNNING state displays navigation hints. This is a deliberate design decision by the author.

Applied to files:

  • src/activities/network/CalibreConnectActivity.cpp
📚 Learning: 2026-02-16T20:43:19.339Z
Learnt from: Levrk
Repo: crosspoint-reader/crosspoint-reader PR: 909
File: src/activities/reader/EpubReaderActivity.cpp:461-461
Timestamp: 2026-02-16T20:43:19.339Z
Learning: In src/activities/reader/EpubReaderActivity.cpp, when using ConfirmationActivity with enterNewActivity(), it's not necessary to call exitActivity() beforehand. The ConfirmationActivity operates as a modal dialog and cleanup is handled via the pendingSubactivityExit flag in the callback lambda. This differs from other activities like EpubReaderChapterSelectionActivity or KOReaderSyncActivity which do require exitActivity() before enterNewActivity().

Applied to files:

  • src/activities/network/CalibreConnectActivity.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (4)
src/activities/network/CalibreConnectActivity.h (1)

29-29: LGTM!

The new member variable is appropriately typed to match lastCompleteAt, well-placed alongside related state variables, and clearly documented with its purpose.

src/activities/network/CalibreConnectActivity.cpp (3)

31-31: LGTM!

Correctly resets lastProcessedCompleteAt on activity entry, ensuring fresh state for re-entry scenarios.


151-158: Well-implemented fix for the infinite loop.

The deduplication logic correctly prevents re-processing stale server timestamps. The guard condition status.lastCompleteAt != 0 && status.lastCompleteAt != lastProcessedCompleteAt ensures we only process genuinely new completions, breaking the loop where the 6-second timeout reset kept restoring the persistent server value.


159-164: LGTM!

The deliberate decision to NOT reset lastProcessedCompleteAt on timeout is the key to breaking the infinite loop. The explanatory comment is valuable for future maintainers understanding why this asymmetry exists.


📝 Walkthrough

Walkthrough

The changes add a deduplication mechanism to CalibreConnectActivity by introducing a new state variable that tracks which server completion values have already been processed, preventing duplicate event processing while maintaining the existing timeout behavior.

Changes

Cohort / File(s) Summary
Deduplication State Tracking
src/activities/network/CalibreConnectActivity.h, src/activities/network/CalibreConnectActivity.cpp
Added lastProcessedCompleteAt member variable to track processed completion events. Updated event processing logic to only process completion events when the server value is non-zero and differs from the previously processed value. Timeout behavior reset lastCompleteAt but intentionally preserves lastProcessedCompleteAt to prevent re-processing stale values.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix—preventing an infinite render loop in Calibre Wireless after file transfer—which directly matches the primary objective of the changeset.
Description check ✅ Passed The description thoroughly explains the problem, solution, testing, and context, all directly related to the bug fix changes made in the pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@pablohc
Copy link
Contributor Author

pablohc commented Feb 21, 2026

Hi @itsthisjustin, could you validate this fix? This was my first time transferring a book from Calibre, and I started noticing the screen behaving strangely. I investigated further, and Claude found the solution.

@itsthisjustin
Copy link
Contributor

Will take a look tonight!

@itsthisjustin
Copy link
Contributor

Hi @itsthisjustin, could you validate this fix? This was my first time transferring a book from Calibre, and I started noticing the screen behaving strangely. I investigated further, and Claude found the solution.

Yes I saw the same thing. Locked up and wouldn't let me go back either. Was going to jump into this but patched the plugin yesterday instead. Good to see it's approved.

@osteotek osteotek requested a review from a team February 24, 2026 22:26
@pablohc
Copy link
Contributor Author

pablohc commented Feb 27, 2026

Hi @daveallie, could this PR get a review?

After a file transfer finishes in Calibre Wireless, a render loop starts, leaving ghost text on the screen that doesn’t clear, even after a full refresh.

This occurs after a transfer finishes and the device goes to sleep (~10 min.).

Screenshot_2026-02-27-02-01-20-05_99c04817c0de5652397fc8b56c3b3817

@znelson znelson merged commit 5b11e45 into crosspoint-reader:master Feb 27, 2026
7 checks passed
@pablohc pablohc deleted the fix/calibre-wireless-render-loop branch February 27, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants