From 6379dce8434479f6052b9329e01f7c4fa9b4fedd Mon Sep 17 00:00:00 2001 From: fy59 Date: Thu, 6 Aug 2026 21:19:06 +0200 Subject: [PATCH] docs: document Lardon3D architecture foundations --- docs/architecture/foundation_review.md | 121 +++++++++++++++++++++++++ docs/architecture/overview.md | 112 +++++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 docs/architecture/foundation_review.md create mode 100644 docs/architecture/overview.md diff --git a/docs/architecture/foundation_review.md b/docs/architecture/foundation_review.md new file mode 100644 index 0000000..7d4eefc --- /dev/null +++ b/docs/architecture/foundation_review.md @@ -0,0 +1,121 @@ +# Revue technique des fondations + +## Périmètre et conclusion + +Cette revue couvre `task`, `task_queue`, le profil matériel, les snapshots de +ressources, le gouverneur, les réservations et leur intégration au scheduler. +Aucune fuite, course, interblocage ou violation reproductible de l'invariant +d'admission n'a été détecté par l'inspection et les tests actuels. Aucun code de +production n'a donc été modifié. + +## Invariants actuellement garantis + +- Une tâche possède une estimation copiée à sa création et exposée seulement + par copie. +- Le scheduler reçoit explicitement son gouverneur et refuse un gouverneur nul. +- Le worker obtient une réservation active avant d'appeler + `lardon3d_task_start`; celui-ci revalide la réservation avant le callback. +- Un callback lancé par la file dispose d'une copie cohérente de son contrat : + lot, RAM, GPU, CPU et slots IO/GPU. +- `WAIT` conserve la tâche en tête et endort le worker sur une condition + variable. Le mutex de file empêche une notification concurrente de se perdre + entre la décision et l'attente. +- `REJECT` termine la tâche sans appeler son callback. `REDUCE_BATCH` transmet + le contrat réduit. +- La réservation détenue par le worker est libérée après succès, échec ou + annulation. Une pause en cours conserve volontairement la réservation. +- La destruction de la file annule les tâches, réveille et rejoint le worker, + puis détruit les tâches dont elle est propriétaire. +- Les compteurs du gouverneur et la création des réservations sont protégés par + un mutex unique. Une double libération est refusée sans débiter les budgets. +- Les calculs de taille contrôlent multiplication et addition ; les compteurs + CPU et slots ne peuvent croître au-delà des budgets calculés. +- Le profil représente les capacités stables. Les snapshots sont des valeurs + datées et indépendantes ; le scheduler ne prend aucune décision de ressources + lui-même. + +## Limites connues + +- La file possède un seul worker et applique un FIFO strict. Une tâche en tête + qui reçoit `WAIT` bloque les tâches suivantes, même si certaines seraient + admissibles. +- Une libération extérieure au scheduler exige ensuite un appel à + `lardon3d_task_queue_resources_changed`. Le gouverneur ne publie pas encore + automatiquement cet événement. +- Les réservations libérées restent comme tombstones jusqu'à la destruction du + gouverneur. Cela sécurise la double libération mais fait croître la mémoire + avec le nombre historique de contrats. +- Une erreur de capture du snapshot fait échouer la tâche ; il n'existe pas + encore de distinction entre erreur transitoire de mesure et rejet durable. +- Le gouverneur et la file doivent être détruits après arrêt de leurs appelants. + Leur destruction concurrente avec une API active n'est pas prise en charge. +- Les tâches, checkpoints, files et réservations ne sont pas persistés. +- Il n'existe ni DAG, ni priorité, ni pool de workers, ni orchestration de + séquences adaptatives successives. + +## Risques à surveiller + +- Formaliser l'ordre de durée de vie : la file doit être détruite avant son + gouverneur ; une tâche cédée à la file ne doit plus être détruite directement. +- Ne jamais permettre à un composant extérieur de libérer la réservation privée + du worker. L'appel `get_active` et le démarrage sont sûrs dans le modèle de + propriété actuel, pas face à une libération concurrente volontaire. +- Éviter qu'un callback détruise ou joigne sa propre tâche, ce qui pourrait + attendre sa propre fin. +- Conserver les prédicats autour de chaque attente de condition et maintenir le + même mutex pour décision `WAIT` et mise en sommeil. +- Surveiller les identifiants et compteurs historiques sur les très longues + sessions, même si leur débordement est irréaliste avec les allocations + actuelles. +- Ne pas transformer `MemAvailable`, le swap ou la zram en promesse de mémoire + supplémentaire. Les snapshots peuvent déjà refléter une consommation réelle + en plus des réservations comptables ; une politique future doit rester + conservatrice. +- Garder la publication de résultats indépendante du contrat d'exécution : seul + un résultat validé atomiquement peut devenir visible. + +## Cohérence documentaire + +Les documents actuels correspondent au code : responsabilités séparées, +réservation préalable, pause conservant les ressources, worker unique et +notification explicite. La vue d'ensemble décrit comme futurs — et non comme +existants — les séquences complètes, la reprise persistante, les snapshots live +et le viewer Vulkan. + +## Ordre recommandé des prochains tickets + +1. Formaliser les contrats de propriété, les événements de libération et les + erreurs transitoires de snapshot. +2. Définir un format de résultat atomique avec identifiant, validation et point + de reprise. +3. Ajouter l'enchaînement borné de lots adaptatifs sous réservations successives. +4. Borner les files et introduire la contre-pression. +5. Persister tâches et checkpoints nécessaires à la reprise après crash. +6. Ajouter ensuite un DAG minimal, puis les priorités. +7. Généraliser vers des pools CPU, IO et GPU en conservant le gouverneur comme + unique arbitre. +8. Publier des snapshots validés avant d'introduire le viewer séparé. + +## Éléments à ne pas réécrire lors du passage à OpenCode + +- Les structures opaques `Task`, `TaskQueue`, `ResourceGovernor` et + `ResourceReservation`. +- La séparation profil matériel / snapshot dynamique / politique / réservation. +- Le calcul centralisé et protégé des budgets et lots. +- L'invariant « réservation active avant callback » et la copie du contrat vers + la tâche. +- L'annulation coopérative, les checkpoints de pause et la propriété ncurses du + thread principal. +- Le FIFO à condition variable comme implémentation V1 fiable ; il doit évoluer + par extension, pas être remplacé avant que les besoins DAG soient spécifiés. +- Les écritures atomiques, rollbacks ciblés et validations déjà utilisés par les + projets et imports. +- Les tests de concurrence, de double libération, d'annulation et de destruction + sûre, qui constituent la base de non-régression. + +## Validation exécutée + +- Suite normale : 10 tests réussis sur 10. +- ASan/UBSan : 10 tests réussis sur 10, aucun diagnostic. +- TSan : 10 tests réussis sur 10, aucune course signalée. +- `git diff --check` : réussi avant la rédaction du présent rapport. diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md new file mode 100644 index 0000000..857bd55 --- /dev/null +++ b/docs/architecture/overview.md @@ -0,0 +1,112 @@ +# Vue d'ensemble de l'architecture Lardon3D + +## Finalité et flux global + +Lardon3D est une application Linux de reconstruction 3D pilotée par une TUI +ncursesw. Le terminal reste le centre de contrôle : il gère les projets, lance +les opérations, présente leur progression et permet leur annulation. Le futur +viewer Vulkan sera un processus ou composant graphique séparé, affiché sur le +workspace 8 ; il ne remplacera pas la TUI et ne devra jamais la bloquer. + +```text +TUI / Projet + ↓ +Task + ↓ +Estimate + ↓ +Governor + ↓ +Reservation + ↓ +Scheduler + ↓ +Worker + ↓ +Résultat atomique + ↓ +Viewer live +``` + +## Composants actuels + +Les projets persistants regroupent leur configuration, les images originales, +le manifeste, les résultats de reconstruction, les exports et les journaux. +Leur création est protégée contre l'écrasement et les écritures structurantes +utilisent des remplacements atomiques. + +L'import d'images s'exécute de manière asynchrone et annulable, sans appel +ncurses depuis son worker. Il copie individuellement les fichiers admissibles +et maintient un manifeste cohérent. Le catalogue charge et valide ce manifeste +en mémoire. La vue d'images en dérive des indices triés et filtrés sans modifier +le catalogue, le manifeste ou les images. + +Le moteur de tâches fournit les états, la progression, la pause, l'annulation +coopérative et les callbacks. La file actuelle possède un worker unique et +respecte l'ordre FIFO. Chaque tâche porte une estimation immuable de ses coûts +RAM, GPU, CPU et IO ainsi que des bornes de lot. + +Le profil matériel décrit les capacités stables détectées sur la machine. Les +snapshots décrivent les ressources disponibles à un instant donné. Le Resource +Governor combine profil, snapshot, marges de sécurité et réservations actives. +Il décide si une demande doit démarrer, attendre, réduire son lot ou être +refusée, puis matérialise toute admission par une réservation opaque. + +Le scheduler ne décide jamais des ressources. Il demande une réservation au +gouverneur juste avant l'exécution et transmet au callback une copie du contrat +accordé. L'invariant est strict : aucun callback de tâche n'est lancé sans +réservation active validée. Après succès, échec ou annulation, cette réservation +est libérée exactement une fois. Une tâche déjà en pause conserve son contrat +dans cette première version. + +## Résultats et publication live + +Les traitements futurs fonctionneront par séquences adaptatives : lire un lot +borné, calculer, écrire un résultat atomique, libérer la mémoire, puis traiter +le lot suivant. La stabilité du système hôte et la réactivité de la TUI ont +priorité sur le débit maximal. + +Le viewer live ne devra observer que des snapshots de résultats complètement +validés et publiés atomiquement. Il ne lira jamais un fichier intermédiaire et +ne partagera pas directement les buffers de travail d'un worker. Une +interruption doit laisser le dernier snapshot validé exploitable et permettre +la reprise à une frontière de séquence connue. + +## Principes non négociables + +- Aucune tâche lourde monolithique ni chargement complet d'un projet en RAM. +- Traitement par séquences adaptatives et libération entre les lots. +- Budgets RAM, GPU, CPU et IO explicitement bornés et réservés. +- Files de travail et buffers intermédiaires bornés. +- La zram est un filet de sécurité, jamais une extension du budget normal. +- La RAM partagée des iGPU est comptabilisée dans le budget système. +- Écritures atomiques, rollback ciblé et absence de résultat partiellement + publié. +- Reprise après interruption depuis le dernier état validé. +- Viewer live séparé, non bloquant et lecteur de snapshots validés seulement. +- Le système hôte, la TUI et les données utilisateur restent prioritaires sur + le débit de reconstruction. + +## Limites actuelles + +La file ne possède encore ni DAG, ni priorités, ni pool de workers CPU/IO/GPU. +Les tâches et leur progression ne sont pas persistées après un arrêt. Les +séquences adaptatives sont préparées par les contrats de lot mais leur +enchaînement complet n'est pas encore orchestré. Le viewer Vulkan et la +publication live restent à implémenter. + +## Ordre recommandé des prochains tickets + +1. Définir les résultats atomiques, leurs métadonnées de validation et leurs + points de reprise. +2. Introduire l'exécution d'une tâche en séquences de lots adaptatifs, toujours + sous réservations successives. +3. Borner explicitement les files et définir la contre-pression entre étapes. +4. Persister les tâches, checkpoints et états nécessaires à la reprise après + crash. +5. Ajouter un DAG minimal et seulement ensuite les priorités. +6. Introduire des pools CPU, IO et GPU sans déplacer l'arbitrage hors du + gouverneur. +7. Publier des snapshots live validés et versionnés. +8. Ajouter le viewer Vulkan séparé sur le workspace 8 comme consommateur en + lecture seule de ces snapshots.