Skip to content

Ask the engine to stop when the session ends - #141

Merged
xroche merged 6 commits into
masterfrom
mirror-survives-logoff
Aug 23, 2026
Merged

Ask the engine to stop when the session ends#141
xroche merged 6 commits into
masterfrom
mirror-survives-logoff

Conversation

@xroche

@xroche xroche commented Aug 22, 2026

Copy link
Copy Markdown
Owner

WinHTTrack ignores WM_QUERYENDSESSION, so logging off kills the engine thread mid-write. The frame now asks the engine to stop, the same request the Cancel button makes once confirmed, and returns TRUE straight away. It cannot wait for the stop: waiting means pumping messages, and the engine's end-of-mirror SendMessage then opens a modal box on top of the handler, which is the hang Windows force-kills. The engine unwinds on whatever time the rest of the shutdown leaves it, and soft_term_requested makes lance() write interrupted.lock, so the project reopens on "continue interrupted download".

Both handlers route through SessionEndStop(bEnding). The ask is guarded on soft_term_requested rather than on a flag of our own, because init_lance() clears it before every mirror: OnQueryEndSession and OnEndSession firing together cannot escalate to termine_requested and abort abruptly, and a shutdown the user cancels still leaves the next mirror its own ask. The selftest covers six cases, the cancelled shutdown among them, where the mirror has to be left running.

CI cannot log off, so the unwind needs a VM: start a mirror on a large site, log off within five seconds, log back in, and check that no crash dialog appeared, that hts-cache/interrupted.lock exists, and that reopening the project preselects "continue interrupted download". Repeat after a minute of mirroring, so more than 32 KB is cached, and once more cancelling the shutdown at the confirmation prompt.

xroche and others added 2 commits August 22, 2026 22:55
Nothing in the tree handled WM_QUERYENDSESSION or WM_ENDSESSION, so a session
end killed the engine thread inside hts_main2 mid-request, with no clean stop
and no chance to mark the mirror interrupted.

CMainFrame now reuses the Cancel button's stop path, factored out of
Cinprogress::OnStopall as RequestMirrorStop(), registers a shutdown block
reason so Windows can name us on the screen that waits, and gives the engine a
bounded fifteen seconds to unwind. The wait pumps sent messages, or the engine
thread's own SendMessage into the UI thread would deadlock against it.

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
The wait added by the first cut pumped messages so the engine's end-of-mirror
SendMessage could land, but that handler opens an unconditional AfxMessageBox
when hts_is_exiting() is 2 -- the value htscore.c sets on exactly the early stop
this path produces (stat_files <= 0 and under 32 KB received). PeekMessage does
not return until the box is dismissed, the box paints behind the shutdown
screen, and Windows force-kills the process on HungAppTimeout mid-unwind.

Drop the wait, the pump and the shutdown block reason. WM_QUERYENDSESSION now
requests the same stop the Cancel button requests and returns at once; the
engine unwinds on whatever time the rest of the shutdown leaves it, which is
strictly more than master gives it. A static guard keeps the request to one per
process, since a second one sets termine_requested and turns the clean stop into
an abrupt abort.

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
@xroche xroche changed the title Stop the mirror cleanly on a Windows logoff or shutdown Ask the engine to stop when the session ends Aug 23, 2026
xroche and others added 3 commits August 23, 2026 03:26
A static "already asked" flag lasted the life of the process, so a shutdown the
user cancelled consumed the one ask: the next mirror then logged off with no
stop request at all, straight back to master's behaviour.

The engine's own soft_term_requested is the right guard. RequestMirrorStop()
escalates on it, CShellApp::init_lance() clears it before every mirror, and
nothing else writes it, so it is already scoped to exactly one mirror. Both
handlers together still ask once, and a cancelled shutdown leaves the next
mirror its own ask without depending on WM_ENDSESSION(FALSE) being delivered.

Selftest gains that sequence as a fifth row, which the per-process flag fails.

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
Both frame handlers now call SessionEndStop(bEnding), so the selftest can reach
a shutdown the user cancels and assert the mirror keeps running. The two stop
helpers return an enum naming what they found rather than a BOOL only the test
read. The smoke test pins the new count; it has no catch-all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
Both sides added a selftest block and its pin at the same place; keep both,
and keep master's CrashReportHeader() startup line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
@xroche
xroche marked this pull request as ready for review August 23, 2026 09:23
@xroche
xroche enabled auto-merge (squash) August 23, 2026 09:23
#140 added its pin at the same anchor; keep both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
@xroche
xroche merged commit 60200c0 into master Aug 23, 2026
4 checks passed
@xroche
xroche deleted the mirror-survives-logoff branch August 23, 2026 13:44
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