1103 lines
20 KiB
C
1103 lines
20 KiB
C
/******************************************************************************
|
|
* @file background_task.c
|
|
* @brief Implémentation d'une tâche générique exécutée en arrière-plan.
|
|
******************************************************************************/
|
|
|
|
#include "core/background_task.h"
|
|
|
|
#include <glib.h>
|
|
|
|
/**
|
|
* @struct BackgroundTask
|
|
* @brief État interne d'une tâche asynchrone.
|
|
*
|
|
* Les champs mutables sont protégés par mutex.
|
|
*
|
|
* Le titre est immuable après la création de la tâche.
|
|
*/
|
|
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;
|
|
};
|
|
|
|
/**
|
|
* @struct BackgroundTaskRunContext
|
|
* @brief Données privées associées à l'exécution GTask.
|
|
*/
|
|
typedef struct
|
|
{
|
|
BackgroundTask *task;
|
|
|
|
BackgroundTaskWorker worker;
|
|
|
|
gpointer worker_data;
|
|
GDestroyNotify worker_data_destroy;
|
|
|
|
GDestroyNotify result_destroy;
|
|
} BackgroundTaskRunContext;
|
|
|
|
GQuark background_task_error_quark(void)
|
|
{
|
|
return g_quark_from_static_string(
|
|
"labfy-investigation-background-task-error"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @brief Exécute le worker dans un thread secondaire.
|
|
*
|
|
* @param async_task Tâche GLib.
|
|
* @param source_object Objet source inutilisé.
|
|
* @param task_data Contexte BackgroundTaskRunContext.
|
|
* @param cancellable Objet d'annulation.
|
|
*/
|
|
static void background_task_run_in_thread(
|
|
GTask *async_task,
|
|
gpointer source_object,
|
|
gpointer task_data,
|
|
GCancellable *cancellable
|
|
)
|
|
{
|
|
BackgroundTaskRunContext *run_context =
|
|
task_data;
|
|
|
|
gpointer worker_result = NULL;
|
|
GError *worker_error = NULL;
|
|
|
|
gboolean worker_succeeded = FALSE;
|
|
|
|
(void) source_object;
|
|
|
|
if (run_context == NULL ||
|
|
run_context->task == NULL ||
|
|
run_context->worker == NULL)
|
|
{
|
|
g_task_return_new_error(
|
|
async_task,
|
|
BACKGROUND_TASK_ERROR,
|
|
BACKGROUND_TASK_ERROR_INVALID_ARGUMENT,
|
|
"Le contexte d'exécution de la tâche est invalide."
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
worker_succeeded = run_context->worker(
|
|
run_context->task,
|
|
cancellable,
|
|
run_context->worker_data,
|
|
&worker_result,
|
|
&worker_error
|
|
);
|
|
|
|
if (worker_succeeded)
|
|
{
|
|
if (worker_error != NULL)
|
|
{
|
|
if (worker_result != NULL &&
|
|
run_context->result_destroy != NULL)
|
|
{
|
|
run_context->result_destroy(
|
|
worker_result
|
|
);
|
|
|
|
worker_result = NULL;
|
|
}
|
|
|
|
g_task_return_new_error(
|
|
async_task,
|
|
BACKGROUND_TASK_ERROR,
|
|
BACKGROUND_TASK_ERROR_WORKER_PROTOCOL,
|
|
"Le worker a signalé un succès tout en retournant "
|
|
"une erreur : %s",
|
|
worker_error->message
|
|
);
|
|
|
|
g_clear_error(
|
|
&worker_error
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
g_task_return_pointer(
|
|
async_task,
|
|
worker_result,
|
|
run_context->result_destroy
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Un résultat ne doit pas être conservé lorsque le worker échoue.
|
|
*/
|
|
if (worker_result != NULL &&
|
|
run_context->result_destroy != NULL)
|
|
{
|
|
run_context->result_destroy(
|
|
worker_result
|
|
);
|
|
|
|
worker_result = NULL;
|
|
}
|
|
|
|
if (worker_error == NULL)
|
|
{
|
|
g_task_return_new_error(
|
|
async_task,
|
|
BACKGROUND_TASK_ERROR,
|
|
BACKGROUND_TASK_ERROR_WORKER_PROTOCOL,
|
|
"Le worker a signalé un échec sans fournir de GError."
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* GTask devient propriétaire de worker_error.
|
|
*/
|
|
g_task_return_error(
|
|
async_task,
|
|
worker_error
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @brief Finalise une tâche après l'exécution du worker.
|
|
*
|
|
* Ce callback est rappelé sur le contexte ayant démarré GTask.
|
|
*
|
|
* @param source_object Objet source inutilisé.
|
|
* @param async_result Résultat asynchrone.
|
|
* @param user_data Pointeur vers BackgroundTask.
|
|
*/
|
|
static void background_task_on_completed(
|
|
GObject *source_object,
|
|
GAsyncResult *async_result,
|
|
gpointer user_data
|
|
)
|
|
{
|
|
BackgroundTask *task = user_data;
|
|
|
|
gpointer worker_result = NULL;
|
|
GError *worker_error = NULL;
|
|
|
|
BackgroundTaskState final_state;
|
|
|
|
BackgroundTaskCompletionCallback
|
|
completion_callback = NULL;
|
|
|
|
gpointer completion_data = NULL;
|
|
|
|
GDestroyNotify completion_data_destroy =
|
|
NULL;
|
|
|
|
BackgroundTaskRunContext *run_context =
|
|
NULL;
|
|
|
|
BackgroundTask *internal_task_reference =
|
|
NULL;
|
|
|
|
(void) source_object;
|
|
|
|
if (task == NULL ||
|
|
async_result == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
run_context =
|
|
g_task_get_task_data(
|
|
G_TASK(async_result)
|
|
);
|
|
|
|
worker_result = g_task_propagate_pointer(
|
|
G_TASK(async_result),
|
|
&worker_error
|
|
);
|
|
|
|
if (worker_error == NULL)
|
|
{
|
|
final_state =
|
|
BACKGROUND_TASK_STATE_COMPLETED;
|
|
}
|
|
else if (g_error_matches(
|
|
worker_error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_CANCELLED
|
|
))
|
|
{
|
|
final_state =
|
|
BACKGROUND_TASK_STATE_CANCELLED;
|
|
}
|
|
else
|
|
{
|
|
final_state =
|
|
BACKGROUND_TASK_STATE_FAILED;
|
|
}
|
|
|
|
g_mutex_lock(
|
|
&task->mutex
|
|
);
|
|
|
|
task->state = final_state;
|
|
|
|
task->finished_at_us =
|
|
g_get_monotonic_time();
|
|
|
|
if (final_state ==
|
|
BACKGROUND_TASK_STATE_COMPLETED)
|
|
{
|
|
task->result = worker_result;
|
|
task->progress = 1.0;
|
|
|
|
worker_result = NULL;
|
|
}
|
|
else
|
|
{
|
|
task->error = worker_error;
|
|
worker_error = NULL;
|
|
}
|
|
|
|
completion_callback =
|
|
task->completion_callback;
|
|
|
|
completion_data =
|
|
task->completion_data;
|
|
|
|
completion_data_destroy =
|
|
task->completion_data_destroy;
|
|
|
|
/*
|
|
* Ces données sont extraites afin de garantir leur destruction
|
|
* exactement une fois après le callback utilisateur.
|
|
*/
|
|
task->completion_callback = NULL;
|
|
task->completion_data = NULL;
|
|
task->completion_data_destroy = NULL;
|
|
|
|
g_mutex_unlock(
|
|
&task->mutex
|
|
);
|
|
|
|
/*
|
|
* Aucun callback utilisateur ne doit être exécuté sous mutex.
|
|
*/
|
|
if (completion_callback != NULL)
|
|
{
|
|
completion_callback(
|
|
task,
|
|
completion_data
|
|
);
|
|
}
|
|
|
|
if (completion_data != NULL &&
|
|
completion_data_destroy != NULL)
|
|
{
|
|
completion_data_destroy(
|
|
completion_data
|
|
);
|
|
}
|
|
|
|
/*
|
|
* Ces variables sont normalement NULL après le transfert,
|
|
* mais ce nettoyage sécurise les futurs changements.
|
|
*/
|
|
if (worker_result != NULL &&
|
|
task->result_destroy != NULL)
|
|
{
|
|
task->result_destroy(
|
|
worker_result
|
|
);
|
|
}
|
|
|
|
g_clear_error(
|
|
&worker_error
|
|
);
|
|
|
|
/*
|
|
* L'exécution est maintenant totalement finalisée.
|
|
*
|
|
* La référence interne doit être libérée ici, et non attendre la
|
|
* destruction ultérieure du GTask. Le pointeur est placé à NULL pour
|
|
* empêcher background_task_run_context_free() de refaire un unref.
|
|
*/
|
|
if (run_context != NULL)
|
|
{
|
|
internal_task_reference =
|
|
run_context->task;
|
|
|
|
run_context->task =
|
|
NULL;
|
|
}
|
|
|
|
background_task_unref(
|
|
internal_task_reference
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @brief Libère le contexte privé d'une exécution.
|
|
*
|
|
* @param user_data Pointeur vers BackgroundTaskRunContext.
|
|
*/
|
|
static void background_task_run_context_free(
|
|
gpointer user_data
|
|
)
|
|
{
|
|
BackgroundTaskRunContext *run_context =
|
|
user_data;
|
|
|
|
if (run_context == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (run_context->worker_data != NULL &&
|
|
run_context->worker_data_destroy != NULL)
|
|
{
|
|
run_context->worker_data_destroy(
|
|
run_context->worker_data
|
|
);
|
|
}
|
|
|
|
background_task_unref(
|
|
run_context->task
|
|
);
|
|
|
|
g_free(
|
|
run_context
|
|
);
|
|
}
|
|
|
|
BackgroundTask *background_task_new(
|
|
const char *title
|
|
)
|
|
{
|
|
BackgroundTask *task = NULL;
|
|
|
|
if (title == NULL ||
|
|
title[0] == '\0')
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
task = g_new0(
|
|
BackgroundTask,
|
|
1
|
|
);
|
|
|
|
g_atomic_ref_count_init(
|
|
&task->reference_count
|
|
);
|
|
|
|
g_mutex_init(
|
|
&task->mutex
|
|
);
|
|
|
|
task->title = g_strdup(
|
|
title
|
|
);
|
|
|
|
task->state =
|
|
BACKGROUND_TASK_STATE_PENDING;
|
|
|
|
task->progress = 0.0;
|
|
task->started_at_us = 0;
|
|
task->finished_at_us = 0;
|
|
|
|
return task;
|
|
}
|
|
|
|
BackgroundTask *background_task_ref(
|
|
BackgroundTask *task
|
|
)
|
|
{
|
|
if (task == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
g_atomic_ref_count_inc(
|
|
&task->reference_count
|
|
);
|
|
|
|
return task;
|
|
}
|
|
|
|
void background_task_unref(
|
|
BackgroundTask *task
|
|
)
|
|
{
|
|
gpointer result = NULL;
|
|
GDestroyNotify result_destroy = NULL;
|
|
|
|
gpointer completion_data = NULL;
|
|
GDestroyNotify completion_data_destroy = NULL;
|
|
|
|
GCancellable *cancellable = NULL;
|
|
GError *error = NULL;
|
|
|
|
char *title = NULL;
|
|
char *status_message = NULL;
|
|
|
|
if (task == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!g_atomic_ref_count_dec(
|
|
&task->reference_count
|
|
))
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* À ce stade, aucune autre référence ne doit accéder à la tâche.
|
|
* Les ressources sont néanmoins extraites proprement avant
|
|
* la destruction du mutex.
|
|
*/
|
|
g_mutex_lock(
|
|
&task->mutex
|
|
);
|
|
|
|
result = task->result;
|
|
result_destroy = task->result_destroy;
|
|
|
|
completion_data = task->completion_data;
|
|
completion_data_destroy =
|
|
task->completion_data_destroy;
|
|
|
|
cancellable = task->cancellable;
|
|
error = task->error;
|
|
|
|
title = task->title;
|
|
status_message = task->status_message;
|
|
|
|
task->result = NULL;
|
|
task->result_destroy = NULL;
|
|
|
|
task->completion_data = NULL;
|
|
task->completion_data_destroy = NULL;
|
|
task->completion_callback = NULL;
|
|
|
|
task->cancellable = NULL;
|
|
task->error = NULL;
|
|
|
|
task->title = NULL;
|
|
task->status_message = NULL;
|
|
|
|
g_mutex_unlock(
|
|
&task->mutex
|
|
);
|
|
|
|
if (result != NULL &&
|
|
result_destroy != NULL)
|
|
{
|
|
result_destroy(
|
|
result
|
|
);
|
|
}
|
|
|
|
if (completion_data != NULL &&
|
|
completion_data_destroy != NULL)
|
|
{
|
|
completion_data_destroy(
|
|
completion_data
|
|
);
|
|
}
|
|
|
|
if (cancellable != NULL)
|
|
{
|
|
g_object_unref(
|
|
cancellable
|
|
);
|
|
}
|
|
|
|
g_clear_error(
|
|
&error
|
|
);
|
|
|
|
g_free(
|
|
status_message
|
|
);
|
|
|
|
g_free(
|
|
title
|
|
);
|
|
|
|
g_mutex_clear(
|
|
&task->mutex
|
|
);
|
|
|
|
g_free(
|
|
task
|
|
);
|
|
}
|
|
|
|
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
|
|
)
|
|
{
|
|
BackgroundTaskRunContext *run_context =
|
|
NULL;
|
|
|
|
GCancellable *cancellable = NULL;
|
|
GTask *async_task = NULL;
|
|
|
|
g_return_val_if_fail(
|
|
error == NULL || *error == NULL,
|
|
FALSE
|
|
);
|
|
|
|
if (task == NULL ||
|
|
worker == NULL)
|
|
{
|
|
g_set_error_literal(
|
|
error,
|
|
BACKGROUND_TASK_ERROR,
|
|
BACKGROUND_TASK_ERROR_INVALID_ARGUMENT,
|
|
"La tâche ou son worker est invalide."
|
|
);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Le GCancellable est préparé avant le verrouillage.
|
|
*/
|
|
cancellable = g_cancellable_new();
|
|
|
|
g_mutex_lock(
|
|
&task->mutex
|
|
);
|
|
|
|
if (task->state !=
|
|
BACKGROUND_TASK_STATE_PENDING)
|
|
{
|
|
g_mutex_unlock(
|
|
&task->mutex
|
|
);
|
|
|
|
g_object_unref(
|
|
cancellable
|
|
);
|
|
|
|
g_set_error_literal(
|
|
error,
|
|
BACKGROUND_TASK_ERROR,
|
|
BACKGROUND_TASK_ERROR_ALREADY_STARTED,
|
|
"Cette tâche a déjà été démarrée."
|
|
);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
task->state =
|
|
BACKGROUND_TASK_STATE_RUNNING;
|
|
|
|
task->progress = 0.0;
|
|
|
|
task->started_at_us =
|
|
g_get_monotonic_time();
|
|
|
|
task->finished_at_us = 0;
|
|
|
|
task->cancellable = g_object_ref(
|
|
cancellable
|
|
);
|
|
|
|
task->result_destroy =
|
|
result_destroy;
|
|
|
|
task->completion_callback =
|
|
completion_callback;
|
|
|
|
task->completion_data =
|
|
completion_data;
|
|
|
|
task->completion_data_destroy =
|
|
completion_data_destroy;
|
|
|
|
g_mutex_unlock(
|
|
&task->mutex
|
|
);
|
|
|
|
run_context = g_new0(
|
|
BackgroundTaskRunContext,
|
|
1
|
|
);
|
|
|
|
/*
|
|
* Cette référence interne protège la tâche même si l'appelant
|
|
* libère immédiatement sa propre référence.
|
|
*/
|
|
run_context->task =
|
|
background_task_ref(task);
|
|
|
|
run_context->worker = worker;
|
|
|
|
run_context->worker_data =
|
|
worker_data;
|
|
|
|
run_context->worker_data_destroy =
|
|
worker_data_destroy;
|
|
|
|
run_context->result_destroy =
|
|
result_destroy;
|
|
|
|
async_task = g_task_new(
|
|
NULL,
|
|
cancellable,
|
|
background_task_on_completed,
|
|
task
|
|
);
|
|
|
|
g_task_set_task_data(
|
|
async_task,
|
|
run_context,
|
|
background_task_run_context_free
|
|
);
|
|
|
|
g_task_set_check_cancellable(
|
|
async_task,
|
|
TRUE
|
|
);
|
|
|
|
g_task_run_in_thread(
|
|
async_task,
|
|
background_task_run_in_thread
|
|
);
|
|
|
|
/*
|
|
* GTask conserve sa propre référence pendant l'exécution.
|
|
*/
|
|
g_object_unref(
|
|
async_task
|
|
);
|
|
|
|
g_object_unref(
|
|
cancellable
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void background_task_cancel(
|
|
BackgroundTask *task
|
|
)
|
|
{
|
|
GCancellable *cancellable = NULL;
|
|
|
|
if (task == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
g_mutex_lock(
|
|
&task->mutex
|
|
);
|
|
|
|
if (task->state ==
|
|
BACKGROUND_TASK_STATE_RUNNING &&
|
|
task->cancellable != NULL)
|
|
{
|
|
cancellable = g_object_ref(
|
|
task->cancellable
|
|
);
|
|
}
|
|
|
|
g_mutex_unlock(
|
|
&task->mutex
|
|
);
|
|
|
|
/*
|
|
* L'annulation est effectuée hors du mutex.
|
|
*
|
|
* GCancellable peut réveiller des callbacks ou des opérations
|
|
* bloquantes. Il ne faut donc pas garder le verrou.
|
|
*/
|
|
if (cancellable != NULL)
|
|
{
|
|
g_cancellable_cancel(
|
|
cancellable
|
|
);
|
|
|
|
g_object_unref(
|
|
cancellable
|
|
);
|
|
}
|
|
}
|
|
|
|
gboolean background_task_is_cancelled(
|
|
const BackgroundTask *task
|
|
)
|
|
{
|
|
BackgroundTask *mutable_task = NULL;
|
|
GCancellable *cancellable = NULL;
|
|
|
|
BackgroundTaskState state;
|
|
gboolean is_cancelled = FALSE;
|
|
|
|
if (task == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
mutable_task = (BackgroundTask *) task;
|
|
|
|
g_mutex_lock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
state = mutable_task->state;
|
|
|
|
if (state ==
|
|
BACKGROUND_TASK_STATE_CANCELLED)
|
|
{
|
|
is_cancelled = TRUE;
|
|
}
|
|
else if (state ==
|
|
BACKGROUND_TASK_STATE_RUNNING &&
|
|
mutable_task->cancellable != NULL)
|
|
{
|
|
cancellable = g_object_ref(
|
|
mutable_task->cancellable
|
|
);
|
|
}
|
|
|
|
g_mutex_unlock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
if (cancellable != NULL)
|
|
{
|
|
is_cancelled = g_cancellable_is_cancelled(
|
|
cancellable
|
|
);
|
|
|
|
g_object_unref(
|
|
cancellable
|
|
);
|
|
}
|
|
|
|
return is_cancelled;
|
|
}
|
|
|
|
void background_task_report_progress(
|
|
BackgroundTask *task,
|
|
double progress,
|
|
const char *status_message
|
|
)
|
|
{
|
|
char *new_status_message = NULL;
|
|
|
|
if (task == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Une valeur NaN ne doit jamais être conservée.
|
|
*/
|
|
if (progress != progress)
|
|
{
|
|
progress = 0.0;
|
|
}
|
|
else if (progress < 0.0)
|
|
{
|
|
progress = 0.0;
|
|
}
|
|
else if (progress > 1.0)
|
|
{
|
|
progress = 1.0;
|
|
}
|
|
|
|
if (status_message != NULL)
|
|
{
|
|
new_status_message = g_strdup(
|
|
status_message
|
|
);
|
|
}
|
|
|
|
g_mutex_lock(
|
|
&task->mutex
|
|
);
|
|
|
|
if (task->state !=
|
|
BACKGROUND_TASK_STATE_RUNNING)
|
|
{
|
|
g_mutex_unlock(
|
|
&task->mutex
|
|
);
|
|
|
|
g_free(
|
|
new_status_message
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
task->progress = progress;
|
|
|
|
g_free(
|
|
task->status_message
|
|
);
|
|
|
|
task->status_message =
|
|
new_status_message;
|
|
|
|
g_mutex_unlock(
|
|
&task->mutex
|
|
);
|
|
}
|
|
|
|
const char *background_task_get_title(
|
|
const BackgroundTask *task
|
|
)
|
|
{
|
|
if (task == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Le titre est immuable après la construction.
|
|
*/
|
|
return task->title;
|
|
}
|
|
|
|
BackgroundTaskState background_task_get_state(
|
|
const BackgroundTask *task
|
|
)
|
|
{
|
|
BackgroundTask *mutable_task = NULL;
|
|
BackgroundTaskState state;
|
|
|
|
if (task == NULL)
|
|
{
|
|
return BACKGROUND_TASK_STATE_PENDING;
|
|
}
|
|
|
|
mutable_task = (BackgroundTask *) task;
|
|
|
|
g_mutex_lock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
state = mutable_task->state;
|
|
|
|
g_mutex_unlock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
return state;
|
|
}
|
|
|
|
double background_task_get_progress(
|
|
const BackgroundTask *task
|
|
)
|
|
{
|
|
BackgroundTask *mutable_task = NULL;
|
|
double progress = 0.0;
|
|
|
|
if (task == NULL)
|
|
{
|
|
return 0.0;
|
|
}
|
|
|
|
mutable_task = (BackgroundTask *) task;
|
|
|
|
g_mutex_lock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
progress = mutable_task->progress;
|
|
|
|
g_mutex_unlock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
return progress;
|
|
}
|
|
|
|
char *background_task_dup_status_message(
|
|
const BackgroundTask *task
|
|
)
|
|
{
|
|
BackgroundTask *mutable_task = NULL;
|
|
char *status_message = NULL;
|
|
|
|
if (task == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
mutable_task = (BackgroundTask *) task;
|
|
|
|
g_mutex_lock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
status_message = g_strdup(
|
|
mutable_task->status_message
|
|
);
|
|
|
|
g_mutex_unlock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
return status_message;
|
|
}
|
|
|
|
gint64 background_task_get_started_at_us(
|
|
const BackgroundTask *task
|
|
)
|
|
{
|
|
BackgroundTask *mutable_task = NULL;
|
|
gint64 started_at_us = 0;
|
|
|
|
if (task == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
mutable_task = (BackgroundTask *) task;
|
|
|
|
g_mutex_lock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
started_at_us =
|
|
mutable_task->started_at_us;
|
|
|
|
g_mutex_unlock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
return started_at_us;
|
|
}
|
|
|
|
gint64 background_task_get_finished_at_us(
|
|
const BackgroundTask *task
|
|
)
|
|
{
|
|
BackgroundTask *mutable_task = NULL;
|
|
gint64 finished_at_us = 0;
|
|
|
|
if (task == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
mutable_task = (BackgroundTask *) task;
|
|
|
|
g_mutex_lock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
finished_at_us =
|
|
mutable_task->finished_at_us;
|
|
|
|
g_mutex_unlock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
return finished_at_us;
|
|
}
|
|
|
|
GError *background_task_dup_error(
|
|
const BackgroundTask *task
|
|
)
|
|
{
|
|
BackgroundTask *mutable_task = NULL;
|
|
GError *error = NULL;
|
|
|
|
if (task == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
mutable_task = (BackgroundTask *) task;
|
|
|
|
g_mutex_lock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
if (mutable_task->error != NULL)
|
|
{
|
|
error = g_error_copy(
|
|
mutable_task->error
|
|
);
|
|
}
|
|
|
|
g_mutex_unlock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
return error;
|
|
}
|
|
|
|
gpointer background_task_get_result(
|
|
const BackgroundTask *task
|
|
)
|
|
{
|
|
BackgroundTask *mutable_task = NULL;
|
|
gpointer result = NULL;
|
|
|
|
if (task == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
mutable_task = (BackgroundTask *) task;
|
|
|
|
g_mutex_lock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
if (mutable_task->state ==
|
|
BACKGROUND_TASK_STATE_COMPLETED)
|
|
{
|
|
result = mutable_task->result;
|
|
}
|
|
|
|
g_mutex_unlock(
|
|
&mutable_task->mutex
|
|
);
|
|
|
|
return result;
|
|
}
|