new models

This commit is contained in:
fy59 2026-08-07 09:24:01 +02:00
parent 60832b51cf
commit ad90295e0d
13 changed files with 617 additions and 102 deletions

View file

@ -5,7 +5,12 @@ model: opencode/nemotron-3-ultra-free
temperature: 0.1 temperature: 0.1
permission: permission:
edit: deny edit: deny
bash: deny bash:
"*": deny
"rg *": allow
"git grep*": allow
"git diff*": allow
"sed -n *": allow
task: deny task: deny
--- ---

View file

@ -0,0 +1,79 @@
---
description: Reprend un build complexe via OpenRouter lorsque Google ou Zen sont indisponibles
mode: subagent
model: openrouter/nvidia/nemotron-3-ultra-550b-a55b:free
temperature: 0.1
permission:
edit:
"*": allow
"scan3d/**": deny
bash:
"*": deny
"rg *": allow
"git grep*": allow
"git status*": allow
"git diff*": allow
"git log*": allow
"sed -n *": allow
"CC=clang meson setup *": allow
"meson setup *": allow
"meson compile *": allow
"meson test *": allow
"ninja *": allow
"git diff --check*": allow
task: deny
---
Tu es le deuxième agent de secours pour l'implémentation de Lardon3D.
Tu interviens uniquement lorsqu'un ticket est déjà préparé et que le build
principal ou le premier backup sont indisponibles.
Commence par lire :
- `.opencode/work/current_ticket.md` ;
- le diff courant ;
- uniquement les fichiers explicitement cités dans le handoff.
Ne refais pas l'exploration ou l'architecture si le handoff contient déjà les
informations nécessaires.
Ne charge jamais tout le dépôt.
Ne lance jamais :
- `pwd`
- `ls`
- `find`
- glob global
- lecture complète de la documentation
Respecte strictement :
- l'objectif du ticket ;
- les contraintes ;
- les API existantes ;
- les invariants ;
- le périmètre de fichiers indiqué.
Ne crée aucune fonctionnalité supplémentaire.
Ne modifie jamais `scan3d/`.
Ne fais aucun commit.
Ne fais aucun push.
Après implémentation :
- exécute uniquement les validations demandées par le ticket ;
- ne tente pas de corriger un autre problème découvert hors périmètre ;
- signale-le seulement.
À la fin, rapporte uniquement :
- modification exacte ;
- fichiers modifiés ;
- validations exécutées ;
- erreurs éventuelles ;
- limites restantes ;
- prochaine action sûre.

View file

@ -1,12 +1,26 @@
--- ---
description: Reprend les tickets complexes interrompus sans refaire l'analyse description: Reprend les tickets complexes interrompus sans refaire l'analyse
mode: subagent mode: subagent
model: opencode/nemotron-3-ultra-free model: opencode/deepseek-v4-flash-free
temperature: 0.1 temperature: 0.1
permission: permission:
edit: edit:
"*": allow "*": allow
"scan3d/**": deny "scan3d/**": deny
bash:
"*": deny
"rg *": allow
"git grep*": allow
"git status*": allow
"git diff*": allow
"git log*": allow
"sed -n *": allow
"CC=clang meson setup *": allow
"meson setup *": allow
"meson compile *": allow
"meson test *": allow
"ninja *": allow
"git diff --check*": allow
task: deny task: deny
--- ---

View file

@ -7,6 +7,20 @@ permission:
edit: edit:
"*": allow "*": allow
"scan3d/**": deny "scan3d/**": deny
bash:
"*": deny
"rg *": allow
"git grep*": allow
"git status*": allow
"git diff*": allow
"git log*": allow
"sed -n *": allow
"CC=clang meson setup *": allow
"meson setup *": allow
"meson compile *": allow
"meson test *": allow
"ninja *": allow
"git diff --check*": allow
task: deny task: deny
--- ---

View file

@ -1,12 +1,26 @@
--- ---
description: Implémente seul les tickets complexes Lardon3D description: Implémente seul les tickets complexes Lardon3D
mode: subagent mode: subagent
model: opencode/deepseek-v4-flash-free model: google/gemini-3.6-flash
temperature: 0.1 temperature: 0.1
permission: permission:
edit: edit:
"*": allow "*": allow
"scan3d/**": deny "scan3d/**": deny
bash:
"*": deny
"rg *": allow
"git grep*": allow
"git status*": allow
"git diff*": allow
"git log*": allow
"sed -n *": allow
"CC=clang meson setup *": allow
"meson setup *": allow
"meson compile *": allow
"meson test *": allow
"ninja *": allow
"git diff --check*": allow
task: deny task: deny
--- ---

View file

@ -7,8 +7,12 @@ permission:
edit: deny edit: deny
bash: bash:
"*": deny "*": deny
"git diff*": allow
"rg *": allow "rg *": allow
"git grep*": allow
"git status*": allow
"git diff*": allow
"git log*": allow
"sed -n *": allow
task: deny task: deny
--- ---

View file

@ -9,6 +9,9 @@ permission:
"*": deny "*": deny
"rg *": allow "rg *": allow
"git grep*": allow "git grep*": allow
"git status*": allow
"git diff*": allow
"sed -n *": allow
task: deny task: deny
--- ---

View file

@ -16,6 +16,7 @@ permission:
"*": deny "*": deny
"lardon-build": allow "lardon-build": allow
"lardon-build-backup": allow "lardon-build-backup": allow
"lardon-build-backup-router": allow
"lardon-build-light": allow "lardon-build-light": allow
"lardon-architect": allow "lardon-architect": allow
"lardon-explore": allow "lardon-explore": allow
@ -70,6 +71,21 @@ Cette règle sapplique même si le ticket ne crée aucun nouveau thread.
- fichiers concernés ; - fichiers concernés ;
- API et invariants ; - API et invariants ;
- tests requis. - tests requis.
Chaîne d'implémentation :
1. Utiliser `lardon-build` pour l'implémentation normale.
2. Si `lardon-build` échoue pour indisponibilité du fournisseur, quota ou erreur
429/503, conserver le handoff et utiliser `lardon-build-backup`.
3. Si le premier backup est lui-même indisponible, utiliser
`lardon-build-backup-router`.
4. Ne jamais recommencer l'exploration ou l'architecture lors d'un fallback si
le handoff contient déjà les informations nécessaires.
5. Ne jamais basculer vers un modèle payant.
Un fallback ne doit modifier ni l'objectif, ni le périmètre, ni les invariants
du ticket.
7. Mettre à jour le handoff après chaque phase importante. 7. Mettre à jour le handoff après chaque phase importante.
Le répertoire `.opencode/work/` est préparé localement. Ne vérifie pas son Le répertoire `.opencode/work/` est préparé localement. Ne vérifie pas son

View file

@ -7,8 +7,12 @@ permission:
edit: deny edit: deny
bash: bash:
"*": deny "*": deny
"git diff*": allow "rg *": allow
"git grep*": allow
"git status*": allow "git status*": allow
"git diff*": allow
"git log*": allow
"sed -n *": allow
task: deny task: deny
--- ---

View file

@ -8,10 +8,11 @@ permission:
bash: bash:
"*": deny "*": deny
"CC=clang meson setup *": allow "CC=clang meson setup *": allow
"meson setup *": allow
"meson compile *": allow "meson compile *": allow
"meson test *": allow "meson test *": allow
"ninja *": allow "ninja *": allow
"git diff --check*": allow "git diff*": allow
"git status*": allow "git status*": allow
task: deny task: deny
--- ---

View file

@ -83,8 +83,11 @@ bool lardon3d_task_execution_contract(
); );
/* Libère la réservation courante, en obtient une nouvelle auprès du gouverneur /* Libère la réservation courante, en obtient une nouvelle auprès du gouverneur
* et met à jour le contrat. À appeler uniquement depuis le callback en cours * et met à jour le contrat. À appeler uniquement depuis le callback en cours
* d'exécution. Retourne false si pause/annulation est demandée ou si le * d'exécution. Une réponse WAIT du gouverneur est une indisponibilité
* gouverneur répond WAIT/REJECT (la tâche passe alors en échec). */ * temporaire : la fonction attend un changement de ressources puis retente
* l'admission sans échouer la tâche. Retourne false si la tâche est annulée
* (TASK_CANCELLED), si le gouverneur répond REJECT ou en cas d'erreur interne
* (TASK_FAILED). */
bool lardon3d_task_sequence_break( bool lardon3d_task_sequence_break(
Lardon3DTask *task, Lardon3DTask *task,
Lardon3DResourceGovernor *governor, Lardon3DResourceGovernor *governor,

View file

@ -278,6 +278,11 @@ lardon3d_task_checkpoint(Lardon3DTask *task)
return continuing; return continuing;
} }
enum {
/* Attente bornée entre deux tentatives d'admission : 50 ms. */
LARDON3D_SEQUENCE_ADMISSION_WAIT_NS = 50000000ULL,
};
bool bool
lardon3d_task_sequence_break( lardon3d_task_sequence_break(
Lardon3DTask *task, Lardon3DTask *task,
@ -302,6 +307,7 @@ lardon3d_task_sequence_break(
(void)pthread_cond_wait(&task->condition, &task->mutex); (void)pthread_cond_wait(&task->condition, &task->mutex);
} }
if (task->cancel_requested) { if (task->cancel_requested) {
finish_locked(task, TASK_CANCELLED, "Tâche annulée.");
(void)pthread_mutex_unlock(&task->mutex); (void)pthread_mutex_unlock(&task->mutex);
return false; return false;
} }
@ -314,28 +320,9 @@ lardon3d_task_sequence_break(
(void)lardon3d_resource_governor_release(governor, previous); (void)lardon3d_resource_governor_release(governor, previous);
} }
Lardon3DResourceDecision decision; for (;;) {
Lardon3DResourceReservation *next = NULL; /* Vérifier pause et annulation avant chaque tentative d'admission. */
bool reserved = lardon3d_resource_governor_reserve_available(
governor,
&task->estimate,
&decision,
&next
);
(void)pthread_mutex_lock(&task->mutex); (void)pthread_mutex_lock(&task->mutex);
if (task->cancel_requested) {
if (next) {
(void)lardon3d_resource_governor_release(governor, next);
}
finish_locked(
task,
TASK_CANCELLED,
"Tâche annulée pendant sequence_break."
);
(void)pthread_mutex_unlock(&task->mutex);
return false;
}
while (task->pause_requested && !task->cancel_requested) { while (task->pause_requested && !task->cancel_requested) {
task->state = TASK_PAUSED; task->state = TASK_PAUSED;
copy_text(task->message, sizeof(task->message), "Tâche en pause."); copy_text(task->message, sizeof(task->message), "Tâche en pause.");
@ -343,14 +330,25 @@ lardon3d_task_sequence_break(
(void)pthread_cond_wait(&task->condition, &task->mutex); (void)pthread_cond_wait(&task->condition, &task->mutex);
} }
if (task->cancel_requested) { if (task->cancel_requested) {
if (next) {
(void)lardon3d_resource_governor_release(governor, next);
}
finish_locked(task, TASK_CANCELLED, "Tâche annulée."); finish_locked(task, TASK_CANCELLED, "Tâche annulée.");
(void)pthread_mutex_unlock(&task->mutex); (void)pthread_mutex_unlock(&task->mutex);
return false; return false;
} }
if (!reserved || !next) { task->state = TASK_RUNNING;
(void)pthread_mutex_unlock(&task->mutex);
uint64_t generation = lardon3d_resource_governor_generation(governor);
Lardon3DResourceDecision decision;
Lardon3DResourceReservation *next = NULL;
bool admitted = lardon3d_resource_governor_reserve_available(
governor,
&task->estimate,
&decision,
&next
);
if (!admitted) {
/* Erreur interne : échec d'allocation ou d'instantané. */
(void)pthread_mutex_lock(&task->mutex);
task->current_reservation = NULL; task->current_reservation = NULL;
finish_locked( finish_locked(
task, task,
@ -360,14 +358,16 @@ lardon3d_task_sequence_break(
(void)pthread_mutex_unlock(&task->mutex); (void)pthread_mutex_unlock(&task->mutex);
return false; return false;
} }
if (decision.kind == LARDON3D_RESOURCE_WAIT switch (decision.kind) {
|| decision.kind == LARDON3D_RESOURCE_REJECT) { case LARDON3D_RESOURCE_START:
case LARDON3D_RESOURCE_REDUCE_BATCH: {
if (!next) {
(void)pthread_mutex_lock(&task->mutex);
task->current_reservation = NULL; task->current_reservation = NULL;
finish_locked( finish_locked(
task, task,
TASK_FAILED, TASK_FAILED,
decision.reason[0] ? decision.reason "Réservation de séquence invalide."
: "Ressources impossibles à réserver."
); );
(void)pthread_mutex_unlock(&task->mutex); (void)pthread_mutex_unlock(&task->mutex);
return false; return false;
@ -379,6 +379,7 @@ lardon3d_task_sequence_break(
&information &information
)) { )) {
(void)lardon3d_resource_governor_release(governor, next); (void)lardon3d_resource_governor_release(governor, next);
(void)pthread_mutex_lock(&task->mutex);
task->current_reservation = NULL; task->current_reservation = NULL;
finish_locked( finish_locked(
task, task,
@ -388,6 +389,7 @@ lardon3d_task_sequence_break(
(void)pthread_mutex_unlock(&task->mutex); (void)pthread_mutex_unlock(&task->mutex);
return false; return false;
} }
(void)pthread_mutex_lock(&task->mutex);
task->current_reservation = next; task->current_reservation = next;
task->contract = (Lardon3DTaskExecutionContract) { task->contract = (Lardon3DTaskExecutionContract) {
.batch_size = information.batch_size, .batch_size = information.batch_size,
@ -404,6 +406,36 @@ lardon3d_task_sequence_break(
(void)pthread_mutex_unlock(&task->mutex); (void)pthread_mutex_unlock(&task->mutex);
return true; return true;
} }
case LARDON3D_RESOURCE_REJECT:
if (next) {
(void)lardon3d_resource_governor_release(governor, next);
}
(void)pthread_mutex_lock(&task->mutex);
task->current_reservation = NULL;
finish_locked(
task,
TASK_FAILED,
decision.reason[0] ? decision.reason
: "Ressources impossibles à réserver."
);
(void)pthread_mutex_unlock(&task->mutex);
return false;
case LARDON3D_RESOURCE_WAIT:
default:
/* Indisponibilité temporaire : ne pas échouer, attendre un
* changement de ressources puis retenter l'admission. */
if (next) {
(void)lardon3d_resource_governor_release(governor, next);
}
(void)lardon3d_resource_governor_wait_for_change(
governor,
generation,
LARDON3D_SEQUENCE_ADMISSION_WAIT_NS
);
break;
}
}
}
unsigned int unsigned int
lardon3d_task_sequence_count(const Lardon3DTask *task) lardon3d_task_sequence_count(const Lardon3DTask *task)

View file

@ -28,7 +28,14 @@ typedef struct {
typedef struct { typedef struct {
Lardon3DResourceGovernor *governor; Lardon3DResourceGovernor *governor;
} WaitContext; pthread_mutex_t sync_mutex;
pthread_cond_t sync_cond;
bool in_break;
} WaitRetryContext;
typedef struct {
Lardon3DResourceGovernor *governor;
} RejectContext;
typedef struct { typedef struct {
Lardon3DResourceGovernor *governor; Lardon3DResourceGovernor *governor;
@ -102,9 +109,27 @@ seq_callback(Lardon3DTask *task, void *userdata)
} }
static bool static bool
wait_callback(Lardon3DTask *task, void *userdata) wait_retry_callback(Lardon3DTask *task, void *userdata)
{ {
WaitContext *ctx = userdata; WaitRetryContext *ctx = userdata;
(void)pthread_mutex_lock(&ctx->sync_mutex);
ctx->in_break = true;
(void)pthread_cond_broadcast(&ctx->sync_cond);
(void)pthread_mutex_unlock(&ctx->sync_mutex);
Lardon3DResourceReservation *reservation = NULL;
Lardon3DTaskExecutionContract contract;
return lardon3d_task_sequence_break(
task,
ctx->governor,
&reservation,
&contract
);
}
static bool
reject_callback(Lardon3DTask *task, void *userdata)
{
RejectContext *ctx = userdata;
Lardon3DResourceReservation *reservation = NULL; Lardon3DResourceReservation *reservation = NULL;
Lardon3DTaskExecutionContract contract; Lardon3DTaskExecutionContract contract;
return lardon3d_task_sequence_break( return lardon3d_task_sequence_break(
@ -247,7 +272,7 @@ run_sequential_test(void)
} }
static bool static bool
run_wait_test(void) run_wait_retry_test(void)
{ {
const Lardon3DResourceEstimate estimate = { const Lardon3DResourceEstimate estimate = {
.minimum_batch_size = 1, .minimum_batch_size = 1,
@ -274,7 +299,6 @@ run_wait_test(void)
Lardon3DResourceSnapshot resource_snapshot = { Lardon3DResourceSnapshot resource_snapshot = {
.memory_available_bytes = UINT64_MAX, .memory_available_bytes = UINT64_MAX,
.cpu_load_1m = 0.0, .cpu_load_1m = 0.0,
.io_pressure_known = false,
}; };
Lardon3DResourceDecision decision; Lardon3DResourceDecision decision;
Lardon3DResourceReservation *reservation; Lardon3DResourceReservation *reservation;
@ -285,11 +309,255 @@ run_wait_test(void)
&decision, &decision,
&reservation &reservation
)); ));
WaitContext ctx = {.governor = governor}; CHECK(reservation);
WaitRetryContext ctx = {.governor = governor};
CHECK(pthread_mutex_init(&ctx.sync_mutex, NULL) == 0);
CHECK(pthread_cond_init(&ctx.sync_cond, NULL) == 0);
Lardon3DTask *task = lardon3d_task_create( Lardon3DTask *task = lardon3d_task_create(
"Attente", "Attente puis reprise",
&estimate, &estimate,
wait_callback, wait_retry_callback,
&ctx
);
CHECK(task);
StartContext start = {task, governor, reservation};
pthread_t thread;
CHECK(pthread_create(&thread, NULL, start_task, &start) == 0);
(void)pthread_mutex_lock(&ctx.sync_mutex);
while (!ctx.in_break) {
(void)pthread_cond_wait(&ctx.sync_cond, &ctx.sync_mutex);
}
(void)pthread_mutex_unlock(&ctx.sync_mutex);
/* Tant que la politique reste bloquante, le gouverneur répond WAIT :
* la tâche doit rester en cours et ne jamais passer en échec. */
Lardon3DTaskSnapshot snapshot;
for (size_t attempt = 0; attempt < 50; ++attempt) {
CHECK(lardon3d_task_snapshot(task, &snapshot));
CHECK(snapshot.state == TASK_RUNNING);
short_pause(1000000);
}
CHECK(lardon3d_task_sequence_count(task) == 0);
/* Lever la contrainte : la prochaine tentative doit réussir. */
Lardon3DResourcePolicy allowed = policy;
allowed.maximum_io_pressure_avg10 = 100.0;
CHECK(lardon3d_resource_governor_set_policy(governor, &allowed));
CHECK(lardon3d_task_join(task));
void *thread_result;
CHECK(pthread_join(thread, &thread_result) == 0);
CHECK((uintptr_t)thread_result == 1);
CHECK(lardon3d_task_snapshot(task, &snapshot));
CHECK(snapshot.state == TASK_COMPLETED);
CHECK(lardon3d_task_sequence_count(task) == 1);
CHECK(lardon3d_resource_governor_reservation_count(governor) == 0);
lardon3d_task_destroy(task);
(void)pthread_cond_destroy(&ctx.sync_cond);
(void)pthread_mutex_destroy(&ctx.sync_mutex);
lardon3d_resource_governor_destroy(governor);
return true;
}
static bool
run_cancel_during_wait_test(void)
{
const Lardon3DResourceEstimate estimate = {
.minimum_batch_size = 1,
.maximum_batch_size = 100,
.desired_cpu_threads = 1,
.desired_io_slots = 1,
};
Lardon3DHardwareProfile profile = {
.logical_cpu_count = 16,
.page_size_bytes = 4096,
.memory_total_bytes = UINT64_MAX,
.cpu_architecture = "test",
};
Lardon3DResourcePolicy policy = {
.maximum_cpu_load_ratio = 1.0,
.maximum_io_pressure_avg10 = 0.0,
.io_slot_capacity = 1,
};
Lardon3DResourceGovernor *governor = lardon3d_resource_governor_create(
&profile,
&policy
);
CHECK(governor);
Lardon3DResourceSnapshot resource_snapshot = {
.memory_available_bytes = UINT64_MAX,
.cpu_load_1m = 0.0,
};
Lardon3DResourceDecision decision;
Lardon3DResourceReservation *reservation;
CHECK(lardon3d_resource_governor_reserve(
governor,
&resource_snapshot,
&estimate,
&decision,
&reservation
));
CHECK(reservation);
WaitRetryContext ctx = {.governor = governor};
CHECK(pthread_mutex_init(&ctx.sync_mutex, NULL) == 0);
CHECK(pthread_cond_init(&ctx.sync_cond, NULL) == 0);
Lardon3DTask *task = lardon3d_task_create(
"Annulation pendant attente",
&estimate,
wait_retry_callback,
&ctx
);
CHECK(task);
StartContext start = {task, governor, reservation};
pthread_t thread;
CHECK(pthread_create(&thread, NULL, start_task, &start) == 0);
(void)pthread_mutex_lock(&ctx.sync_mutex);
while (!ctx.in_break) {
(void)pthread_cond_wait(&ctx.sync_cond, &ctx.sync_mutex);
}
(void)pthread_mutex_unlock(&ctx.sync_mutex);
/* Annuler pendant que la tâche attend les ressources. */
lardon3d_task_request_cancel(task);
CHECK(lardon3d_task_join(task));
CHECK(pthread_join(thread, NULL) == 0);
Lardon3DTaskSnapshot snapshot;
CHECK(lardon3d_task_snapshot(task, &snapshot));
CHECK(snapshot.state == TASK_CANCELLED);
CHECK(lardon3d_resource_governor_reservation_count(governor) == 0);
lardon3d_task_destroy(task);
(void)pthread_cond_destroy(&ctx.sync_cond);
(void)pthread_mutex_destroy(&ctx.sync_mutex);
lardon3d_resource_governor_destroy(governor);
return true;
}
static bool
run_pause_during_wait_test(void)
{
const Lardon3DResourceEstimate estimate = {
.minimum_batch_size = 1,
.maximum_batch_size = 100,
.desired_cpu_threads = 1,
.desired_io_slots = 1,
};
Lardon3DHardwareProfile profile = {
.logical_cpu_count = 16,
.page_size_bytes = 4096,
.memory_total_bytes = UINT64_MAX,
.cpu_architecture = "test",
};
Lardon3DResourcePolicy policy = {
.maximum_cpu_load_ratio = 1.0,
.maximum_io_pressure_avg10 = 0.0,
.io_slot_capacity = 1,
};
Lardon3DResourceGovernor *governor = lardon3d_resource_governor_create(
&profile,
&policy
);
CHECK(governor);
Lardon3DResourceSnapshot resource_snapshot = {
.memory_available_bytes = UINT64_MAX,
.cpu_load_1m = 0.0,
};
Lardon3DResourceDecision decision;
Lardon3DResourceReservation *reservation;
CHECK(lardon3d_resource_governor_reserve(
governor,
&resource_snapshot,
&estimate,
&decision,
&reservation
));
CHECK(reservation);
WaitRetryContext ctx = {.governor = governor};
CHECK(pthread_mutex_init(&ctx.sync_mutex, NULL) == 0);
CHECK(pthread_cond_init(&ctx.sync_cond, NULL) == 0);
Lardon3DTask *task = lardon3d_task_create(
"Pause pendant attente",
&estimate,
wait_retry_callback,
&ctx
);
CHECK(task);
StartContext start = {task, governor, reservation};
pthread_t thread;
CHECK(pthread_create(&thread, NULL, start_task, &start) == 0);
(void)pthread_mutex_lock(&ctx.sync_mutex);
while (!ctx.in_break) {
(void)pthread_cond_wait(&ctx.sync_cond, &ctx.sync_mutex);
}
(void)pthread_mutex_unlock(&ctx.sync_mutex);
/* Mettre en pause pendant l'attente, puis reprendre. */
CHECK(lardon3d_task_pause(task));
CHECK(wait_for_state(task, TASK_PAUSED));
CHECK(lardon3d_task_resume(task));
CHECK(wait_for_state(task, TASK_RUNNING));
/* Lever la contrainte : la tâche doit reprendre puis réussir. */
Lardon3DResourcePolicy allowed = policy;
allowed.maximum_io_pressure_avg10 = 100.0;
CHECK(lardon3d_resource_governor_set_policy(governor, &allowed));
CHECK(lardon3d_task_join(task));
CHECK(pthread_join(thread, NULL) == 0);
Lardon3DTaskSnapshot snapshot;
CHECK(lardon3d_task_snapshot(task, &snapshot));
CHECK(snapshot.state == TASK_COMPLETED);
CHECK(lardon3d_task_sequence_count(task) == 1);
CHECK(lardon3d_resource_governor_reservation_count(governor) == 0);
lardon3d_task_destroy(task);
(void)pthread_cond_destroy(&ctx.sync_cond);
(void)pthread_mutex_destroy(&ctx.sync_mutex);
lardon3d_resource_governor_destroy(governor);
return true;
}
static bool
run_reject_test(void)
{
const Lardon3DResourceEstimate valid_estimate = {
.minimum_batch_size = 1,
.maximum_batch_size = 1,
.desired_cpu_threads = 1,
};
const Lardon3DResourceEstimate reject_estimate = {
.memory_fixed_bytes = UINT64_MAX,
.memory_bytes_per_item = 1,
.minimum_batch_size = 1,
.maximum_batch_size = 1,
.desired_cpu_threads = 1,
};
Lardon3DHardwareProfile profile = {
.logical_cpu_count = 16,
.page_size_bytes = 4096,
.memory_total_bytes = UINT64_MAX,
.cpu_architecture = "test",
};
Lardon3DResourcePolicy policy = {
.maximum_cpu_load_ratio = 1.0,
.maximum_io_pressure_avg10 = 100.0,
.io_slot_capacity = 1,
};
Lardon3DResourceGovernor *governor = lardon3d_resource_governor_create(
&profile,
&policy
);
CHECK(governor);
Lardon3DResourceSnapshot resource_snapshot = {
.memory_available_bytes = UINT64_MAX,
.cpu_load_1m = 0.0,
};
Lardon3DResourceDecision decision;
Lardon3DResourceReservation *reservation;
CHECK(lardon3d_resource_governor_reserve(
governor,
&resource_snapshot,
&valid_estimate,
&decision,
&reservation
));
CHECK(reservation);
RejectContext ctx = {.governor = governor};
Lardon3DTask *task = lardon3d_task_create(
"Refus gouverneur",
&reject_estimate,
reject_callback,
&ctx &ctx
); );
CHECK(task); CHECK(task);
@ -303,6 +571,52 @@ run_wait_test(void)
return true; return true;
} }
static bool
run_invalid_break_test(void)
{
const Lardon3DResourceEstimate estimate = {
.minimum_batch_size = 1,
.maximum_batch_size = 1,
.desired_cpu_threads = 1,
};
Lardon3DHardwareProfile profile = {
.logical_cpu_count = 4,
.page_size_bytes = 4096,
.memory_total_bytes = UINT64_MAX,
.cpu_architecture = "test",
};
Lardon3DResourcePolicy policy = {
.maximum_cpu_load_ratio = 1.0,
.maximum_io_pressure_avg10 = 100.0,
.io_slot_capacity = 1,
};
Lardon3DResourceGovernor *governor = lardon3d_resource_governor_create(
&profile,
&policy
);
CHECK(governor);
Lardon3DTask *task = lardon3d_task_create(
"Sans réservation",
&estimate,
wait_retry_callback,
NULL
);
CHECK(task);
Lardon3DResourceReservation *reservation = NULL;
Lardon3DTaskExecutionContract contract;
CHECK(!lardon3d_task_sequence_break(task, governor, &reservation, &contract));
CHECK(!lardon3d_task_sequence_break(NULL, governor, &reservation, &contract));
CHECK(!lardon3d_task_sequence_break(task, NULL, &reservation, &contract));
CHECK(!lardon3d_task_sequence_break(task, governor, NULL, &contract));
CHECK(!lardon3d_task_sequence_break(task, governor, &reservation, NULL));
Lardon3DTaskSnapshot snapshot;
CHECK(lardon3d_task_snapshot(task, &snapshot));
CHECK(snapshot.state == TASK_PENDING);
lardon3d_task_destroy(task);
lardon3d_resource_governor_destroy(governor);
return true;
}
static bool static bool
run_cancel_test(void) run_cancel_test(void)
{ {
@ -382,11 +696,23 @@ main(void)
if (!run_sequential_test()) { if (!run_sequential_test()) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (!run_wait_test()) { if (!run_wait_retry_test()) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (!run_cancel_test()) { if (!run_cancel_test()) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (!run_cancel_during_wait_test()) {
return EXIT_FAILURE;
}
if (!run_pause_during_wait_test()) {
return EXIT_FAILURE;
}
if (!run_reject_test()) {
return EXIT_FAILURE;
}
if (!run_invalid_break_test()) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }