Skip to content

Phase 0 / Hotfix / Scheduler livelock investigation#15

Merged
guysenpai merged 33 commits into
mainfrom
phase-0/hotfix/scheduler-livelock-investigation
May 24, 2026
Merged

Phase 0 / Hotfix / Scheduler livelock investigation#15
guysenpai merged 33 commits into
mainfrom
phase-0/hotfix/scheduler-livelock-investigation

Conversation

@guysenpai
Copy link
Copy Markdown
Contributor

Brief

Implémente le hotfix M0.2.1 conformément à
briefs/M0.2.1-scheduler-livelock.md.

Cause root

Race wave-lifecycle dans le scheduler ECS work-stealing (src/core/jobs/scheduler.zig).
Sous préemption entre la mise à jour de last_generation par un worker et
sa lecture de chunk_count à l'entrée de pushShare, un worker pouvait
contribuer 2 fetchSub à la wave G+1 au lieu d'1, produisant un underflow
de pending_count (u64::MAX) et un spin infini de publishWaveAndWait.
Probabilité observée : ~30 % par pre-push complet, ~2.7 % par signal de
stress synthétique.

Fix appliqué

Packing atomique (generation: u32, chunk_count: u32) → gen_and_n: std.atomic.Value(u64). Un load .acquire 64-bit fournit un snapshot
atomique de la paire par construction, fermant prouvablement la fenêtre
de race. Coût perf nul vs deux loads séparées (un load 64-bit aligné
coûte autant qu'un load 32-bit sur Apple Silicon et x86_64).

Belt-and-suspenders :

  • Assertion prev > 0 au siège de fetchSub (scheduler.zig:333)
  • Assertion défensive dans publishWaveAndWait (Debug + ReleaseSafe)
  • Comptime guard de séparation cache line gen_and_npending_count

Validation

Tier Description Résultat
Validation immédiate E5 50× test-stress 50/50 PASS
E2ter assertion 50× test-stress 50/50 PASS
E7 Tier 1 200× test-stress avec caffeinate -i 200/200 PASS, 220 s
E7 Tier 2 30× pre-push complet avec caffeinate -i 30/30 PASS, 637 s

Cumul : 330+ invocations sans hang vs ~2.7 % de reproduction synthétique
et ~30 % pre-push pré-fix observée pendant M0.2.

Non-régression bench (thermal-aware MBP M-series)

Mode Médiane des médianes Baseline Gate Verdict
S1 61.04 µs 60.17 µs (M0.2) 62 µs +1.45 %, dans bande ± 5 %
C0.1 3.74 ms 14.2 ms (M0.1, à investiguer) 16.6 ms ~4.4× headroom

Protocole : 30 min idle initial, ≥ 15 min idle entre runs, 3 runs/session,
powermetrics --samplers thermal,cpu_power -i 100 Nominal sur 100 % des
samples (265 samples cumulés sur les 6 runs).

Rapports archivés :

Points à signaler en review

  • Discipline anti-hallucination E1 : helpUntilDone (mentionné en
    hypothèse du brief) confirmé absent du code par lecture directe ;
    ranking H1..H5 originel ajusté en conséquence avant E2.
  • 6 retours Claude.ai actés au cours du milestone (B-E2-1, B-E2-2,
    B-E2bis-1, B-E3-1, E4 plan A/Option fix, review E5/E6/E7) — conformes
    au protocole hotfix de diagnostic, non-symptomatiques d'un re-scope.
  • Rejet Option 1 (snapshot séquentiel) en E4 : ne fermait pas la
    race, juste la déplaçait à 2 lignes de distance. Option 2 (atomic
    packé) retenue pour fermeture prouvable par construction.
  • Cible de reproduction synthétique révisée 90 % → 50 % en E2bis :
    plafond ~2.7 % atteint après ajouts cumulés (sursubscription CPU,
    forks de processes, fs I/O). Plan E7 cumulatif Tier 1 (200×
    synthétique) + Tier 2 (30× pre-push) compense, gates statistiques
    validés.
  • caffeinate -i requis pour toute boucle longue thermal-aware
    sur MBP — 3 faux positifs de hang observés lors de la première
    tentative E7 Tier 1 sans caffeinate (sleep macOS). À acter dans
    CLAUDE.md / engine-phase-0-criteria.md à la review.

Dettes résiduelles documentées

  • D-M0.2.1-publishWaveAndWait-spin : publishWaveAndWait
    spin-yield sur pending_count au lieu d'utiliser sleep/wake comme
    acté par le squash M0.1 (D-S1-3). À investiguer dans un milestone
    ECS Phase 0.1+ dédié.
  • D-M0.2.1-c01-baseline-investigation : C0.1 mesuré à 3.74 ms
    vs baseline M0.1 documentée à 14.2 ms (≈ 3.8× delta). À éclaircir :
    changement de paramètres du bench, opportunité de re-baseline, ou
    bug de mesure. Milestone Phase 0.1+ dédié.
  • D-M0.2.1-cleanup-double-load (mineur) : double pending_count. load(.acquire) dans la belt-and-suspenders de publishWaveAndWait.
    Correct mais redondant. À nettoyer dans un commit cleanup futur.

Checklist validation pré-PR

  • Tous les livrables du Scope présents
  • Aucune dérive vers Out-of-scope
  • Tous les tests des Critères d'acceptation passent Debug + ReleaseSafe
  • Benchmarks atteignent leurs cibles chiffrées sous protocole thermal-aware
  • Comportement observable démontrable
  • zig build, zig build test (Debug + ReleaseSafe), zig fmt --check,
    zig build lint verts
  • Section « Notes de fin » du brief remplie
  • Status: CLOSED, Date de fermeture renseignée
  • Commit final docs(brief): close M0.2.1

Closes M0.2.1.

guysenpai added 30 commits May 23, 2026 13:23
guysenpai added 3 commits May 24, 2026 08:08
6 entrées intégrées suite à la review Claude.ai de la PR #15
(M0.2.1 — Scheduler livelock investigation) :

- Workflow thermal-aware bench MBP M-series : `caffeinate -i`
  obligatoire pour boucles longues.
- Workflow milestone hotfix à cause root inconnue : décomposition
  E1..En et tiers cumulatifs de validation.
- Anti-hallucination E1 : vérifier symboles nommés dans brief par
  lecture directe code.
- Pattern atomic packing pour racing snapshots `(a, b)`.
- Pattern comptime layout guards cache-line.
- Garde-fou interprétation baselines bench héritées (vérifier
  conformité au protocole opposable).
@guysenpai guysenpai merged commit df67e1c into main May 24, 2026
6 checks passed
@guysenpai guysenpai deleted the phase-0/hotfix/scheduler-livelock-investigation branch May 24, 2026 08:20
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