feat: skip resource-blocked tasks in scheduler

This commit is contained in:
fy59 2026-08-06 22:16:45 +02:00
parent 983b435d3c
commit cc9c29cb74
27 changed files with 496 additions and 112 deletions

5
.opencode/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
node_modules
package.json
package-lock.json
bun.lock
work/

View file

@ -9,6 +9,7 @@ permission:
task: deny task: deny
--- ---
Analyse uniquement. Vérifie les frontières TUI, métier, scheduler et gouverneur, Interviens uniquement pour une API publique, le scheduler, le gouverneur, le
DAG, la persistance ou une concurrence importante. Vérifie les frontières,
les propriétés, durées de vie et invariants documentés. Ne modifie aucun fichier les propriétés, durées de vie et invariants documentés. Ne modifie aucun fichier
et rends au parent un rapport concis, avec les défauts bloquants en premier. et rends au parent seulement conclusions, risques, fichiers et décisions.

View file

@ -0,0 +1,16 @@
---
description: Reprend les tickets complexes interrompus sans refaire l'analyse
mode: subagent
model: opencode/nemotron-3-ultra-free
temperature: 0.1
permission:
edit:
"*": allow
"scan3d/**": deny
task: deny
---
Lis d'abord `.opencode/work/current_ticket.md`, le diff et les seuls fichiers
concernés. Reprends le working tree existant à la prochaine action sûre sans
refaire l'analyse si le handoff suffit. Implémente minimalement, sans sous-agent,
commit, push ni modèle payant. Mets à jour le handoff avec un résumé concis.

View file

@ -0,0 +1,17 @@
---
description: Traite les petits tickets et corrections locales à faible coût
mode: subagent
model: opencode/mimo-v2.5-free
temperature: 0.1
permission:
edit:
"*": allow
"scan3d/**": deny
task: deny
---
Traite seulement les petits changements locaux, tests simples, nettoyage ou
documentation demandée. Lis le handoff et les fichiers ciblés, sans sous-agent.
Si le ticket touche une fondation sensible, une API complexe ou exige un gros
refactoring, arrête-toi et signale qu'il faut Nemotron, DeepSeek ou Codex. Ne
réécris jamais une fondation pour contourner cette limite.

View file

@ -1,26 +1,18 @@
--- ---
description: Agent principal C17 qui orchestre et implémente les tickets Lardon3D description: Implémente seul les tickets complexes Lardon3D
mode: primary mode: subagent
model: opencode/deepseek-v4-flash-free model: opencode/deepseek-v4-flash-free
temperature: 0.1 temperature: 0.1
permission: permission:
edit: edit:
"*": allow "*": allow
"scan3d/**": deny "scan3d/**": deny
task: task: deny
"*": deny
"lardon-architect": allow
"lardon-explore": allow
"lardon-review": allow
"lardon-concurrency": allow
"lardon-tests": allow
"lardon-docs": allow
--- ---
Orchestre le ticket avec le minimum de sous-agents pertinents. Un seul agent Implémente uniquement le plan concis transmis par l'orchestrateur. Lis le
modifie les sources à un instant donné : toi. Lis les instructions projet déjà handoff et seulement les fichiers et documents indiqués. Ne refais pas
chargées, préserve les changements existants et fournis aux sous-agents un l'exploration, ne lance aucun sous-agent et préserve le working tree existant.
périmètre précis. Leurs rapports doivent rester concis. N'imbrique jamais de Tu es l'unique auteur des sources pendant ta phase. Mets à jour le handoff avec
sous-agent. Si DeepSeek retourne une saturation 503, demande à l'utilisateur de les changements et erreurs utiles, sans longs logs. Aucun commit, push ou
relancer avec `opencode/nemotron-3-ultra-free`; ne sélectionne jamais un modèle modèle payant.
payant.

View file

@ -14,5 +14,5 @@ permission:
Audite pthread, mutex, conditions, transitions d'état, réveils perdus, Audite pthread, mutex, conditions, transitions d'état, réveils perdus,
deadlocks, doubles libérations et data races. Cet audit est obligatoire pour deadlocks, doubles libérations et data races. Cet audit est obligatoire pour
task, task_queue et resource_governor. Ne modifie rien et rapporte les scénarios task, task_queue et resource_governor, mais inutile hors code concurrent. Ne
précis au parent. modifie rien et rapporte uniquement les scénarios précis et leur gravité.

View file

@ -13,5 +13,6 @@ permission:
task: deny task: deny
--- ---
Modifie uniquement README.md, AGENTS.md et docs/. Synthétise les documents Modifie uniquement README.md, AGENTS.md et docs/ lorsqu'un ticket le demande
chargés sans les dupliquer et ne change jamais le code ou la configuration. explicitement. Charge seulement les documents pertinents, synthétise sans les
dupliquer et ne change jamais le code ou la configuration.

View file

@ -13,5 +13,6 @@ permission:
--- ---
Travaille en lecture seule. Localise exactement les fichiers, symboles, Travaille en lecture seule. Localise exactement les fichiers, symboles,
appelants et dépendances demandés. Ne propose pas de réécriture et retourne des appelants, dépendances et documents strictement utiles. Ne lis pas toute
chemins et conclusions synthétiques au parent. l'architecture par défaut, ne propose pas de réécriture et retourne uniquement
les chemins, conclusions et risques synthétiques.

View file

@ -0,0 +1,32 @@
---
description: Orchestre les tickets avec un contexte minimal et sans écrire les sources
mode: primary
model: opencode/north-mini-code-free
temperature: 0.1
permission:
edit:
"*": deny
".opencode/work/current_ticket.md": allow
bash:
"*": deny
"git status*": allow
"git diff*": allow
"codex --help*": allow
task:
"*": deny
"lardon-build": allow
"lardon-build-backup": allow
"lardon-build-light": allow
"lardon-architect": allow
"lardon-explore": allow
"lardon-review": allow
"lardon-concurrency": allow
"lardon-tests": allow
"lardon-docs": allow
---
Orchestre sans modifier les sources. Charge AGENTS.md et l'overview déjà
injectés, puis utilise explore pour cibler le contexte. N'appelle que les agents
pertinents, sans sous-agent imbriqué. Transmets au seul implémenteur un résumé
court : objectif, contraintes, fichiers, API, invariants et tests. Tiens le
handoff concis à jour après chaque phase. Aucun modèle payant, commit ou push.

View file

@ -14,4 +14,4 @@ permission:
Relis le diff sans le modifier. Vérifie C17, erreurs, nettoyage, overflows, Relis le diff sans le modifier. Vérifie C17, erreurs, nettoyage, overflows,
contrats d'API, ownership et conformité à AGENTS.md. Distingue bloquants, contrats d'API, ownership et conformité à AGENTS.md. Distingue bloquants,
importants et améliorations, puis rends un rapport court. importants et limites. Ne répète pas les tests réussis et rends un rapport court.

View file

@ -17,6 +17,6 @@ permission:
--- ---
Exécute les commandes demandées et rapporte leurs sorties réelles. Utilise le Exécute les commandes demandées et rapporte leurs sorties réelles. Utilise le
build normal, ASan/UBSan et TSan selon le risque. Ne modifie jamais le code sauf build normal, ASan/UBSan et TSan selon le risque. Ne modifie jamais le code : si
instruction explicite du prompt principal, auquel cas refuse et renvoie la une correction est nécessaire, renvoie-la au seul agent implémenteur. Pour les
correction au seul agent implémenteur. succès, rapporte seulement commande, statut et synthèse.

View file

@ -0,0 +1,11 @@
---
description: Préparer une reprise courte dans Codex CLI
agent: lardon-orchestrator
subtask: false
---
Mets le handoff à jour depuis le statut et le diff, sans modifier les sources.
Vérifie d'abord `codex --help`. Produis ensuite un prompt court copiable qui
demande à Codex de lire AGENTS.md, le handoff, le diff et seulement les fichiers
cités, puis de reprendre à la prochaine action sûre. Ne lance pas Codex, n'invente
aucune option, ne demande aucune clé et n'inclus aucun secret.

View file

@ -1,10 +1,10 @@
--- ---
description: Produire un compte rendu de transmission sans modifier le dépôt description: Produire un compte rendu de transmission sans modifier le dépôt
agent: lardon-build agent: lardon-orchestrator
subtask: false subtask: false
--- ---
Sans modifier le code ni la documentation, produis un compte rendu autonome Sans modifier le code ni la documentation suivie, lis le handoff, le statut Git
pour une autre session ou un autre modèle : objectif, architecture pertinente, et le diff, puis produis un compte rendu autonome : objectif, contraintes,
état Git, travail réalisé, validations réellement exécutées, défauts et limites, architecture strictement pertinente, travail réalisé, validations réelles,
fichiers concernés et prochaine action sûre. défauts, fichiers et prochaine action sûre. N'inclus aucun long log ni secret.

View file

@ -0,0 +1,14 @@
---
description: Planifier en lecture seule avec un contexte minimal
agent: lardon-orchestrator
subtask: false
---
Planifie ce ticket sans modifier les sources : $ARGUMENTS
Lis AGENTS.md et l'overview déjà chargés. Demande à lardon-explore uniquement
les modules concernés. Liste les fichiers et documents à charger, propose un plan
court et indique les agents réellement nécessaires. Écris dans
`.opencode/work/current_ticket.md` : titre, objectif, contraintes, fichiers,
documents utiles, plan, travail terminé/restant, fichiers modifiés, commandes,
tests passés/restants, décisions, erreurs, modèle et prochaine action sûre.

View file

@ -0,0 +1,10 @@
---
description: Reprise rapide du handoff avec Nemotron
agent: lardon-orchestrator
subtask: false
---
Reprends le ticket avec lardon-build-backup. Lis uniquement le handoff, le diff
et les fichiers qu'ils citent. Ne refais ni exploration ni architecture si les
informations suffisent. Appelle concurrency uniquement si nécessaire, puis mets
le handoff à jour avec la prochaine action sûre.

View file

@ -0,0 +1,10 @@
---
description: Reprendre dans OpenCode le working tree laissé par Codex
agent: lardon-orchestrator
subtask: false
---
Lis le handoff et inspecte le diff laissé par Codex. Charge uniquement les
fichiers modifiés, sans refaire l'architecture. Lance lardon-review, puis
lardon-concurrency seulement si pertinent, puis lardon-tests. Mets le handoff à
jour avec les résultats et la prochaine action. Ne modifie pas les sources.

View file

@ -0,0 +1,10 @@
---
description: Reprise limitée d'un petit ticket avec MiMo
agent: lardon-orchestrator
subtask: false
---
Lis le handoff, le diff et les fichiers cités, puis confie uniquement un petit
travail local à lardon-build-light. Si le reste touche une fondation, une API ou
un refactoring sensible, ne modifie rien : conserve le handoff et recommande
DeepSeek, Nemotron ou Codex. Mets à jour la prochaine action sûre.

View file

@ -1,9 +1,10 @@
--- ---
description: Lancer une revue indépendante sans modification description: Lancer une revue indépendante sans modification
agent: lardon-build agent: lardon-orchestrator
subtask: false subtask: false
--- ---
Sans modifier aucun fichier, lance lardon-review et lardon-concurrency comme Sans modifier aucun fichier, lance lardon-review sur le diff courant. Lance
sous-tâches indépendantes sur le diff courant. Agrège leurs rapports en lardon-concurrency en sous-tâche indépendante uniquement si le diff touche
distinguant défauts bloquants, risques et limites. Ne lance aucun autre agent. pthread, task, task_queue ou resource_governor. Agrège seulement les défauts,
risques et limites. Ne lance aucun autre agent.

View file

@ -0,0 +1,10 @@
---
description: Sauvegarder l'état courant du ticket sans modifier les sources
agent: lardon-orchestrator
subtask: false
---
Sans sous-agent ni modification des sources, inspecte `git status`, le diff et
le handoff existant. Mets `.opencode/work/current_ticket.md` à jour avec les
rubriques prescrites. Résume les commandes et erreurs ; ne copie ni sources,
documentation, longs logs, secrets ou données utilisateur.

View file

@ -0,0 +1,14 @@
---
description: Traiter un petit ticket avec MiMo et des validations ciblées
agent: lardon-orchestrator
subtask: false
---
Traite ce petit ticket : $ARGUMENTS
Utilise lardon-explore pour cibler les fichiers, puis un seul lardon-build-light
pour modifier. N'appelle architect, concurrency ou docs que si leur besoin est
avéré ; si le ticket est sensible, arrête et recommande le workflow normal.
Appelle ensuite lardon-tests pour compilation, tests ciblés et
`git diff --check`, puis une revue seulement si le diff le justifie. Maintiens
le handoff concis. Aucun commit, push ou fallback payant.

View file

@ -0,0 +1,11 @@
---
description: Reprendre un ticket gratuit avec Nemotron après indisponibilité DeepSeek
agent: lardon-orchestrator
subtask: false
---
Lis d'abord `.opencode/work/current_ticket.md` et le diff. Si le handoff suffit,
ne refais pas l'exploration. Transmets seulement l'objectif, les fichiers, les
invariants, le travail restant et la prochaine action à lardon-build-backup.
Après reprise, appelle lardon-tests puis lardon-review ; concurrency uniquement
si pertinent. Mets à jour le handoff après chaque phase. Aucun modèle payant.

View file

@ -1,14 +1,19 @@
--- ---
description: Traiter un ticket Lardon3D avec exploration, architecture, tests et revue description: Traiter un ticket gratuit avec contexte progressif et handoff
agent: lardon-build agent: lardon-orchestrator
subtask: false subtask: false
--- ---
Traite ce ticket : $ARGUMENTS Traite ce ticket : $ARGUMENTS
Lis les instructions déjà chargées, inspecte Git et les fichiers concernés. 1. Initialise `.opencode/work/current_ticket.md` avec les rubriques prescrites.
Utilise lardon-explore puis lardon-architect seulement si pertinents. Après leur 2. Demande à lardon-explore les seuls fichiers et documents utiles.
rapport, implémente seul et minimalement. Délègue la validation à lardon-tests, 3. Charge uniquement ce contexte. Appelle lardon-architect seulement pour une
puis la revue finale à lardon-review et obligatoirement à lardon-concurrency si abstraction importante, puis transmets un résumé concis à lardon-build.
task, task_queue, resource_governor ou pthread sont touchés. Aucun sous-agent 4. Après l'implémentation, appelle lardon-tests une fois, puis lardon-review une
imbriqué, commit, push ou ajout de scan3d/. fois. Ajoute lardon-concurrency uniquement pour du code concurrent.
5. Mets le handoff à jour après chaque phase avec conclusions, commandes,
erreurs et prochaine action, sans documentation, sources ou logs complets.
Un seul agent écrit les sources. Aucun sous-agent imbriqué, commit, push,
fallback payant ou modification de scan3d/.

View file

@ -1,15 +1,11 @@
{ {
"$schema": "https://opencode.ai/config.json", "$schema": "https://opencode.ai/config.json",
"default_agent": "lardon-build", "default_agent": "lardon-orchestrator",
"model": "opencode/deepseek-v4-flash-free", "model": "opencode/north-mini-code-free",
"subagent_depth": 1, "subagent_depth": 1,
"instructions": [ "instructions": [
"AGENTS.md", "AGENTS.md",
"docs/architecture/overview.md", "docs/architecture/overview.md"
"docs/architecture/resource_governor.md",
"docs/architecture/scheduler_resource_integration.md",
"docs/architecture/foundation_review.md",
"docs/opencode_handoff.md"
], ],
"permission": { "permission": {
"*": "ask", "*": "ask",
@ -20,7 +16,8 @@
"external_directory": "deny", "external_directory": "deny",
"edit": { "edit": {
"*": "allow", "*": "allow",
"scan3d/**": "deny" "scan3d/**": "deny",
"scan3d/tri_photos.py": "deny"
}, },
"bash": { "bash": {
"*": "ask", "*": "ask",
@ -40,6 +37,7 @@
"ignore": [ "ignore": [
".git/**", ".git/**",
"build*/**", "build*/**",
".opencode/work/**",
"scan3d/**" "scan3d/**"
] ]
} }

View file

@ -61,6 +61,63 @@ indisponibles, interrompre le travail plutôt que choisir un modèle payant.
Aucun fournisseur ni modèle local n'a été détecté dans la configuration Aucun fournisseur ni modèle local n'a été détecté dans la configuration
OpenCode actuelle. OpenCode actuelle.
## Optimisation du quota
L'agent par défaut est `lardon-orchestrator` sur North Mini Code Free. Il charge
seulement `AGENTS.md` et l'overview, puis demande à North Mini Code une
exploration ciblée. DeepSeek est réservé à l'implémentation complexe ; il ne sert
ni à l'orchestration, ni aux tests, ni à la documentation. Nemotron traite les
reprises et analyses d'architecture ou de concurrence, MiMo les petits tickets,
Laguna la revue et Ling la documentation.
Les commandes `lardon-plan`, `lardon-small`, `lardon-ticket` et
`lardon-ticket-backup` chargent progressivement le contexte. Elles ne lisent pas
automatiquement toute l'architecture et transmettent uniquement objectif,
contraintes, fichiers, API, invariants, risques et tests. Les rapports de succès
restent courts. Le handoff ignoré `.opencode/work/current_ticket.md` permet une
reprise sans rejouer toute l'analyse.
## Chaîne de secours
La voie normale est DeepSeek V4 Flash Free pour l'implémentation. En cas de 503,
saturation, quota épuisé ou interruption, sauvegarder le handoff et reprendre
avec Nemotron 3 Ultra Free. Si Nemotron est indisponible, MiMo V2.5 Free est
limité aux petits changements ; il doit refuser une fondation sensible.
GPT-5.6 Sol n'est pas actif dans OpenCode, car l'identifiant visible passe par
Zen et annonce un tarif. Pour utiliser le quota ChatGPT/Codex, la voie normale
reste Codex CLI via `lardon-codex-handoff`, puis
`lardon-resume-from-codex`. Aucun modèle payant n'est un fallback.
## GPT-5.6 Sol dans OpenCode
Vérification locale du 6 août 2026 avec OpenCode 1.18.13 :
- identifiant exact : `opencode/gpt-5.6-sol` ;
- fournisseur : `opencode`, affiché comme OpenCode Zen ;
- endpoint déclaré : `https://opencode.ai/zen/v1` ;
- authentification active : credential Zen de type `api` ;
- clé OpenAI API séparée : non requise par cette route Zen, mais le credential
Zen reste nécessaire ;
- OAuth ChatGPT/Codex : non détecté ;
- plugin Codex OAuth : non installé ;
- OpenCode Zen utilisé : oui ;
- tarif affiché : entrée 5, sortie 30, cache lecture 0,5 et écriture 6,25 dans
les unités tarifaires retournées par OpenCode ; tarifs supérieurs au-delà du
palier de contexte indiqué ;
- facturation distincte du quota Codex : route Zen tarifée, donc à considérer
distincte ;
- quota Codex partagé : non confirmé et aucune preuve locale ne l'indique ;
- test minimal : non exécuté, car la route identifiée est payante ;
- classement : **Q3 — GPT-5.6 Sol fourni par OpenCode Zen** ;
- politique : aucun agent, commande active ou fallback Sol dans OpenCode.
La preuve locale combine `opencode models --refresh --verbose`, qui expose le
fournisseur, l'endpoint et le coût, et `opencode providers list`, qui expose un
seul credential OpenCode Zen. La configuration globale ne déclare aucun
fournisseur OpenAI ni plugin d'authentification Codex. Aucun secret n'a été lu
ou reproduit dans ce document.
## Actualisation ## Actualisation
Les offres changent. Réexécuter : Les offres changent. Réexécuter :

76
docs/opencode_workflow.md Normal file
View file

@ -0,0 +1,76 @@
# Workflow OpenCode et Codex
Ce workflow minimise le contexte tout en conservant un handoff fiable. Le
fichier `.opencode/work/current_ticket.md` est local et ignoré par Git. Il ne
contient que l'objectif, les contraintes, fichiers et documents utiles, plan,
travail terminé et restant, fichiers modifiés, commandes, tests, décisions,
erreurs, modèle actif et prochaine action sûre.
## A. Ticket gratuit normal
Lancer `/lardon-plan`, puis `/lardon-ticket`. North orchestre et explore via des
agents distincts, puis DeepSeek implémente. Architect et concurrency ne sont appelés que si le
périmètre l'exige. Tests et revue interviennent une seule fois après le code.
## B. DeepSeek retourne 503
Lancer `/lardon-save-handoff`, puis `/lardon-resume-backup`. Nemotron lit le
handoff et le diff, reprend la prochaine action et ne refait pas l'analyse si
les informations suffisent.
## C. DeepSeek et Nemotron sont indisponibles
Lancer `/lardon-resume-light` uniquement si le travail restant est petit et
local. Pour une fondation sensible, conserver le handoff et attendre DeepSeek
ou Codex ; ne pas forcer MiMo à réécrire l'architecture.
## D. Petit ticket avec MiMo
Lancer `/lardon-small`. L'exploration reste ciblée, MiMo est le seul auteur,
puis les validations ciblées et `git diff --check` sont exécutées. Basculer vers
le workflow normal si la complexité dépasse une correction locale.
## E. Quota Codex renouvelé
GPT-5.6 Sol visible dans OpenCode est une offre Zen payante, pas une voie Codex
confirmée. Lancer `/lardon-codex-handoff`, copier le prompt court produit dans
Codex CLI, puis utiliser Codex directement. Aucune clé n'est copiée entre les
outils.
## F. Retour depuis Codex
Lancer `/lardon-resume-from-codex`. OpenCode lit le handoff et le diff laissé
par Codex, puis effectue revue et tests sans refaire l'architecture. Le handoff
est ensuite actualisé.
## G. GPT-5.6 Sol direct dans OpenCode
Ce scénario est désactivé : Q1 n'est pas confirmé. Il ne pourra être ajouté que
si une route ChatGPT/Codex officiellement supportée, sans facturation Zen ou API
distincte, et une consommation du quota Codex sont toutes prouvées.
## H. Authentification directe avec quota inconnu
Le cas Q2 n'a pas été observé. S'il apparaît plus tard, aucune utilisation ne
doit être automatique : documenter le fournisseur et le risque, puis obtenir une
décision explicite avant une commande expérimentale.
## I. Fallback payant interdit
La configuration ne référence que des modèles explicitement gratuits. Une panne
de tous les secours gratuits arrête le ticket en conservant le handoff. Elle ne
doit jamais sélectionner `big-pickle`, GPT-5.6 Sol ou un autre modèle tarifé ou
de statut inconnu.
## Chargement progressif
1. Lire AGENTS.md et l'overview injectés par la configuration.
2. Faire identifier par explore les seuls fichiers et documents utiles.
3. Charger ces documents seulement ; appeler architect si une abstraction est
touchée.
4. Transmettre à l'implémenteur un résumé concis, jamais les documents complets.
5. Appeler uniquement les validations et revues pertinentes.
Pour une reprise, commencer par le handoff et le diff. Les sorties intermédiaires
ne conservent que conclusions, risques, fichiers, commandes, erreurs et
décisions ; les longs logs et les sources complètes sont exclus.

View file

@ -34,6 +34,82 @@ terminal_state(Lardon3DTaskState state)
|| state == TASK_COMPLETED; || state == TASK_COMPLETED;
} }
static void
unlink_pending(Lardon3DTaskQueue *queue, TaskNode *previous, TaskNode *node)
{
if (previous) {
previous->next_pending = node->next_pending;
} else {
queue->pending_head = node->next_pending;
}
if (queue->pending_tail == node) {
queue->pending_tail = previous;
}
node->next_pending = NULL;
}
/* Parcourt la file d'attente et sélectionne la première tâche admissible.
* Les tâches terminales ou refusées sont retirées de la file d'attente.
* Une tâche en attente de ressources reste en file et sera réévaluée.
* Retourne NULL si aucune tâche ne peut démarrer immédiatement. */
static Lardon3DTask *
select_admissible(
Lardon3DTaskQueue *queue,
Lardon3DResourceReservation **reservation
)
{
TaskNode *previous = NULL;
TaskNode *node = queue->pending_head;
while (node) {
TaskNode *next = node->next_pending;
Lardon3DTaskSnapshot task_snapshot;
if (!lardon3d_task_snapshot(node->task, &task_snapshot)) {
unlink_pending(queue, previous, node);
node = next;
continue;
}
if (terminal_state(task_snapshot.state)) {
unlink_pending(queue, previous, node);
node = next;
continue;
}
Lardon3DResourceEstimate estimate;
Lardon3DResourceDecision decision;
Lardon3DResourceReservation *candidate = NULL;
bool evaluated = lardon3d_task_resource_estimate(node->task, &estimate)
&& lardon3d_resource_governor_reserve_available(
queue->governor,
&estimate,
&decision,
&candidate
);
if (!evaluated) {
(void)lardon3d_task_reject(
node->task,
"Impossible d'évaluer les ressources disponibles."
);
unlink_pending(queue, previous, node);
node = next;
continue;
}
if (decision.kind == LARDON3D_RESOURCE_WAIT) {
previous = node;
node = next;
continue;
}
if (decision.kind == LARDON3D_RESOURCE_REJECT || !candidate) {
(void)lardon3d_task_reject(node->task, decision.reason);
unlink_pending(queue, previous, node);
node = next;
continue;
}
unlink_pending(queue, previous, node);
*reservation = candidate;
return node->task;
}
return NULL;
}
static void * static void *
queue_worker(void *context) queue_worker(void *context)
{ {
@ -47,64 +123,19 @@ queue_worker(void *context)
(void)pthread_mutex_unlock(&queue->mutex); (void)pthread_mutex_unlock(&queue->mutex);
return NULL; return NULL;
} }
TaskNode *node = queue->pending_head;
Lardon3DTaskSnapshot task_snapshot;
if (!lardon3d_task_snapshot(node->task, &task_snapshot)) {
(void)pthread_mutex_unlock(&queue->mutex);
continue;
}
if (terminal_state(task_snapshot.state)) {
queue->pending_head = node->next_pending;
if (!queue->pending_head) {
queue->pending_tail = NULL;
}
node->next_pending = NULL;
(void)pthread_mutex_unlock(&queue->mutex);
continue;
}
Lardon3DResourceEstimate estimate;
Lardon3DResourceDecision decision;
Lardon3DResourceReservation *reservation = NULL; Lardon3DResourceReservation *reservation = NULL;
bool evaluated = lardon3d_task_resource_estimate(node->task, &estimate) Lardon3DTask *selected = select_admissible(queue, &reservation);
&& lardon3d_resource_governor_reserve_available( if (!selected) {
queue->governor,
&estimate,
&decision,
&reservation
);
if (!evaluated) {
(void)lardon3d_task_reject(
node->task,
"Impossible d'évaluer les ressources disponibles."
);
(void)pthread_mutex_unlock(&queue->mutex);
continue;
}
if (decision.kind == LARDON3D_RESOURCE_WAIT) {
(void)pthread_cond_wait(&queue->condition, &queue->mutex); (void)pthread_cond_wait(&queue->condition, &queue->mutex);
(void)pthread_mutex_unlock(&queue->mutex); (void)pthread_mutex_unlock(&queue->mutex);
continue; continue;
} }
if (decision.kind == LARDON3D_RESOURCE_REJECT || !reservation) { queue->active = selected;
(void)lardon3d_task_reject(node->task, decision.reason);
(void)pthread_mutex_unlock(&queue->mutex);
continue;
}
queue->pending_head = node->next_pending;
if (!queue->pending_head) {
queue->pending_tail = NULL;
}
node->next_pending = NULL;
queue->active = node->task;
(void)pthread_mutex_unlock(&queue->mutex); (void)pthread_mutex_unlock(&queue->mutex);
if (!lardon3d_task_start( if (!lardon3d_task_start(selected, queue->governor, reservation)) {
node->task,
queue->governor,
reservation
)) {
(void)lardon3d_task_reject( (void)lardon3d_task_reject(
node->task, selected,
"Réservation de ressources invalide." "Réservation de ressources invalide."
); );
} }
@ -290,14 +321,7 @@ lardon3d_task_queue_remove(Lardon3DTaskQueue *queue, uint64_t task_id)
pending = pending->next_pending; pending = pending->next_pending;
} }
if (pending) { if (pending) {
if (pending_previous) { unlink_pending(queue, pending_previous, pending);
pending_previous->next_pending = pending->next_pending;
} else {
queue->pending_head = pending->next_pending;
}
if (queue->pending_tail == pending) {
queue->pending_tail = pending_previous;
}
} }
--queue->count; --queue->count;
(void)pthread_mutex_unlock(&queue->mutex); (void)pthread_mutex_unlock(&queue->mutex);

View file

@ -296,6 +296,74 @@ run_test(void)
lardon3d_task_queue_destroy(queue); lardon3d_task_queue_destroy(queue);
CHECK(pthread_mutex_destroy(&wait_log.mutex) == 0); CHECK(pthread_mutex_destroy(&wait_log.mutex) == 0);
/* Une tâche de tête bloquée en WAIT ne doit pas empêcher une tâche
admissible située derrière elle de démarrer. */
Lardon3DResourceSnapshot io_blocking_snapshot = {
.memory_available_bytes = UINT64_MAX,
.cpu_load_1m = 0.0,
};
Lardon3DResourceEstimate io_blocking_estimate = {
.minimum_batch_size = 1,
.maximum_batch_size = 1,
.desired_cpu_threads = 1,
.desired_io_slots = 1,
};
Lardon3DResourceDecision io_decision;
Lardon3DResourceReservation *io_blocking_reservation;
CHECK(lardon3d_resource_governor_reserve(
governor,
&io_blocking_snapshot,
&io_blocking_estimate,
&io_decision,
&io_blocking_reservation
));
CHECK(io_blocking_reservation);
queue = lardon3d_task_queue_create(governor);
CHECK(queue);
OrderLog bypass_log = {0};
CHECK(pthread_mutex_init(&bypass_log.mutex, NULL) == 0);
Lardon3DResourceEstimate io_estimate = {
.minimum_batch_size = 1,
.maximum_batch_size = 1,
.desired_cpu_threads = 1,
.desired_io_slots = 1,
};
QueueWork blocked = {.log = &bypass_log, .value = 1, .steps = 1};
Lardon3DTask *blocked_task = lardon3d_task_create(
"Bloquée en tête",
&io_estimate,
queue_callback,
&blocked
);
uint64_t blocked_id;
CHECK(blocked_task);
CHECK(lardon3d_task_queue_add(queue, blocked_task, &blocked_id));
QueueWork bypass = {.log = &bypass_log, .value = 2, .steps = 1};
Lardon3DTask *bypass_task = lardon3d_task_create(
"Admissible derrière",
&estimate,
queue_callback,
&bypass
);
uint64_t bypass_id;
CHECK(bypass_task);
CHECK(lardon3d_task_queue_add(queue, bypass_task, &bypass_id));
CHECK(wait_terminal(queue, bypass_id, &snapshot));
CHECK(snapshot.state == TASK_COMPLETED);
CHECK(bypass.contract_seen);
CHECK(lardon3d_task_queue_get(queue, blocked_id, &snapshot));
CHECK(snapshot.state == TASK_PENDING);
CHECK(!blocked.contract_seen);
CHECK(lardon3d_resource_governor_release(governor, io_blocking_reservation));
lardon3d_task_queue_resources_changed(queue);
CHECK(wait_terminal(queue, blocked_id, &snapshot));
CHECK(snapshot.state == TASK_COMPLETED);
CHECK(blocked.contract_seen);
CHECK(bypass_log.count == 2);
CHECK(lardon3d_resource_governor_reservation_count(governor) == 0);
lardon3d_task_queue_destroy(queue);
CHECK(pthread_mutex_destroy(&bypass_log.mutex) == 0);
queue = lardon3d_task_queue_create(governor); queue = lardon3d_task_queue_create(governor);
CHECK(queue); CHECK(queue);
OrderLog contract_log = {0}; OrderLog contract_log = {0};