[Rawstudio-commit] r2221 - trunk/librawstudio

Anders Brander anders at brander.dk
Tue Feb 24 03:58:03 CET 2009


Author: abrander
Date: 2009-02-24 03:58:03 +0100 (Tue, 24 Feb 2009)
New Revision: 2221

Added:
   trunk/librawstudio/rs-job-queue.c
   trunk/librawstudio/rs-job-queue.h
Modified:
   trunk/librawstudio/Makefile.am
   trunk/librawstudio/rawstudio.h
Log:
Added RSJobQueue.

Modified: trunk/librawstudio/Makefile.am
===================================================================
--- trunk/librawstudio/Makefile.am	2009-02-23 00:22:15 UTC (rev 2220)
+++ trunk/librawstudio/Makefile.am	2009-02-24 02:58:03 UTC (rev 2221)
@@ -18,6 +18,7 @@
 	rs-filter.h \
 	rs-output.h \
 	rs-plugin-manager.h \
+	rs-job-queue.h \
 	rs-utils.h \
 	rs-math.h \
 	rs-settings.h \
@@ -39,6 +40,7 @@
 	rs-filter.c rs-filter.h \
 	rs-output.c rs-output.h \
 	rs-plugin-manager.c rs-plugin-manager.h \
+	rs-job-queue.c rs-job-queue.h \
 	rs-utils.c rs-utils.h \
 	rs-math.c rs-math.h \
 	rs-settings.c rs-settings.h \

Modified: trunk/librawstudio/rawstudio.h
===================================================================
--- trunk/librawstudio/rawstudio.h	2009-02-23 00:22:15 UTC (rev 2220)
+++ trunk/librawstudio/rawstudio.h	2009-02-24 02:58:03 UTC (rev 2221)
@@ -39,6 +39,7 @@
 #include "rs-filter.h"
 #include "rs-output.h"
 #include "rs-plugin-manager.h"
+#include "rs-job-queue.h"
 #include "rs-utils.h"
 #include "rs-settings.h"
 #include "rs-adobe-coeff.h"

Added: trunk/librawstudio/rs-job-queue.c
===================================================================
--- trunk/librawstudio/rs-job-queue.c	                        (rev 0)
+++ trunk/librawstudio/rs-job-queue.c	2009-02-24 02:58:03 UTC (rev 2221)
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2006-2009 Anders Brander <anders at brander.dk> and 
+ * Anders Kvist <akv at lnxbx.dk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include <rawstudio.h>
+#include "rs-job-queue.h"
+
+struct _RSJobQueueSlot {
+	GtkWidget *container;
+	GtkWidget *label;
+	GtkWidget *progress;
+};
+
+struct _RSJob {
+	RSJobFunc func;
+	RSJobQueue *job_queue;
+	RSJobQueueSlot *slot;
+	gpointer data;
+	gpointer result;
+	gboolean done;
+	GCond *done_cond;
+	GMutex *done_mutex;
+};
+
+struct _RSJobQueue {
+	GObject parent;
+	gboolean dispose_has_run;
+
+	GMutex *lock;
+	GThreadPool *pool;
+	gint n_slots;
+	GtkWidget *window;
+	GtkWidget *box;
+};
+
+G_DEFINE_TYPE (RSJobQueue, rs_job_queue, G_TYPE_OBJECT)
+
+static void job_consumer(gpointer data, gpointer unused);
+
+static void
+rs_job_queue_dispose (GObject *object)
+{
+	RSJobQueue *job_queue = RS_JOB_QUEUE(object);
+
+	if (!job_queue->dispose_has_run)
+	{
+		job_queue->dispose_has_run = TRUE;
+
+		g_mutex_free(job_queue->lock);
+	}
+
+	/* Chain up */
+	G_OBJECT_CLASS(rs_job_queue_parent_class)->dispose(object);
+}
+
+static void
+rs_job_queue_class_init (RSJobQueueClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->dispose = rs_job_queue_dispose;
+}
+
+static void
+rs_job_queue_init(RSJobQueue *job_queue)
+{
+	job_queue->dispose_has_run = FALSE;
+	job_queue->lock = g_mutex_new();
+	job_queue->pool = g_thread_pool_new(((GFunc) job_consumer), NULL, rs_get_number_of_processor_cores(), TRUE, NULL);
+	job_queue->n_slots = 0;
+	job_queue->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	job_queue->box = gtk_vbox_new(TRUE, 1);
+
+	gtk_container_add(GTK_CONTAINER(job_queue->window), job_queue->box);
+
+	gtk_window_set_deletable(GTK_WINDOW(job_queue->window), FALSE);
+	gtk_window_set_accept_focus(GTK_WINDOW(job_queue->window), FALSE);
+	gtk_window_set_keep_above(GTK_WINDOW(job_queue->window), TRUE);
+	gtk_window_set_skip_pager_hint(GTK_WINDOW(job_queue->window), TRUE);
+	gtk_window_set_skip_taskbar_hint(GTK_WINDOW(job_queue->window), TRUE);
+	gtk_window_set_title(GTK_WINDOW(job_queue->window), "");
+	gtk_window_set_type_hint(GTK_WINDOW(job_queue->window), GDK_WINDOW_TYPE_HINT_UTILITY);
+
+	/* Let's spice it up a notch! :) */
+#if GTK_CHECK_VERSION(2,12,0)
+	gtk_window_set_opacity(GTK_WINDOW(job_queue->window), 0.85);
+#endif
+
+	/* Set the gravity, so that resizes will still result in a window
+	 * positioned in the lower left */
+	gtk_window_set_gravity(GTK_WINDOW(job_queue->window), GDK_GRAVITY_SOUTH_EAST);
+
+	/* Place the window in lower left corner of screen */
+	gtk_window_move(GTK_WINDOW(job_queue->window), 0, gdk_screen_get_height(gdk_display_get_default_screen(gdk_display_get_default())));
+}
+
+/**
+ * Get a new RSJobQueue
+ * @return A new RSJobQueue
+ */
+static RSJobQueue *
+rs_job_queue_new(void)
+{
+	return g_object_new (RS_TYPE_JOB_QUEUE, NULL);
+}
+
+/**
+ * Return the RSJobQueue singleton
+ * @note THis function should be thread safe
+ * @return A RSJobQueue singleton
+ */
+static RSJobQueue *
+rs_job_queue_get_singleton()
+{
+	static RSJobQueue *singleton = NULL;
+	GStaticMutex lock = G_STATIC_MUTEX_INIT;
+
+	g_static_mutex_lock(&lock);
+	if (!singleton)
+		singleton = rs_job_queue_new();
+	g_static_mutex_unlock(&lock);
+
+	g_assert(RS_IS_JOB_QUEUE(singleton));
+
+	return singleton;
+}
+
+/**
+ * Add a new processing slot to a RSJobQueue window
+ * @param job_queue A RSJobQueue
+ * @return A new RSJobQueueSlot
+ */
+static RSJobQueueSlot *
+rs_job_queue_add_slot(RSJobQueue *job_queue)
+{
+	RSJobQueueSlot *slot = g_new0(RSJobQueueSlot, 1);
+
+	g_mutex_lock(job_queue->lock);
+	gdk_threads_enter();
+
+	slot->container = gtk_vbox_new(FALSE, 0);
+	slot->progress = gtk_progress_bar_new();
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(slot->progress), "Hello...");
+	gtk_box_pack_start(GTK_BOX(slot->container), slot->progress, FALSE, TRUE, 1);
+
+	gtk_box_pack_start(GTK_BOX(job_queue->box), slot->container, FALSE, TRUE, 1);
+
+	/* If we previously got 0 slots open, position the window again */
+	if (job_queue->n_slots == 0)
+		gtk_window_move(GTK_WINDOW(job_queue->window), 0, gdk_screen_get_height(gdk_display_get_default_screen(gdk_display_get_default())));
+
+	/* For some reason this must be called everytime to trigger correct placement?! */
+	gtk_widget_show_all(job_queue->window);
+
+	job_queue->n_slots++;
+
+	gdk_threads_leave();
+	g_mutex_unlock(job_queue->lock);
+
+	return slot;
+}
+
+/**
+ * Remove and frees a RSJobQueueSlot from a RSJobQueue window
+ * @param job_queue A RSJobQueue
+ * @param slot The slot to remove and free
+ */
+static void
+rs_job_queue_remove_slot(RSJobQueue *job_queue, RSJobQueueSlot *slot)
+{
+	g_mutex_lock(job_queue->lock);
+	gdk_threads_enter();
+
+	gtk_container_remove(GTK_CONTAINER(job_queue->box), slot->container);
+	job_queue->n_slots--;
+
+	/* If we got less than 1 slot left, we hide the window, no reason to
+	 * show an empty window */
+	if (job_queue->n_slots < 1)
+		gtk_widget_hide_all(job_queue->window);
+
+	/* We resize the window to 1,1 to make it as small as _possible_ to
+	 * avoid blank space after removing a slot */
+	gtk_window_resize(GTK_WINDOW(job_queue->window), 1, 1);
+
+	gdk_threads_leave();
+	g_mutex_unlock(job_queue->lock);
+}
+
+/**
+ * A function to consume jobs, this should run in its own thread
+ * @note Will never return
+ */
+static void
+job_consumer(gpointer data, gpointer unused)
+{
+	gpointer result;
+
+	RSJob *job = (RSJob *) data;
+	RSJobQueueSlot *slot = rs_job_queue_add_slot(job->job_queue);
+	
+	/* Call the job */
+	if (!job->done)
+		job->result = job->func(slot, job->data);
+
+	rs_job_queue_remove_slot(job->job_queue, slot);
+	g_object_unref(job->job_queue);
+
+	if (job->done_cond)
+	{
+		/* If we take this path, we shouldn't free the job, rs_job_queue_wait()
+		 * must take care of that */
+		g_mutex_lock(job->done_mutex);
+		job->done = TRUE;
+		g_cond_signal(job->done_cond);
+		g_mutex_unlock(job->done_mutex);
+	}
+	else
+		g_free(job);
+}
+
+/**
+ * Add a new job to the job queue
+ * @note When func is called, it WILL be from another thread, it may be
+ *       required to acquire the GDK lock if any GTK+ stuff is done in the
+ *       callback!
+ * @param func A function to call for performing the job
+ * @param data Data to pass to func
+ * @param waitable If TRUE, rs_job_queue_wait() will wait until completion
+ */
+RSJob *
+rs_job_queue_add_job(RSJobFunc func, gpointer data, gboolean waitable)
+{
+	RSJobQueue *job_queue = rs_job_queue_get_singleton();
+
+    g_assert(func != NULL);
+
+	g_mutex_lock(job_queue->lock);
+
+	RSJob *job = g_new0(RSJob, 1);
+    job->func = func;
+	job->job_queue = g_object_ref(job_queue);
+    job->data = data;
+    job->done = FALSE;
+
+	if (waitable)
+    {
+        job->done_cond = g_cond_new();
+        job->done_mutex = g_mutex_new();
+    }
+    else
+    {
+        job->done_cond = NULL;
+        job->done_mutex = NULL;
+    }
+
+    g_thread_pool_push(job_queue->pool, job, NULL);
+	g_mutex_unlock(job_queue->lock);
+}
+
+/**
+ * Wait (hang) until a job is finished and then free the memory allocated to job
+ * @param job The RSJob to wait for
+ * @return The value returned by the func given to rs_job_queue_add()
+ */
+gpointer
+rs_job_queue_wait(RSJob *job)
+{
+	gpointer result = NULL;
+
+	g_assert(job != NULL);
+	g_assert(job->done_cond != NULL);
+	g_assert(job->done_mutex != NULL);
+
+	/* Wait for it */
+	g_mutex_lock(job->done_mutex);
+	while(!job->done)
+		g_cond_wait(job->done_cond, job->done_mutex);
+	g_mutex_unlock(job->done_mutex);
+
+	/* Free everything */
+	g_cond_free(job->done_cond);
+	g_mutex_free(job->done_mutex);
+	g_free(job);
+
+	result = job->result;
+
+	return result;
+}
+
+/**
+ * Update the job description
+ * @note You should NOT have aquired the GDK thread lock when calling this
+ *       function.
+ * @param slot A job_slot as recieved in the job callback function
+ * @param description The new description or NULL to show nothing
+ */
+void
+rs_job_update_description(RSJobQueueSlot *slot, const gchar *description)
+{
+	gdk_threads_enter();
+
+	if (description)
+		gtk_progress_bar_set_text(GTK_PROGRESS_BAR(slot->progress), description);
+	else
+		gtk_progress_bar_set_text(GTK_PROGRESS_BAR(slot->progress), "");
+
+	gdk_threads_leave();
+}
+
+/**
+ * Update the job progress bar
+ * @note You should NOT have aquired the GDK thread lock when calling this
+ *       function.
+ * @param slot A job_slot as recieved in the job callback function
+ * @param fraction A value between 0.0 and 1.0 to set the progress bar at
+ *                 the specific fraction or -1.0 to pulse the progress bar.
+ */
+void
+rs_job_update_progress(RSJobQueueSlot *slot, const gdouble fraction)
+{
+	gdk_threads_enter();
+
+	if (fraction < 0.0)
+		gtk_progress_bar_pulse(GTK_PROGRESS_BAR(slot->progress));
+	else
+		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(slot->progress), fraction);
+
+	gdk_threads_leave();
+}

Added: trunk/librawstudio/rs-job-queue.h
===================================================================
--- trunk/librawstudio/rs-job-queue.h	                        (rev 0)
+++ trunk/librawstudio/rs-job-queue.h	2009-02-24 02:58:03 UTC (rev 2221)
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2006-2009 Anders Brander <anders at brander.dk> and 
+ * Anders Kvist <akv at lnxbx.dk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef RS_JOB_QUEUE_H
+#define RS_JOB_QUEUE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define RS_TYPE_JOB_QUEUE rs_job_queue_get_type()
+#define RS_JOB_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RS_TYPE_JOB_QUEUE, RSJobQueue))
+#define RS_JOB_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), RS_TYPE_JOB_QUEUE, RSJobQueueClass))
+#define RS_IS_JOB_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RS_TYPE_JOB_QUEUE))
+#define RS_IS_JOB_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), RS_TYPE_JOB_QUEUE))
+#define RS_JOB_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), RS_TYPE_JOB_QUEUE, RSJobQueueClass))
+
+typedef struct _RSJobQueueSlot RSJobQueueSlot;
+typedef struct _RSJob RSJob;
+typedef struct _RSJobQueue RSJobQueue;
+
+typedef struct {
+	GObjectClass parent_class;
+} RSJobQueueClass;
+
+typedef gpointer (*RSJobFunc)(RSJobQueueSlot *, gpointer);
+
+GType rs_job_queue_get_type(void);
+
+/**
+ * Add a new job to the job queue
+ * @note When func is called, it WILL be from another thread, it may be
+ *       required to acquire the GDK lock if any GTK+ stuff is done in the
+ *       callback!
+ * @param func A function to call for performing the job
+ * @param data Data to pass to func
+ * @param waitable If TRUE, rs_job_queue_wait() will wait until completion
+ */
+RSJob *
+rs_job_queue_add_job(RSJobFunc func, gpointer data, gboolean waitable);
+
+/**
+ * Wait (hang) until a job is finished and then free the memory allocated to job
+ * @param job The RSJob to wait for
+ * @return The value returned by the func given to rs_job_queue_add()
+ */
+gpointer
+rs_job_queue_wait(RSJob *job);
+
+/**
+ * Update the job description
+ * @note You should NOT have aquired the GDK thread lock when calling this
+ *       function.
+ * @param slot A job_slot as recieved in the job callback function
+ * @param description The new description or NULL to show nothing
+ */
+void
+rs_job_update_description(RSJobQueueSlot *slot, const gchar *description);
+
+/**
+ * Update the job progress bar
+ * @note You should NOT have aquired the GDK thread lock when calling this
+ *       function.
+ * @param slot A job_slot as recieved in the job callback function
+ * @param fraction A value between 0.0 and 1.0 to set the progress bar at
+ *                 the specific fraction or -1.0 to pulse the progress bar.
+ */
+void
+rs_job_update_progress(RSJobQueueSlot *slot, const gdouble fraction);
+
+G_END_DECLS
+
+#endif /* RS_JOB_QUEUE_H */




More information about the Rawstudio-commit mailing list