mirror of
https://gitlab.gnome.org/GNOME/eog
synced 2024-10-19 14:34:42 +00:00
52a1664049
2006-07-19 Claudio Saavedra <csaavedra@alumnos.utalca.cl> * libeog/eog-image-private.h: * libeog/eog-image.c: (eog_image_dispose), (eog_image_init), (eog_image_lock), (eog_image_unlock), (eog_image_cancel_load): * libeog/eog-image.h: * libeog/eog-job-manager.c: (thread_start_func): * libeog/eog-job.c: (eog_job_call_action): * shell/eog-window.c: (eog_window_init), (job_image_load_action), (job_image_load_finished), (handle_image_selection_changed): Add image_lock/unlock methods to ensure that there are no race conditions when loading and unloading images (Fixes bug #340827). Patch from Callum McKenzie <callum@spooky-possum.org>.
453 lines
9.6 KiB
C
453 lines
9.6 KiB
C
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <libgnome/gnome-macros.h>
|
|
|
|
#include "eog-job.h"
|
|
|
|
static guint last_job_id = 0;
|
|
#define DEBUG_EOG_JOB 0
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_PROGRESS_THRESHOLD,
|
|
PROP_PROGRESS_N_PARTS,
|
|
PROP_PRIORITY
|
|
};
|
|
|
|
struct _EogJobPrivate {
|
|
GMutex *mutex;
|
|
guint id;
|
|
gpointer data;
|
|
EogJobStatus status;
|
|
GError *error;
|
|
float progress;
|
|
float progress_last_called;
|
|
guint progress_idle_id;
|
|
guint progress_nth_part;
|
|
guint progress_n_parts;
|
|
float progress_threshold;
|
|
guint priority;
|
|
|
|
EogJobActionFunc af;
|
|
EogJobCancelFunc cf;
|
|
EogJobFinishedFunc ff;
|
|
EogJobProgressFunc pf;
|
|
EogJobFreeDataFunc df;
|
|
};
|
|
|
|
static void
|
|
eog_job_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
EogJob *job;
|
|
|
|
g_return_if_fail (EOG_IS_JOB (object));
|
|
|
|
job = EOG_JOB (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_PROGRESS_THRESHOLD:
|
|
job->priv->progress_threshold =
|
|
g_value_get_float (value);
|
|
break;
|
|
case PROP_PROGRESS_N_PARTS:
|
|
job->priv->progress_n_parts =
|
|
g_value_get_uint (value);
|
|
break;
|
|
case PROP_PRIORITY:
|
|
job->priv->priority =
|
|
g_value_get_uint (value);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
eog_job_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
EogJob *job;
|
|
|
|
g_return_if_fail (EOG_IS_JOB (object));
|
|
|
|
job = EOG_JOB (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_PROGRESS_THRESHOLD:
|
|
g_value_set_float (value,
|
|
job->priv->progress_threshold);
|
|
break;
|
|
case PROP_PROGRESS_N_PARTS:
|
|
g_value_set_uint (value,
|
|
job->priv->progress_n_parts);
|
|
break;
|
|
case PROP_PRIORITY:
|
|
g_value_set_uint (value,
|
|
job->priv->priority);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
eog_job_finalize (GObject *object)
|
|
{
|
|
EogJob *instance = EOG_JOB (object);
|
|
|
|
g_free (instance->priv);
|
|
instance->priv = NULL;
|
|
}
|
|
|
|
static void
|
|
eog_job_dispose (GObject *object)
|
|
{
|
|
EogJobPrivate *priv;
|
|
|
|
priv = EOG_JOB (object)->priv;
|
|
|
|
#if DEBUG_EOG_JOB
|
|
g_print ("Job %.3u: disposing ...\n", priv->id);
|
|
#endif
|
|
|
|
if (priv->mutex != NULL) {
|
|
g_mutex_lock (priv->mutex);
|
|
if (priv->data != NULL)
|
|
{
|
|
if (priv->df != NULL) {
|
|
(*priv->df) (priv->data);
|
|
}
|
|
else if (G_IS_OBJECT (priv->data))
|
|
g_object_unref (G_OBJECT (priv->data));
|
|
|
|
priv->data = NULL;
|
|
}
|
|
g_mutex_unlock (priv->mutex);
|
|
|
|
g_mutex_free (priv->mutex);
|
|
priv->mutex = NULL;
|
|
}
|
|
|
|
#if DEBUG_EOG_JOB
|
|
g_print ("Job %.3u: disposing end\n", priv->id);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
eog_job_init (EogJob *obj)
|
|
{
|
|
EogJobPrivate *priv;
|
|
guint id;
|
|
|
|
id = ++last_job_id;
|
|
if (id == 0) { /* in overflow case */
|
|
++last_job_id;
|
|
++id;
|
|
}
|
|
|
|
#if DEBUG_EOG_JOB
|
|
g_print ("Instantiate job with id %u.\n", id);
|
|
#endif
|
|
|
|
priv = g_new0 (EogJobPrivate, 1);
|
|
priv->id = id;
|
|
priv->mutex = g_mutex_new ();
|
|
priv->error = NULL;
|
|
priv->data = NULL;
|
|
priv->af = NULL;
|
|
priv->cf = NULL;
|
|
priv->ff = NULL;
|
|
priv->pf = NULL;
|
|
priv->df = NULL;
|
|
priv->progress = 0.0;
|
|
priv->progress_idle_id = 0;
|
|
priv->progress_threshold = 0.1;
|
|
priv->progress_n_parts = 1;
|
|
priv->priority = EOG_JOB_PRIORITY_NORMAL;
|
|
|
|
obj->priv = priv;
|
|
}
|
|
|
|
static void
|
|
eog_job_class_init (EogJobClass *klass)
|
|
{
|
|
GObjectClass *object_class = (GObjectClass*) klass;
|
|
|
|
object_class->finalize = eog_job_finalize;
|
|
object_class->dispose = eog_job_dispose;
|
|
object_class->set_property = eog_job_set_property;
|
|
object_class->get_property = eog_job_get_property;
|
|
|
|
/* Properties */
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_PROGRESS_THRESHOLD,
|
|
g_param_spec_float ("progress-threshold", NULL,
|
|
"Difference threshold between two progress values"
|
|
"before calling progress callback function again",
|
|
0.0, 1.0, 0.1,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_PROGRESS_N_PARTS,
|
|
g_param_spec_uint ("progress-n-parts", NULL,
|
|
"Number of parts for this progress so that "
|
|
"the total progress is the sum of progress "
|
|
"for each part devided through number of parts.",
|
|
0, G_MAXINT, 1,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_PRIORITY,
|
|
g_param_spec_uint ("priority", NULL,
|
|
"Priority of the job.",
|
|
0, G_MAXINT, 1,
|
|
G_PARAM_WRITABLE));
|
|
}
|
|
|
|
|
|
G_DEFINE_TYPE (EogJob, eog_job, G_TYPE_OBJECT)
|
|
|
|
EogJob*
|
|
eog_job_new (GObject *data_obj,
|
|
EogJobActionFunc af,
|
|
EogJobFinishedFunc ff,
|
|
EogJobCancelFunc cf,
|
|
EogJobProgressFunc pf)
|
|
{
|
|
g_return_val_if_fail (G_IS_OBJECT (data_obj), NULL);
|
|
|
|
return eog_job_new_full ((gpointer) data_obj, af, ff, cf, pf, NULL);
|
|
}
|
|
|
|
/* called from main thread */
|
|
EogJob*
|
|
eog_job_new_full (gpointer data,
|
|
EogJobActionFunc af,
|
|
EogJobFinishedFunc ff,
|
|
EogJobCancelFunc cf,
|
|
EogJobProgressFunc pf,
|
|
EogJobFreeDataFunc df)
|
|
{
|
|
EogJob *job;
|
|
EogJobPrivate *priv;
|
|
|
|
g_return_val_if_fail (af != NULL, NULL);
|
|
|
|
job = g_object_new (EOG_TYPE_JOB, NULL);
|
|
priv = job->priv;
|
|
|
|
if (G_IS_OBJECT (data)) {
|
|
g_object_ref (G_OBJECT (data));
|
|
}
|
|
priv->data = data;
|
|
priv->status = EOG_JOB_STATUS_WAITING;
|
|
priv->af = af;
|
|
priv->ff = ff;
|
|
priv->cf = cf;
|
|
priv->pf = pf;
|
|
priv->df = df;
|
|
|
|
return job;
|
|
}
|
|
|
|
/* called from main thread */
|
|
EogJobStatus
|
|
eog_job_get_status (EogJob *job)
|
|
{
|
|
g_return_val_if_fail (EOG_IS_JOB (job), EOG_JOB_STATUS_ERROR);
|
|
|
|
return job->priv->status;
|
|
}
|
|
|
|
/* called from main thread */
|
|
guint
|
|
eog_job_get_id (EogJob *job)
|
|
{
|
|
g_return_val_if_fail (EOG_IS_JOB (job), 0);
|
|
|
|
return job->priv->id;
|
|
}
|
|
|
|
/* called from main thread */
|
|
gboolean
|
|
eog_job_get_success (EogJob *job)
|
|
{
|
|
gboolean success;
|
|
|
|
g_return_val_if_fail (EOG_IS_JOB (job), FALSE);
|
|
|
|
g_mutex_lock (job->priv->mutex);
|
|
|
|
success = ((job->priv->status == EOG_JOB_STATUS_FINISHED) &&
|
|
(job->priv->error == NULL));
|
|
|
|
g_mutex_unlock (job->priv->mutex);
|
|
|
|
return success;
|
|
}
|
|
|
|
void
|
|
eog_job_part_finished (EogJob *job)
|
|
{
|
|
g_return_if_fail (EOG_IS_JOB (job));
|
|
|
|
job->priv->progress_nth_part =
|
|
MIN ((job->priv->progress_nth_part + 1),
|
|
job->priv->progress_n_parts);
|
|
}
|
|
|
|
/* private func, called within main thread from idle loop */
|
|
static gboolean
|
|
eog_job_call_progress (gpointer data)
|
|
{
|
|
EogJob *job = EOG_JOB (data);
|
|
|
|
g_assert (job->priv->pf != NULL);
|
|
|
|
g_mutex_lock (job->priv->mutex);
|
|
job->priv->progress_idle_id = 0;
|
|
g_mutex_unlock (job->priv->mutex);
|
|
|
|
/* call progress callback */
|
|
(*job->priv->pf) (job, job->priv->data, job->priv->progress);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* called from concurrent thread */
|
|
void
|
|
eog_job_set_progress (EogJob *job, float progress)
|
|
{
|
|
EogJobPrivate *priv;
|
|
|
|
g_return_if_fail (EOG_IS_JOB (job));
|
|
|
|
priv = job->priv;
|
|
|
|
g_mutex_lock (job->priv->mutex);
|
|
/* calculate new progress */
|
|
priv->progress = ((float) priv->progress_nth_part + progress) /
|
|
priv->progress_n_parts;
|
|
|
|
/* check if all preconditions are met for calling progress callback */
|
|
if ((priv->pf != NULL) &&
|
|
(priv->progress_idle_id == 0) &&
|
|
(priv->progress == 1.0 ||
|
|
(priv->progress - priv->progress_last_called) >= priv->progress_threshold))
|
|
{
|
|
priv->progress_last_called = priv->progress;
|
|
priv->progress_idle_id = g_idle_add (eog_job_call_progress, job);
|
|
}
|
|
g_mutex_unlock (job->priv->mutex);
|
|
}
|
|
|
|
/* this runs in its own thread
|
|
* called by EogJobManager
|
|
*/
|
|
void
|
|
eog_job_call_action (EogJob *job)
|
|
{
|
|
gboolean do_action = TRUE;
|
|
|
|
g_return_if_fail (EOG_IS_JOB (job));
|
|
|
|
g_mutex_lock (job->priv->mutex);
|
|
if (job->priv->status != EOG_JOB_STATUS_WAITING)
|
|
do_action = FALSE;
|
|
|
|
if (job->priv->af == NULL) {
|
|
job->priv->status = EOG_JOB_STATUS_ERROR;
|
|
do_action = FALSE;
|
|
}
|
|
|
|
if (do_action) {
|
|
job->priv->status = EOG_JOB_STATUS_RUNNING;
|
|
job->priv->progress = 0.0;
|
|
job->priv->progress_last_called = 0.0;
|
|
job->priv->progress_nth_part = 0;
|
|
}
|
|
|
|
g_mutex_unlock (job->priv->mutex);
|
|
|
|
if (!do_action) {
|
|
return;
|
|
}
|
|
|
|
/* do the actual work */
|
|
(*job->priv->af) (job, job->priv->data, &job->priv->error);
|
|
|
|
g_mutex_lock (job->priv->mutex);
|
|
if (job->priv->status != EOG_JOB_STATUS_CANCELED)
|
|
job->priv->status = EOG_JOB_STATUS_FINISHED;
|
|
g_mutex_unlock (job->priv->mutex);
|
|
}
|
|
|
|
/* called within main thread from idle loop
|
|
* by EogJobManager
|
|
*/
|
|
gboolean
|
|
eog_job_call_canceled (EogJob *job)
|
|
{
|
|
gboolean do_cancel = FALSE;
|
|
gboolean valid_state = FALSE;
|
|
|
|
g_return_val_if_fail (EOG_IS_JOB (job), FALSE);
|
|
|
|
g_mutex_lock (job->priv->mutex);
|
|
valid_state = (job->priv->status == EOG_JOB_STATUS_WAITING ||
|
|
job->priv->status == EOG_JOB_STATUS_RUNNING);
|
|
do_cancel = (job->priv->cf != NULL && valid_state);
|
|
if (valid_state) {
|
|
job->priv->status = EOG_JOB_STATUS_CANCELED;
|
|
}
|
|
g_mutex_unlock (job->priv->mutex);
|
|
|
|
if (do_cancel) {
|
|
/* call cancel function */
|
|
(*job->priv->cf) (job, job->priv->data);
|
|
}
|
|
|
|
return valid_state;
|
|
}
|
|
|
|
/* called within main thread from idle loop
|
|
* by EogJobManager
|
|
*/
|
|
void
|
|
eog_job_call_finished (EogJob *job)
|
|
{
|
|
gboolean call_finished = FALSE;
|
|
|
|
g_return_if_fail (EOG_IS_JOB (job));
|
|
|
|
g_mutex_lock (job->priv->mutex);
|
|
g_assert (job->priv->status == EOG_JOB_STATUS_FINISHED ||
|
|
job->priv->status == EOG_JOB_STATUS_CANCELED);
|
|
|
|
call_finished = (job->priv->ff != NULL);
|
|
g_mutex_unlock (job->priv->mutex);
|
|
|
|
if (call_finished) {
|
|
/* call finished callback */
|
|
(*job->priv->ff) (job, job->priv->data, job->priv->error);
|
|
}
|
|
}
|
|
|
|
EogJobPriority
|
|
eog_job_get_priority (EogJob *job)
|
|
{
|
|
g_return_val_if_fail (EOG_IS_JOB (job), EOG_JOB_PRIORITY_NORMAL);
|
|
|
|
return job->priv->priority;
|
|
}
|