feat(core): add asynchronous background task

This commit is contained in:
grayTerminal-sh 2026-07-17 10:55:40 +02:00
parent f7b521cda6
commit 2a8ed10034
7 changed files with 3808 additions and 2 deletions

View file

@ -40,6 +40,7 @@ TEST_ERROR = tests/test_error
TEST_INVESTIGATION_RECORD = tests/test_investigation_record TEST_INVESTIGATION_RECORD = tests/test_investigation_record
TEST_INVESTIGATION_DAO := tests/test_investigation_dao TEST_INVESTIGATION_DAO := tests/test_investigation_dao
TEST_INVESTIGATION_SESSION := tests/test_investigation_session TEST_INVESTIGATION_SESSION := tests/test_investigation_session
TEST_BACKGROUND_TASK := tests/test_background_task
all: $(TARGET) all: $(TARGET)
@ -137,6 +138,11 @@ $(TEST_INVESTIGATION_SESSION): \
src/database/error.c src/database/error.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3 $(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
$(TEST_BACKGROUND_TASK): \
tests/test_background_task.c \
src/core/background_task.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
test: \ test: \
$(TEST_NODE) \ $(TEST_NODE) \
$(TEST_TREE_MODEL) \ $(TEST_TREE_MODEL) \
@ -148,7 +154,8 @@ test: \
$(TEST_ERROR) \ $(TEST_ERROR) \
$(TEST_INVESTIGATION_RECORD) \ $(TEST_INVESTIGATION_RECORD) \
$(TEST_INVESTIGATION_DAO) \ $(TEST_INVESTIGATION_DAO) \
$(TEST_INVESTIGATION_SESSION) $(TEST_INVESTIGATION_SESSION) \
$(TEST_BACKGROUND_TASK)
@echo "Exécution des tests..." @echo "Exécution des tests..."
@./$(TEST_NODE) @./$(TEST_NODE)
@./$(TEST_TREE_MODEL) @./$(TEST_TREE_MODEL)
@ -161,6 +168,7 @@ test: \
@$(TEST_INVESTIGATION_RECORD) @$(TEST_INVESTIGATION_RECORD)
@$(TEST_INVESTIGATION_DAO) @$(TEST_INVESTIGATION_DAO)
@$(TEST_INVESTIGATION_SESSION) @$(TEST_INVESTIGATION_SESSION)
@$(TEST_BACKGROUND_TASK)
@echo "Tous les tests sont valides." @echo "Tous les tests sont valides."
%.o: %.c %.o: %.c
@ -181,6 +189,7 @@ clean:
$(TEST_ERROR) \ $(TEST_ERROR) \
$(TEST_INVESTIGATION_RECORD) \ $(TEST_INVESTIGATION_RECORD) \
$(TEST_INVESTIGATION_DAO) \ $(TEST_INVESTIGATION_DAO) \
$(TEST_INVESTIGATION_SESSION) $(TEST_INVESTIGATION_SESSION) \
$(TEST_BACKGROUND_TASK)
.PHONY: clean run test .PHONY: clean run test

View file

@ -0,0 +1,967 @@
# Ticket #034 — Modèle et exécuteur de tâches asynchrones
## Contexte
Labfy Investigation va bientôt exécuter des traitements potentiellement longs :
- calcul dempreintes ;
- copie de fichiers ;
- extraction de métadonnées ;
- lancement doutils externes ;
- recherches DNS et réseau ;
- analyse de résultats ;
- génération de rapports.
Ces opérations ne doivent jamais bloquer la boucle principale GTK.
Le ticket #035 ajoutera une file de tâches et un panneau dactivité. Avant cela, il faut créer une abstraction asynchrone fiable, indépendante de GTK et réutilisable par tous les futurs modules.
## Objectif
Créer un type opaque :
```c
BackgroundTask
```
capable de :
- exécuter une fonction de travail dans un thread GLib ;
- conserver son état ;
- suivre sa progression ;
- accepter une demande dannulation ;
- conserver un résultat ;
- conserver une erreur ;
- enregistrer ses dates de début et de fin ;
- appeler un callback de fin sur le contexte principal ;
- garantir une gestion correcte de sa durée de vie.
Le module doit sappuyer sur :
```text
GTask
GCancellable
GMutex
gatomicrefcount
```
Il ne doit dépendre ni de GTK, ni de SQLite, ni dune enquête particulière.
---
# Architecture attendue
Créer :
```text
include/core/background_task.h
src/core/background_task.c
tests/test_background_task.c
```
Le flux général doit être :
```text
background_task_new()
background_task_start()
GTask exécute le worker dans un thread
le worker signale sa progression
succès / erreur / annulation
callback de fin sur le contexte principal
background_task_unref()
```
---
# 1. Définir les états publics
Dans :
```text
include/core/background_task.h
```
définir :
```c
typedef enum
{
BACKGROUND_TASK_STATE_PENDING,
BACKGROUND_TASK_STATE_RUNNING,
BACKGROUND_TASK_STATE_COMPLETED,
BACKGROUND_TASK_STATE_FAILED,
BACKGROUND_TASK_STATE_CANCELLED
} BackgroundTaskState;
```
Transitions autorisées :
```text
PENDING → RUNNING
RUNNING → COMPLETED
RUNNING → FAILED
RUNNING → CANCELLED
```
Une tâche terminée ne peut jamais être redémarrée.
---
# 2. Définir le type opaque
```c
typedef struct BackgroundTask BackgroundTask;
```
La structure interne ne doit jamais apparaître dans le header.
---
# 3. Définir le worker
Ajouter :
```c
typedef gboolean (*BackgroundTaskWorker)(
BackgroundTask *task,
GCancellable *cancellable,
gpointer worker_data,
gpointer *result,
GError **error
);
```
Contrat :
```text
succès :
retourne TRUE
error reste NULL
result peut être NULL ou contenir un résultat
échec :
retourne FALSE
error doit normalement être renseignée
result doit rester NULL
annulation :
retourne FALSE
error appartient au domaine G_IO_ERROR
code G_IO_ERROR_CANCELLED
```
Le worker sexécute dans un thread secondaire.
Il ne doit jamais :
- manipuler directement GTK ;
- accéder à un widget ;
- modifier une structure non protégée ;
- appeler le callback final lui-même.
Le worker peut appeler :
```c
background_task_report_progress()
```
depuis son thread.
---
# 4. Définir le callback final
Ajouter :
```c
typedef void (*BackgroundTaskCompletionCallback)(
BackgroundTask *task,
gpointer user_data
);
```
Le callback doit être appelé après la mise à jour de létat final.
Lorsquune tâche est lancée depuis le thread principal GTK, le callback doit revenir sur ce contexte principal grâce au comportement de `GTask`.
---
# 5. Définir le domaine derreur
Ajouter :
```c
typedef enum
{
BACKGROUND_TASK_ERROR_INVALID_ARGUMENT,
BACKGROUND_TASK_ERROR_ALREADY_STARTED,
BACKGROUND_TASK_ERROR_WORKER_PROTOCOL
} BackgroundTaskError;
```
Puis :
```c
#define BACKGROUND_TASK_ERROR \
background_task_error_quark()
GQuark background_task_error_quark(void);
```
Utilisation :
- argument invalide ;
- seconde tentative de démarrage ;
- worker retournant `FALSE` sans fournir de `GError` ;
- worker retournant `TRUE` tout en fournissant une erreur.
---
# 6. API publique attendue
## Construction et références
```c
BackgroundTask *background_task_new(
const char *title
);
BackgroundTask *background_task_ref(
BackgroundTask *task
);
void background_task_unref(
BackgroundTask *task
);
```
`background_task_new()` doit refuser :
```text
title == NULL
title vide
```
La tâche utilise un comptage de références atomique.
Ne pas exposer une fonction `background_task_free()`.
Cette décision est importante : la tâche doit pouvoir rester vivante pendant lexécution même si son propriétaire visuel disparaît.
---
## Démarrage
```c
gboolean background_task_start(
BackgroundTask *task,
BackgroundTaskWorker worker,
gpointer worker_data,
GDestroyNotify worker_data_destroy,
GDestroyNotify result_destroy,
BackgroundTaskCompletionCallback completion_callback,
gpointer completion_data,
GDestroyNotify completion_data_destroy,
GError **error
);
```
### Propriété des paramètres
Si le démarrage réussit :
```text
BackgroundTask prend en charge worker_data
BackgroundTask prend en charge completion_data
BackgroundTask prend en charge le futur result
```
Les fonctions de destruction correspondantes seront appelées au moment approprié.
Si le démarrage échoue :
```text
lappelant conserve worker_data
lappelant conserve completion_data
```
### Contraintes
La fonction doit :
1. vérifier `task` ;
2. vérifier `worker` ;
3. vérifier la convention `GError` ;
4. refuser une tâche qui nest plus `PENDING` ;
5. créer un `GCancellable` ;
6. passer létat à `RUNNING` ;
7. enregistrer la date de début ;
8. lancer le worker avec `g_task_run_in_thread()` ;
9. conserver une référence interne jusquau callback final.
---
## Annulation
```c
void background_task_cancel(
BackgroundTask *task
);
gboolean background_task_is_cancelled(
const BackgroundTask *task
);
```
`background_task_cancel()` exprime une demande.
Le worker reste responsable de vérifier régulièrement :
```c
g_cancellable_set_error_if_cancelled()
```
ou :
```c
g_cancellable_is_cancelled()
```
Lannulation ne doit jamais tuer brutalement un thread.
---
## Progression
```c
void background_task_report_progress(
BackgroundTask *task,
double progress,
const char *status_message
);
```
Règles :
- utilisable depuis le worker ;
- protégée par `GMutex` ;
- valeur limitée entre `0.0` et `1.0` ;
- ignorée si la tâche nest pas `RUNNING` ;
- `status_message` est copié ;
- `status_message == NULL` est accepté.
Le ticket #035 pourra lire régulièrement ces valeurs pour mettre à jour le panneau dactivité.
---
## Accesseurs
```c
const char *background_task_get_title(
const BackgroundTask *task
);
BackgroundTaskState background_task_get_state(
const BackgroundTask *task
);
double background_task_get_progress(
const BackgroundTask *task
);
char *background_task_dup_status_message(
const BackgroundTask *task
);
gint64 background_task_get_started_at_us(
const BackgroundTask *task
);
gint64 background_task_get_finished_at_us(
const BackgroundTask *task
);
GError *background_task_dup_error(
const BackgroundTask *task
);
gpointer background_task_get_result(
const BackgroundTask *task
);
```
### Propriété des valeurs
```text
get_title() :
pointeur emprunté
valide pendant la durée de vie de task
titre immuable
dup_status_message() :
nouvelle chaîne
lappelant doit appeler g_free()
dup_error() :
nouvelle copie
lappelant doit appeler g_error_free()
get_result() :
pointeur emprunté
ne doit jamais être libéré par lappelant
```
`get_result()` ne doit être considéré comme valide quaprès létat :
```text
BACKGROUND_TASK_STATE_COMPLETED
```
---
# 7. Structure interne recommandée
Dans :
```text
src/core/background_task.c
```
la structure peut contenir :
```c
struct BackgroundTask
{
gatomicrefcount reference_count;
GMutex mutex;
char *title;
char *status_message;
BackgroundTaskState state;
double progress;
gint64 started_at_us;
gint64 finished_at_us;
GCancellable *cancellable;
gpointer result;
GDestroyNotify result_destroy;
GError *error;
BackgroundTaskCompletionCallback
completion_callback;
gpointer completion_data;
GDestroyNotify completion_data_destroy;
};
```
Les champs mutables doivent être protégés par `mutex`.
Le titre est immuable après construction.
---
# 8. Contexte interne dexécution
Créer une structure privée, par exemple :
```c
typedef struct
{
BackgroundTask *task;
BackgroundTaskWorker worker;
gpointer worker_data;
GDestroyNotify worker_data_destroy;
GDestroyNotify result_destroy;
} BackgroundTaskRunContext;
```
Le `worker_data` doit être détruit lorsque le contexte `GTask` est libéré.
Le pointeur `task` peut être non propriétaire si une référence interne distincte garantit sa durée de vie jusquau callback final.
---
# 9. Fonction exécutée dans le thread
Créer un trampoline privé compatible avec :
```c
GTaskThreadFunc
```
Il doit :
1. récupérer le contexte ;
2. appeler le worker ;
3. vérifier le contrat de retour ;
4. retourner le résultat avec `g_task_return_pointer()` ;
5. retourner lerreur avec `g_task_return_error()` ;
6. créer une erreur `BACKGROUND_TASK_ERROR_WORKER_PROTOCOL` si le worker viole son contrat.
Cas à traiter :
```text
FALSE + error valide :
échec normal
FALSE + error NULL :
erreur de protocole
TRUE + error non NULL :
erreur de protocole
TRUE + result quelconque :
succès
```
Si un résultat a été produit alors que lexécution échoue, il doit être détruit avec `result_destroy`.
---
# 10. Callback interne de fin
Créer un callback privé compatible avec :
```c
GAsyncReadyCallback
```
Il doit :
1. appeler `g_task_propagate_pointer()` ;
2. déterminer le nouvel état ;
3. conserver le résultat ou lerreur ;
4. enregistrer la date de fin ;
5. forcer la progression à `1.0` en cas de succès ;
6. appeler le callback utilisateur ;
7. détruire les données du callback utilisateur ;
8. libérer la référence interne de la tâche.
Détermination de létat :
```text
aucune erreur :
COMPLETED
G_IO_ERROR_CANCELLED :
CANCELLED
autre erreur :
FAILED
```
Le callback utilisateur doit observer un objet déjà entièrement finalisé.
---
# 11. Destruction de la tâche
Quand la dernière référence est libérée :
1. vérifier quaucune référence interne dexécution ne subsiste ;
2. détruire `result` avec `result_destroy` ;
3. libérer `error` ;
4. libérer les chaînes ;
5. libérer `GCancellable` ;
6. libérer les éventuelles données de callback restantes ;
7. nettoyer `GMutex` ;
8. libérer la structure.
Lappel suivant doit être accepté :
```c
background_task_unref(NULL);
```
---
# 12. Sécurité des threads
Les opérations suivantes doivent utiliser `GMutex` :
- lecture et écriture de létat ;
- progression ;
- message de progression ;
- dates ;
- résultat ;
- erreur ;
- accès au cancellable si nécessaire.
Ne jamais conserver le mutex verrouillé pendant :
- lappel du worker ;
- lappel du callback utilisateur ;
- une fonction de destruction fournie par lappelant ;
- un appel potentiellement bloquant.
---
# 13. Tests unitaires
Créer :
```text
tests/test_background_task.c
```
Les tests doivent utiliser un `GMainLoop` pour attendre le callback final.
## Test de construction
Vérifier :
```c
background_task_new(NULL) == NULL
background_task_new("") == NULL
```
Vérifier quune tâche valide commence avec :
```text
PENDING
progression 0.0
date de début 0
date de fin 0
aucune erreur
aucun résultat
```
## Test de succès
Créer un worker qui :
1. signale plusieurs progressions ;
2. renvoie une chaîne allouée ;
3. retourne `TRUE`.
Vérifier dans le callback :
```text
état COMPLETED
progression 1.0
résultat correct
erreur NULL
date de début > 0
date de fin >= date de début
callback appelé une seule fois
```
Vérifier que `result_destroy` est appelé lors du dernier `unref`.
## Test déchec
Créer un worker qui retourne :
```c
FALSE
```
avec une erreur :
```text
G_IO_ERROR_FAILED
```
Vérifier :
```text
état FAILED
résultat NULL
erreur conservée
message conservé
```
## Test de protocole invalide
Créer un worker qui retourne :
```c
FALSE
```
sans renseigner `GError`.
Vérifier :
```text
état FAILED
domaine BACKGROUND_TASK_ERROR
code BACKGROUND_TASK_ERROR_WORKER_PROTOCOL
```
## Test dannulation
Créer un worker qui travaille par petites étapes et vérifie régulièrement le `GCancellable`.
Programmer :
```c
background_task_cancel()
```
depuis le contexte principal avec `g_timeout_add()`.
Vérifier :
```text
état CANCELLED
erreur G_IO_ERROR_CANCELLED
callback final appelé
aucun crash
```
## Test de double démarrage
Démarrer une tâche puis rappeler immédiatement :
```c
background_task_start()
```
Vérifier :
```text
FALSE
BACKGROUND_TASK_ERROR_ALREADY_STARTED
```
La première exécution doit continuer normalement.
## Test des données utilisateur
Vérifier que :
- `worker_data_destroy` est appelé exactement une fois ;
- `completion_data_destroy` est appelé exactement une fois ;
- aucune donnée nest détruite lorsque `background_task_start()` échoue avant transfert de propriété.
## Test du comptage de références
Démarrer une tâche puis libérer immédiatement la référence de lappelant.
Vérifier que :
- la tâche reste vivante jusquau callback ;
- aucun accès mémoire invalide na lieu ;
- la destruction finale intervient après la fin de lexécution.
---
# 14. Makefile
Le code de production est déjà découvert automatiquement si le Makefile utilise :
```make
SRC := $(shell find src -name "*.c")
```
Ajouter toutefois une cible de test dédiée :
```make
TEST_BACKGROUND_TASK := tests/test_background_task
```
La cible doit compiler au minimum :
```text
tests/test_background_task.c
src/core/background_task.c
```
Lier avec les paquets GLib/GIO déjà utilisés par le projet.
Ajouter le binaire aux cibles :
```text
test
clean
```
Sortie attendue :
```text
BackgroundTask : tous les tests sont valides.
```
---
# 15. Hors périmètre
Ce ticket ne doit pas encore ajouter :
- de file de tâches ;
- de limite de concurrence ;
- de panneau GTK ;
- de persistance SQLite ;
- de tâche associée à une enquête ;
- dexécution de commande externe ;
- dadaptateur ExifTool ;
- de recherche DNS ;
- de système de notifications ;
- de reprise après redémarrage ;
- de priorité entre tâches.
Ces fonctions viendront dans les tickets suivants.
---
# 16. Critères dacceptation
- [ ] `BackgroundTask` est opaque.
- [ ] Le module ne dépend pas de GTK.
- [ ] Le module ne dépend pas de SQLite.
- [ ] Le module utilise `GTask`.
- [ ] Le module utilise `GCancellable`.
- [ ] Le module utilise un comptage de références.
- [ ] Le module protège son état avec `GMutex`.
- [ ] Une tâche ne peut être démarrée quune fois.
- [ ] Le worker sexécute dans un thread secondaire.
- [ ] Le callback final revient sur le contexte principal.
- [ ] La progression est comprise entre `0.0` et `1.0`.
- [ ] Lannulation est coopérative.
- [ ] Le résultat est conservé jusquà la destruction.
- [ ] Lerreur est conservée jusquà la destruction.
- [ ] Les dates de début et de fin sont enregistrées.
- [ ] Les données utilisateur sont détruites exactement une fois.
- [ ] La tâche reste vivante pendant son exécution.
- [ ] Aucun callback utilisateur nest appelé sous mutex.
- [ ] Tous les tests unitaires passent.
- [ ] Les anciens tests restent valides.
- [ ] `make` réussit.
- [ ] `make test` réussit.
- [ ] `git diff --check` ne retourne aucune erreur.
---
# 17. Audit attendu
Vérifier labsence de GTK et SQLite :
```bash
rg -n \
'#include <gtk|sqlite3_|Database|Investigation' \
include/core/background_task.h \
src/core/background_task.c
```
Résultat attendu :
```text
aucune sortie
```
Vérifier les primitives asynchrones :
```bash
rg -n \
'GTask|GCancellable|GMutex|g_atomic_ref_count' \
include/core/background_task.h \
src/core/background_task.c
```
Vérifier quaucun thread POSIX brut nest ajouté :
```bash
rg -n \
'pthread_|pthread.h' \
include/core/background_task.h \
src/core/background_task.c
```
Résultat attendu :
```text
aucune sortie
```
Vérifier labsence de sortie forcée :
```bash
rg -n \
'\bexit\s*\(' \
src/core/background_task.c \
tests/test_background_task.c
```
Résultat attendu :
```text
aucune sortie
```
---
# 18. Fichiers concernés
```text
include/core/background_task.h
src/core/background_task.c
tests/test_background_task.c
Makefile
```
Aucune modification de `Application` ou de GTK nest attendue.
---
# 19. Commit attendu
```bash
make clean
make
make test
git diff --check
git status --short
```
```bash
git add \
include/core/background_task.h \
src/core/background_task.c \
tests/test_background_task.c \
Makefile
```
```bash
git diff --cached --stat
git diff --cached
```
```bash
git commit -m "feat(core): add asynchronous background task"
git push
```
---
# Résultat attendu
Après ce ticket, Labfy disposera dune primitive générique pour exécuter proprement les futurs traitements longs :
```text
SHA-256
copie de preuve
ExifTool
dig
RDAP
TLS
HTTP
recherche Web
génération de rapport
```
Le ticket #035 pourra ensuite construire une file de tâches et un panneau dactivité au-dessus de cette abstraction.

View file

@ -0,0 +1,289 @@
/******************************************************************************
* @file background_task.h
* @brief Tâche générique exécutée en arrière-plan.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_BACKGROUND_TASK_H
#define LABFY_INVESTIGATION_BACKGROUND_TASK_H
#include <gio/gio.h>
/**
* @brief Représentation opaque d'une tâche asynchrone.
*/
typedef struct BackgroundTask BackgroundTask;
/**
* @brief États possibles d'une tâche.
*/
typedef enum
{
BACKGROUND_TASK_STATE_PENDING,
BACKGROUND_TASK_STATE_RUNNING,
BACKGROUND_TASK_STATE_COMPLETED,
BACKGROUND_TASK_STATE_FAILED,
BACKGROUND_TASK_STATE_CANCELLED
} BackgroundTaskState;
/**
* @brief Codes d'erreur propres au module BackgroundTask.
*/
typedef enum
{
BACKGROUND_TASK_ERROR_INVALID_ARGUMENT,
BACKGROUND_TASK_ERROR_ALREADY_STARTED,
BACKGROUND_TASK_ERROR_WORKER_PROTOCOL
} BackgroundTaskError;
/**
* @brief Domaine d'erreur du module BackgroundTask.
*/
#define BACKGROUND_TASK_ERROR \
background_task_error_quark()
/**
* @brief Fonction exécutée dans un thread secondaire.
*
* En cas de succès, la fonction retourne TRUE et peut placer un résultat
* dans result.
*
* En cas d'échec, elle retourne FALSE et renseigne normalement error.
*
* @param task Tâche en cours d'exécution.
* @param cancellable Objet d'annulation associé à la tâche.
* @param worker_data Données privées du worker.
* @param result Emplacement recevant le résultat alloué.
* @param error Emplacement recevant une erreur.
*
* @return TRUE en cas de succès, sinon FALSE.
*/
typedef gboolean (*BackgroundTaskWorker)(
BackgroundTask *task,
GCancellable *cancellable,
gpointer worker_data,
gpointer *result,
GError **error
);
/**
* @brief Callback appelé lorsque la tâche est terminée.
*
* Ce callback est appelé après la mise à jour de l'état final.
*
* @param task Tâche terminée.
* @param user_data Données privées du callback.
*/
typedef void (*BackgroundTaskCompletionCallback)(
BackgroundTask *task,
gpointer user_data
);
/**
* @brief Retourne le domaine d'erreur du module.
*
* @return Quark GLib du domaine d'erreur.
*/
GQuark background_task_error_quark(void);
/**
* @brief Crée une nouvelle tâche en attente.
*
* @param title Titre non vide de la tâche.
*
* @return Nouvelle tâche, ou NULL si le titre est invalide.
*/
BackgroundTask *background_task_new(
const char *title
);
/**
* @brief Ajoute une référence à une tâche.
*
* @param task Tâche concernée.
*
* @return La tâche fournie, ou NULL.
*/
BackgroundTask *background_task_ref(
BackgroundTask *task
);
/**
* @brief Libère une référence à une tâche.
*
* La fonction accepte task == NULL.
*
* @param task Tâche concernée.
*/
void background_task_unref(
BackgroundTask *task
);
/**
* @brief Démarre l'exécution asynchrone d'une tâche.
*
* Si le démarrage réussit, la tâche devient propriétaire de worker_data
* et completion_data.
*
* Si le démarrage échoue, l'appelant conserve leur propriété.
*
* Le futur résultat produit par le worker sera détruit avec
* result_destroy lors de la destruction de la tâche.
*
* @param task Tâche à démarrer.
* @param worker Fonction exécutée dans un thread secondaire.
* @param worker_data Données transmises au worker.
* @param worker_data_destroy Fonction de destruction de worker_data.
* @param result_destroy Fonction de destruction du futur résultat.
* @param completion_callback Callback final facultatif.
* @param completion_data Données transmises au callback final.
* @param completion_data_destroy Fonction de destruction associée.
* @param error Emplacement facultatif recevant une erreur.
*
* @return TRUE si la tâche a é lancée, sinon FALSE.
*/
gboolean background_task_start(
BackgroundTask *task,
BackgroundTaskWorker worker,
gpointer worker_data,
GDestroyNotify worker_data_destroy,
GDestroyNotify result_destroy,
BackgroundTaskCompletionCallback completion_callback,
gpointer completion_data,
GDestroyNotify completion_data_destroy,
GError **error
);
/**
* @brief Demande l'annulation coopérative de la tâche.
*
* @param task Tâche concernée.
*/
void background_task_cancel(
BackgroundTask *task
);
/**
* @brief Indique si une annulation a é demandée.
*
* @param task Tâche concernée.
*
* @return TRUE si l'annulation a é demandée, sinon FALSE.
*/
gboolean background_task_is_cancelled(
const BackgroundTask *task
);
/**
* @brief Met à jour la progression d'une tâche en cours.
*
* La progression est automatiquement limitée à l'intervalle
* compris entre 0.0 et 1.0.
*
* @param task Tâche concernée.
* @param progress Nouvelle progression.
* @param status_message Message facultatif, copié par le module.
*/
void background_task_report_progress(
BackgroundTask *task,
double progress,
const char *status_message
);
/**
* @brief Retourne le titre immuable d'une tâche.
*
* Le pointeur retourné est emprunté.
*
* @param task Tâche concernée.
*
* @return Titre de la tâche, ou NULL.
*/
const char *background_task_get_title(
const BackgroundTask *task
);
/**
* @brief Retourne l'état courant d'une tâche.
*
* @param task Tâche concernée.
*
* @return État courant de la tâche.
*/
BackgroundTaskState background_task_get_state(
const BackgroundTask *task
);
/**
* @brief Retourne la progression courante.
*
* @param task Tâche concernée.
*
* @return Progression comprise entre 0.0 et 1.0.
*/
double background_task_get_progress(
const BackgroundTask *task
);
/**
* @brief Copie le message de progression courant.
*
* L'appelant doit libérer la chaîne avec g_free().
*
* @param task Tâche concernée.
*
* @return Nouvelle chaîne, ou NULL.
*/
char *background_task_dup_status_message(
const BackgroundTask *task
);
/**
* @brief Retourne la date de démarrage monotone en microsecondes.
*
* @param task Tâche concernée.
*
* @return Date de démarrage, ou 0 si la tâche n'a pas démarré.
*/
gint64 background_task_get_started_at_us(
const BackgroundTask *task
);
/**
* @brief Retourne la date de fin monotone en microsecondes.
*
* @param task Tâche concernée.
*
* @return Date de fin, ou 0 si la tâche n'est pas terminée.
*/
gint64 background_task_get_finished_at_us(
const BackgroundTask *task
);
/**
* @brief Retourne une copie de l'erreur finale.
*
* L'appelant doit libérer l'erreur avec g_error_free().
*
* @param task Tâche concernée.
*
* @return Nouvelle copie de l'erreur, ou NULL.
*/
GError *background_task_dup_error(
const BackgroundTask *task
);
/**
* @brief Retourne le résultat final de la tâche.
*
* Le pointeur retourné est emprunté et ne doit pas être libéré.
* Il n'est exploitable qu'après l'état BACKGROUND_TASK_STATE_COMPLETED.
*
* @param task Tâche concernée.
*
* @return Résultat final, ou NULL.
*/
gpointer background_task_get_result(
const BackgroundTask *task
);
#endif

Binary file not shown.

1072
src/core/background_task.c Normal file

File diff suppressed because it is too large Load diff

BIN
tests/test_background_task Executable file

Binary file not shown.

1469
tests/test_background_task.c Normal file

File diff suppressed because it is too large Load diff