Skip to content

schedule runs beyond QTimer's 24.8 day ceiling (#2360, phase 3) - #2547

Open
ebuzerdrmz44 wants to merge 4 commits into
borgbase:masterfrom
ebuzerdrmz44:refactor/scheduler-timer-overflow
Open

schedule runs beyond QTimer's 24.8 day ceiling (#2360, phase 3)#2547
ebuzerdrmz44 wants to merge 4 commits into
borgbase:masterfrom
ebuzerdrmz44:refactor/scheduler-timer-overflow

Conversation

@ebuzerdrmz44

Copy link
Copy Markdown
Contributor

Description

Phase C of #2360. Stacked on #2532, which is where _set_pause and its clamp come from.

A QTimer interval is a C++ int, so one wait tops out at 2**31 - 1 ms, about 24.855 days. Two places hit that, each worked around differently.

set_timer_for_profile stores TOO_FAR_AHEAD with no timer and leaves the 15 minute poll to pick the profile up once the wait fits. The unit list offers weeks and the count spinbox goes to 740, so 4 weeks reaches this from the UI, and next_job skips entries with no active qtt, so the tray reads "None scheduled" for three days after each run while the schedule page shows the correct date beside it. A yearly schedule sits that way for eleven months. The run is not lost, just imprecise.

_set_pause clamps instead. A paused_until read back after a clock correction fires 24.8 days in, set_timer_for_profile finds the pause still running and re-marks it without arming anything, so the repeating timer ends it up to another 24.8 days late.

The fix

arm_deadline_timer(deadline, on_expiry) replaces both: a single-shot timer of at most MAX_TIMER_MS that recomputes the remainder against the wall clock on each expiry and re-arms until the deadline has passed. TOO_FAR_AHEAD loses its writer and comes out with its branch in schedule_page.

Measuring per chunk also absorbs CoarseTimer's 5% early fire, which the old single interval had nothing to catch, and lets a chunk spanning a suspend extend itself instead of running late.

A deadline already in the past arms a zero length timer rather than calling back inline, which keeps set_timer_for_profile from re-entering self.lock through create_backup.

Notes

  • _set_pause now stops the timer it replaces. Overriding a pause always orphaned the old one, harmlessly, because nothing held a reference to it afterwards. The chunked timer keeps a cycle through its own timeout connection, so an orphan would now outlive collection and fire one spurious reschedule.
  • The pause timer is single-shot rather than repeating. Only the first fire was ever used, and the helper's 100 ms grace preserves the old + 100.
  • pending_jobs on add a Jobs view: pending runs and recorded jobs in one table (#2360, phase 4) #2538 lists SCHEDULED and TOO_FAR_AHEAD. Whichever lands second drops that one line.

@ebuzerdrmz44

Copy link
Copy Markdown
Contributor Author

@m3nu Only the last commit, bb8be7d, belongs to this PR. The three under it are #2532's: this is stacked on it, and a PR into this repo has to target a branch here, so they ride along.

Just this PR's change: bb8be7d

store/models.py, store/connection.py and main_window.py are #2532's entirely. So that one wants to go in first, and I'll rebase this down to the single commit once it does.

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.

1 participant