emrg: fix Restart Manager scan dead-loop (RmGetList m=0, DeleteFile code 5) - #829
Merged
Conversation
…ode 5) Rant 2026-08-17T21:04:32: the C# RM.Who() loop passed m=0 (buffer capacity) forever — RmGetList's pdwProcCount is in/out, so every call returned ERROR_MORE_DATA(234) -> infinite loop -> zero owners killed -> installer still hit DeleteFile code 5 on Windows (websockets pyd locked by an external browser-harness daemon). - RmGetList: preallocate 50 entries (m=50), on 234 resize to n and retry, hard-capped at MAX_ATTEMPTS=3 so an abnormal API can never dead-loop; result takes Math.Min(n, m) capped by buffer length - RmRegisterResources return value now checked (reg_fail counter) - New rm-diag line (files/owners/elapsed_ms/reg_fail) + Python _print_rm_diag logging in stop_lock_owners() and _verify_windows() so a scan can never be silently idle - Tests: parametrized pure-Python model of the RmGetList loop (7 scenarios incl. cap termination) + C# template static assertions + diag parsing/logging tests (893 -> 904)
argszero
commented
Aug 17, 2026
argszero
left a comment
Owner
Author
There was a problem hiding this comment.
✅ LGTM — cycle (1/3)
Verified:
- C# RmGetList loop fixed: m=50 preallocated capacity, resize to n on 234, hard-capped at MAX_ATTEMPTS=3 — no unbounded do/while(rc==234) remains
- RmRegisterResources return value checked (reg_fail counter surfaced via rm-diag line)
- _print_rm_diag() logs files/owners/elapsed/reg_fail in both stop_lock_owners() and _verify_windows() — no silent scans
- Parametrized pure-Python model covers 7 RM scenarios incl. cap termination (never dead-loops)
- pytest 903 passed + 1 skipped (904); import + CLI green; CI test + test-windows PASS (run 32033707677)
… 21:06:05/21:06:31) Companion rants to the RmGetList dead-loop fix (21:04:32), explicitly to be merged together. Both target emrg/_stop_all.py: 21:06:05 - verify must be INDEPENDENT of Restart Manager + pre-verify the install dir writability, so a broken detector can never silently pass verify: - new check_install_writable(): probes install\ files with an exclusive open (CreateFileW dwShareMode=0 = FileShare.None — exactly the semantic the installer's DeleteFile needs); returns the locked-file list; pure _check_locked_files(try_open=...) extractable + testable on POSIX - verify() now includes a lock-probe category: locked files -> named residual -> exit 1 (installer aborts with a clear list, no code-5 dialog) - kill retry: RM owners killed but locks linger -> re-probe + retry up to 2x (Try-again semantics); still locked -> verify failure list 21:06:31 - stop_all.py log standard so every run is diagnosable from the log: - header: build stamp / python version / platform / pid - per-step '[N/T] name -> done (Xs)' via _step_plan() (steps stay ordered: clients first, daemon last, bundled-git+RM Windows-only) - every step wrapped: exceptions print 'ERROR <step>: <reason> (<class>)' and the run continues to the final exit code - verify per-category summary line (GUI/daemon/cmdline-scan/RM re-scan/ lock-probe/bundled-git) + 'exit code 0 (clean) / 1 (N residual)' + total - all lines keep the 'emrg stop: ' prefix Tests (904 -> 914): probe traversal/collection (injected opener), check_install_writable branches (posix noop / no install dir / locked / probe error), verify categories incl. lock-probe + summary, kill-retry loop (3 lock_owners calls), step-exception logging, clean/residual exit lines.
argszero
commented
Aug 17, 2026
argszero
left a comment
Owner
Author
There was a problem hiding this comment.
✅ LGTM — cycle (2/3) — re-verified on updated head 70876f5 (companion rants 21:06:05 + 21:06:31 folded in, per host instruction to merge together)
Companion changes verified:
- check_install_writable(): independent of Restart Manager — probes install\ with CreateFileW dwShareMode=0 (exact installer-DeleteFile semantic); _check_locked_files(try_open=...) pure + POSIX-testable
- verify() gains lock-probe category → locked files become named residuals → exit 1 (no silent pass)
- kill retry: re-probe + up to 2 retries (Try-again semantics); still locked → verify failure list
- stop_all() log standard: header (stamp/python/platform/pid), [N/T] steps via _step_plan() (order preserved: clients → daemon → bundled-git → RM), step exceptions print ERROR and continue, per-category verify summary, exit-code + total elapsed
- pytest 913 passed + 1 skipped (914); import + CLI green; CI test + test-windows PASS (run 32034581689)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the Windows installer DeleteFile-code-5 root cause identified in rant 2026-08-17T21:04:32 (v0.2.44 install still failed).
Root cause
The C#
RM.Who()inemrg/_stop_all.pypassedm=0(buffer capacity) forever:RmGetList'spdwProcCountparameter is IN/OUT — input = buffer capacity,output = count written. With capacity 0 every call returned ERROR_MORE_DATA(234),
so the loop never terminated → zero owners killed → the installer still hit
DeleteFile code 5 (websockets pyd locked by an external browser-harness daemon).
Host evidence: two 60s RM scan timeouts ≈ 120s gap between install start and
stop_all.log.
Changes
m=50), on 234 resize tonand retry, hard-capped at
MAX_ATTEMPTS=3so an abnormal API can neverdead-loop; result takes
Math.Min(n, m)capped by buffer length.reg_failcounter — a failed batchis now visible, not silent).
rm-diagline(files / owners / elapsed_ms / reg_fail); Python
_print_rm_diag()logs thesummary in both
stop_lock_owners()and_verify_windows().(7 scenarios incl. cap termination — never dead-loops), C# template static
assertions, diag parsing + logging tests.
Acceptance (self-verifiable)
killed file-lock owner: PID ... python.exe | ...browser_harness.daemonrm-scan ...line in stop_all.log, and the installer no longerreports DeleteFile code 5.