asynchronous libgnomecups implementation



Hi,

Matthias Clasen and I have been working on adding an asynchronous API
(vaguely modeled after GnomeVFS) to libgnomecups, and making the
internals use this.  Right now it's fairly naïve; it creates a new
thread for every request.  I'm working on moving it to a model where it
has a thread per server.  But anyways, we just wanted to get feedback on
the API as it stands now, and the approach in general.


cvs server: Diffing .
Index: configure.in
===================================================================
RCS file: /cvs/gnome/libgnomecups/configure.in,v
retrieving revision 1.27
diff -u -d -r1.27 configure.in
--- configure.in	4 Jun 2004 20:01:46 -0000	1.27
+++ configure.in	18 Jun 2004 17:57:57 -0000
@@ -20,7 +20,7 @@
 GLIB_REQUIRED=2.0.0
 AC_SUBST(GLIB_REQUIRED)
 
-PKG_CHECK_MODULES(LIBGNOMECUPS, glib-2.0 >= $GLIB_REQUIRED gobject-2.0 >= $GLIB_REQUIRED)
+PKG_CHECK_MODULES(LIBGNOMECUPS, glib-2.0 >= $GLIB_REQUIRED gobject-2.0 >= $GLIB_REQUIRED gthread-2.0 >= $GLIB_REQUIRED)
 AC_SUBST(LIBGNOMECUPS_CFLAGS)
 AC_SUBST(LIBGNOMECUPS_LIBS)
 
cvs server: Diffing libgnomecups
Index: libgnomecups/gnome-cups-init.c
===================================================================
RCS file: /cvs/gnome/libgnomecups/libgnomecups/gnome-cups-init.c,v
retrieving revision 1.3
diff -u -d -r1.3 gnome-cups-init.c
--- libgnomecups/gnome-cups-init.c	14 Jan 2004 22:14:52 -0000	1.3
+++ libgnomecups/gnome-cups-init.c	18 Jun 2004 17:57:57 -0000
@@ -2,39 +2,7 @@
 #include <cups/cups.h>
 #include "gnome-cups-init.h"
 #include "gnome-cups-printer.h"
-
-/* Should be per thread with push/pop/user_data etc. (clearly) */
-static GnomeCupsAuthFunction global_auth = NULL;
-
-static const char *
-cups_password_cb (const char *prompt)
-{
-	static char *hazard = NULL;
-
-	g_free (hazard);
-	hazard = NULL;
-
-	if (global_auth) {
-		char *password = NULL;
-		char *username = g_strdup (g_get_user_name ());
-
-		if (global_auth (prompt, &username, &password, NULL)) {
-
-			if (username) {
-				cupsSetUser (username);
-			} else {
-				cupsSetUser (g_get_user_name ());
-			}
-			hazard = password;
-		}
-		g_free (username);
-
-	} else {
-		g_warning ("Cannot prompt for password: '%s'", prompt);
-	}
-
-	return hazard;
-}
+#include "gnome-cups-request.h"
 
 /**
  * gnome_cups_init:
@@ -48,9 +16,12 @@
 {
 	g_type_init ();
 
-	global_auth = opt_auth_fn;
-	cupsSetPasswordCB (cups_password_cb);
-
+	_gnome_cups_request_init (opt_auth_fn);
 	_gnome_cups_printer_init ();
 }
 
+void
+gnome_cups_shutdown (void)
+{
+	_gnome_cups_request_shutdown ();
+}
Index: libgnomecups/gnome-cups-init.h
===================================================================
RCS file: /cvs/gnome/libgnomecups/libgnomecups/gnome-cups-init.h,v
retrieving revision 1.2
diff -u -d -r1.2 gnome-cups-init.h
--- libgnomecups/gnome-cups-init.h	2 May 2003 10:03:39 -0000	1.2
+++ libgnomecups/gnome-cups-init.h	18 Jun 2004 17:57:57 -0000
@@ -13,6 +13,7 @@
 					   GnomeCupsAuthContext *ctxt);
 
 void gnome_cups_init (GnomeCupsAuthFunction opt_auth_fn);
+void gnome_cups_shutdown (void);
 
 G_END_DECLS
 
Index: libgnomecups/gnome-cups-printer.c
===================================================================
RCS file: /cvs/gnome/libgnomecups/libgnomecups/gnome-cups-printer.c,v
retrieving revision 1.18
diff -u -d -r1.18 gnome-cups-printer.c
--- libgnomecups/gnome-cups-printer.c	17 Jun 2004 17:21:03 -0000	1.18
+++ libgnomecups/gnome-cups-printer.c	18 Jun 2004 17:57:57 -0000
@@ -46,6 +46,8 @@
 	guint is_gone : 1;
 	guint is_local : 1;
 
+	guint attributes_request_id;
+
 	/* Option management */
 	guint options_invalid : 1;
 	GHashTable *ppd_options;
@@ -207,34 +209,23 @@
 	return printer->details->info;
 }
 
-static void
-update_attributes (GnomeCupsPrinter *printer)
-{
 #define MAP_INT(v,a) {if (!g_ascii_strcasecmp (attr->name, (a))) { if ((v) != attr->values[0].integer) { changed = TRUE; } (v) = attr->values[0].integer; }}
 #define MAP_STRING(v,a) {if (!g_ascii_strcasecmp (attr->name, (a))) { if (!v || strcmp (v, attr->values[0].string.text)) { g_free (v); changed = TRUE; (v) = g_strdup (attr->values[0].string.text); }}}
 
-	ipp_t *request;
-	ipp_t *response;	
+static void
+attributes_update_cb (guint id,
+		      ipp_t *request,
+		      const char *path,
+		      ipp_t *response,
+		      GError **error,
+		      gpointer cb_data)
+{
+	GnomeCupsPrinter *printer;
 	ipp_attribute_t *attr;
 	gboolean changed;
-	GError *error = NULL;
-	static const char *attributes[] = {
-	  "printer-state", "queued-job-count",
-	  "printer-location", "printer-info",
-	  "printer-state-message", "device-uri",
-	  "printer-state-reasons", "printer-info",
-	  "printer-make-and-model", "printer-uri-supported"
-	};
-	
-	request = gnome_cups_request_new_for_printer (IPP_GET_PRINTER_ATTRIBUTES,
-						      printer->details->printer_name);
-	gnome_cups_request_add_requested_attributes (request, 
-						     IPP_TAG_OPERATION,
-						     G_N_ELEMENTS (attributes),
-						     (char**)attributes);
 
-	response = gnome_cups_request_execute (request, "/", &error);
-	
+	printer = GNOME_CUPS_PRINTER (cb_data);
+
 	changed = FALSE;
 
 	if (!error && response) {
@@ -260,9 +251,7 @@
 		}
 	}
 	ippDelete (response);
-	if (error) {
-		g_error_free (error);
-	}
+	g_clear_error (error);
 
 	if (changed) {
 		g_free (printer->details->full_state);
@@ -270,8 +259,42 @@
 		g_signal_emit (printer, signals[ATTRIBUTES_CHANGED], 0);
 	}
 
+	printer->details->attributes_request_id = 0;
+}
+
 #undef MAP_INT
 #undef MAP_STRING
+
+
+static void
+update_attributes (GnomeCupsPrinter *printer)
+{
+	ipp_t *request;
+	static const char *attributes[] = {
+	  "printer-state", "queued-job-count",
+	  "printer-location", "printer-info",
+	  "printer-state-message", "device-uri",
+	  "printer-state-reasons", "printer-info",
+	  "printer-make-and-model", "printer-uri-supported"
+	};
+
+	if (printer->details->attributes_request_id > 0) {
+		return;
+	}
+
+	request = gnome_cups_request_new_for_printer (IPP_GET_PRINTER_ATTRIBUTES,
+						      printer->details->printer_name);
+
+	gnome_cups_request_add_requested_attributes (request, 
+						     IPP_TAG_OPERATION,
+						     G_N_ELEMENTS (attributes),
+						     (char**)attributes);
+	printer->details->attributes_request_id = 
+		gnome_cups_request_execute_async (request, NULL, "/",
+						  (GnomeCupsAsyncRequestCallback) attributes_update_cb,
+						  g_object_ref (printer),
+						  g_object_unref);
+	
 }
 
 static char *
@@ -311,7 +334,7 @@
 	cupsFreeDests (num_dests, dests);
 
 	request = gnome_cups_request_new (CUPS_GET_DEFAULT);
-	response = gnome_cups_request_execute (request, "/", &error);
+	response = gnome_cups_request_execute (request, NULL, "/", &error);
 
 	if (error) {
 		ippDelete (response);
@@ -428,7 +451,7 @@
 	
 	request = gnome_cups_request_new (CUPS_GET_PRINTERS);
 	
-	response = gnome_cups_request_execute (request, "/", &error);
+	response = gnome_cups_request_execute (request, NULL, "/", &error);
 
 	if (error) {
 		ippDelete (response);
@@ -602,7 +625,7 @@
 
 	request = gnome_cups_request_new_for_printer (IPP_GET_PRINTER_ATTRIBUTES,
 						      printer);
-	response = gnome_cups_request_execute (request, "/", &error);
+	response = gnome_cups_request_execute (request, NULL, "/", &error);
 
 	ippDelete (response);
 
@@ -797,7 +820,7 @@
 						      printer->details->printer_name);
 	ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
 		      "printer-info", NULL, description);
-	response = gnome_cups_request_execute (request, "/admin/", error);
+	response = gnome_cups_request_execute (request, NULL, "/admin/", error);
 	ippDelete (response);
 	update_attributes (printer);
 }
@@ -829,7 +852,7 @@
 						      printer->details->printer_name);
 	ippAddString (request, IPP_TAG_PRINTER, IPP_TAG_TEXT,
 		      "printer-location", NULL, location);
-	response = gnome_cups_request_execute (request, "/admin/", error);
+	response = gnome_cups_request_execute (request, NULL, "/admin/", error);
 	ippDelete (response);
 	update_attributes (printer);
 }
@@ -845,7 +868,7 @@
 	
 	request = gnome_cups_request_new_for_printer (IPP_PAUSE_PRINTER,
 						      printer->details->printer_name);
-	response = gnome_cups_request_execute (request, "/admin/", error);
+	response = gnome_cups_request_execute (request, NULL, "/admin/", error);
 	ippDelete (response);
 	update_attributes (printer);
 }
@@ -861,7 +884,7 @@
 	
 	request = gnome_cups_request_new_for_printer (IPP_RESUME_PRINTER,
 						      printer->details->printer_name);
-	response = gnome_cups_request_execute (request, "/admin/", error);
+	response = gnome_cups_request_execute (request, NULL, "/admin/", error);
 	ippDelete (response);
 	update_attributes (printer);	
 }
@@ -877,7 +900,7 @@
 
 	request = gnome_cups_request_new_for_printer (CUPS_DELETE_PRINTER,
 						      printer->details->printer_name);
-	response = gnome_cups_request_execute (request, "/admin/", error);
+	response = gnome_cups_request_execute (request, NULL, "/admin/", error);
 	ippDelete (response);
 }
 
@@ -1233,6 +1256,9 @@
 gnome_cups_printer_finalize (GObject *object)
 {
 	GnomeCupsPrinter *printer = GNOME_CUPS_PRINTER (object);
+
+	if (printer->details->attributes_request_id > 0)
+		gnome_cups_request_cancel (printer->details->attributes_request_id > 0);
 	
 	if (printer->details->ppd_options) {
 		g_hash_table_destroy (printer->details->ppd_options);
@@ -1471,6 +1497,29 @@
 	    printer->details->dest_options) {
 		printer->details->options_invalid = TRUE;
 	}
+}
+
+gchar *_gnome_cups_printer_get_host (GnomeCupsPrinter *printer)
+{
+	gchar *host = NULL;
+	if (printer->details->printer_uri)
+		{
+			gchar *x, *y;
+
+			x = strstr (printer->details->printer_uri, "://");
+
+			if (x) 
+				{
+					x += 3;
+					y = strpbrk (x, ":/");
+					if (y)
+						host = g_strndup (x, y - x);
+					else 
+						host = g_strdup (x);
+				}
+		}
+	
+	return host;
 }
 
 char *
Index: libgnomecups/gnome-cups-queue.c
===================================================================
RCS file: /cvs/gnome/libgnomecups/libgnomecups/gnome-cups-queue.c,v
retrieving revision 1.11
diff -u -d -r1.11 gnome-cups-queue.c
--- libgnomecups/gnome-cups-queue.c	10 Jun 2004 15:55:44 -0000	1.11
+++ libgnomecups/gnome-cups-queue.c	18 Jun 2004 17:57:57 -0000
@@ -9,6 +9,7 @@
 
 #include "util.h"
 #include "gnome-cups-request.h"
+#include "gnome-cups-util.h"
 #include "gnome-cups-i18n.h"
 
 #define UPDATE_TIMEOUT 3000
@@ -17,6 +18,8 @@
 	char *queue_name;
 	GList *jobs;
 	gboolean is_gone;
+
+	guint get_jobs_request_id;
 };
 
 enum {
@@ -198,26 +201,34 @@
 #define MAP_STR(dest, src) { if (!g_ascii_strcasecmp (attr->name, (src))) { if ((dest) != NULL) g_free (dest); (dest) = g_strdup (attr->values[0].string.text);}}
 #define MAP_INT(dest, src) { if (!g_ascii_strcasecmp (attr->name, (src))) { (dest) = attr->values[0].integer; } }
 
-static GList *
-get_jobs (const char *printer_name)
+static void
+get_jobs_cb (guint id,
+	     ipp_t *request,
+	     const char *path,
+	     ipp_t *response,
+	     GError **error,
+	     gpointer cb_data)
 {
-	GError *error = NULL;
-	ipp_t *request;
-	ipp_t *response;	
-	ipp_attribute_t *attr;
+	GnomeCupsQueue *queue;
 	GList *jobs;
 	GnomeCupsJob *job;
+	ipp_attribute_t *attr;
+	GList *old_jobs;
+	GList *added_jobs;
+	GList *removed_jobs;
+	GList *changed_jobs;
 
-	request = gnome_cups_request_new_for_printer (IPP_GET_JOBS,
-						      printer_name);
-	response = gnome_cups_request_execute (request, "/", &error);
 	if (error) {
 		ippDelete (response);
-		g_error_free (error);
-		return NULL;
+		g_clear_error (error);
+		return;
 	}
-	
+
+	queue = GNOME_CUPS_QUEUE (cb_data);
+
+	old_jobs = queue->details->jobs;
 	jobs = NULL;
+	
 	if (response) {
 		job = g_new0 (GnomeCupsJob, 1);
 		for (attr = response->attrs; attr != NULL; attr = attr->next) {
@@ -235,8 +246,8 @@
 			
 			if (!g_ascii_strcasecmp (attr->name, "attributes-charset") || !g_ascii_strcasecmp (attr->name, "attributes-charset")) {
 				continue;
+				
 			}
-
 			MAP_STR (job->name, "job-name");
 			MAP_INT (job->id, "job-id");
 			MAP_STR (job->owner, "job-originating-user-name");
@@ -257,12 +268,50 @@
 			gnome_cups_job_free (job);
 		}
 		
-		jobs = g_list_reverse (jobs);
+		queue->details->jobs = g_list_reverse (jobs);
 	
 		ippDelete (response);
 	}
+	
+	compare_queues (old_jobs, queue->details->jobs, 
+			&added_jobs, &removed_jobs, &changed_jobs);
 
-	return jobs;
+	if (added_jobs) {
+		g_signal_emit (queue, signals[JOBS_ADDED], 0, added_jobs);
+		g_list_free (added_jobs);
+	} 
+	if (changed_jobs) {
+		g_signal_emit (queue, signals[JOBS_CHANGED], 0, changed_jobs);
+		g_list_free (changed_jobs);
+	} 
+	if (removed_jobs) {
+		g_signal_emit (queue, signals[JOBS_REMOVED], 0, removed_jobs);
+		g_list_free (removed_jobs);
+	} 
+
+	gnome_cups_job_list_free (old_jobs);
+
+	queue->details->get_jobs_request_id = 0;
+}
+
+static void
+get_jobs_on_server (GnomeCupsQueue *queue, const char *server)
+{
+	ipp_t *request;
+	const char *printer_name;
+
+	if (queue->details->get_jobs_request_id > 0)
+		return;
+
+	printer_name = queue->details->queue_name;
+
+	request = gnome_cups_request_new_for_printer (IPP_GET_JOBS,
+						      printer_name);
+	queue->details->get_jobs_request_id =
+		gnome_cups_request_execute_async (request, server, "/",
+						  (GnomeCupsAsyncRequestCallback) get_jobs_cb,
+						  g_object_ref (queue),
+						  (GDestroyNotify) g_object_unref);
 }
 
 static GnomeCupsJob *
@@ -273,11 +322,27 @@
 	ipp_t *request;
 	ipp_t *response;	
 	ipp_attribute_t *attr;
+	GnomeCupsPrinter *printer;
 	GnomeCupsJob *job;
+	char *server;
+
+	printer = gnome_cups_printer_get (queue->details->queue_name);
+	
+	if (!printer)
+		return NULL;
+
+	server = _gnome_cups_printer_get_host (printer);
+
+	g_object_unref (G_OBJECT (printer));
 	
 	request = gnome_cups_request_new_for_job (IPP_GET_JOB_ATTRIBUTES, 
 						  job_id);
-	response = gnome_cups_request_execute (request, "/", &error);
+
+	if (gnome_cups_printer_get_is_local (printer))
+	  response = gnome_cups_request_execute (request, NULL, "/", &error);
+	else
+	  response = gnome_cups_request_execute (request, server, "/", &error);
+	g_free (server);
 	if (error) {
 		ippDelete (response);
 		g_error_free (error);
@@ -357,32 +422,25 @@
 static void
 update_queue (GnomeCupsQueue *queue)
 {
-	GList *old_jobs;
+	GnomeCupsPrinter *printer;
+	gchar *printer_host = NULL;
 
-	GList *added_jobs;
-	GList *removed_jobs;
-	GList *changed_jobs;
+	printer = gnome_cups_printer_get_existing (queue->details->queue_name);
 	
-	old_jobs = queue->details->jobs;
-	queue->details->jobs = get_jobs (queue->details->queue_name);
+	if (printer) {
+		printer_host = _gnome_cups_printer_get_host (printer);
+		g_object_unref (printer);
+	}
 
-	compare_queues (old_jobs, queue->details->jobs, 
-			&added_jobs, &removed_jobs, &changed_jobs);
+	if (!printer_host)
+		return;
 
-	if (added_jobs) {
-		g_signal_emit (queue, signals[JOBS_ADDED], 0, added_jobs);
-		g_list_free (added_jobs);
-	} 
-	if (changed_jobs) {
-		g_signal_emit (queue, signals[JOBS_CHANGED], 0, changed_jobs);
-		g_list_free (changed_jobs);
-	} 
-	if (removed_jobs) {
-		g_signal_emit (queue, signals[JOBS_REMOVED], 0, removed_jobs);
-		g_list_free (removed_jobs);
-	} 
+	if (gnome_cups_printer_get_is_local (printer))
+		get_jobs_on_server (queue, NULL);
+	else
+		get_jobs_on_server (queue, printer_host);
 
-	gnome_cups_job_list_free (old_jobs);
+	g_free (printer_host);
 }
 
 static gboolean
@@ -513,7 +571,7 @@
 
 	request = gnome_cups_request_new_for_printer (IPP_GET_PRINTER_ATTRIBUTES,
 						      queue);
-	response = gnome_cups_request_execute (request, "/", &error);
+	response = gnome_cups_request_execute (request, NULL, "/", &error);
 
 	ippDelete (response);
 
@@ -598,7 +656,7 @@
 	g_return_if_fail (GNOME_CUPS_IS_QUEUE (queue));
 
 	request = gnome_cups_request_new_for_job (IPP_HOLD_JOB, job_id);
-	response = gnome_cups_request_execute (request, "/jobs", error);
+	response = gnome_cups_request_execute (request, NULL, "/jobs", error);
 	ippDelete (response);
 
 	update_queue (queue);
@@ -615,7 +673,7 @@
 	g_return_if_fail (GNOME_CUPS_IS_QUEUE (queue));
 
 	request = gnome_cups_request_new_for_job (IPP_RELEASE_JOB, job_id);
-	response = gnome_cups_request_execute (request, "/jobs", error);
+	response = gnome_cups_request_execute (request, NULL, "/jobs", error);
 	ippDelete (response);	
 
 	update_queue (queue);
@@ -632,7 +690,7 @@
 	g_return_if_fail (GNOME_CUPS_IS_QUEUE (queue));
 
 	request = gnome_cups_request_new_for_job (IPP_CANCEL_JOB, job_id);
-	response = gnome_cups_request_execute (request, "/jobs", error);
+	response = gnome_cups_request_execute (request, NULL, "/jobs", error);
 	ippDelete (response);
 
 	update_queue (queue);
@@ -642,6 +700,9 @@
 gnome_cups_queue_finalize (GObject *object)
 {
 	GnomeCupsQueue *queue = GNOME_CUPS_QUEUE (object);
+
+	if (queue->details->get_jobs_request_id > 0)
+		gnome_cups_request_cancel (queue->details->get_jobs_request_id > 0);
 	
 	if (queue->details->jobs) {
 		gnome_cups_job_list_free (queue->details->jobs);
Index: libgnomecups/gnome-cups-request.c
===================================================================
RCS file: /cvs/gnome/libgnomecups/libgnomecups/gnome-cups-request.c,v
retrieving revision 1.11
diff -u -d -r1.11 gnome-cups-request.c
--- libgnomecups/gnome-cups-request.c	17 May 2004 16:46:18 -0000	1.11
+++ libgnomecups/gnome-cups-request.c	18 Jun 2004 17:57:57 -0000
@@ -10,6 +10,15 @@
 #include "gnome-cups-util.h"
 #include "gnome-cups-i18n.h"
 
+static guint
+gnome_cups_request_execute_async_internal (ipp_t      *request, 
+					   const char *server, 
+					   const char *path, 
+					   gboolean direct_callback,
+					   GnomeCupsAsyncRequestCallback callback,
+					   gpointer cb_data,
+					   GDestroyNotify destroy_notify);
+
 static const char *
 get_error_string (ipp_status_t error)
 {  
@@ -77,6 +86,179 @@
 	return _("Unknown error");
 }
 
+/* Should be per thread with push/pop/user_data etc. (clearly) */
+static GnomeCupsAuthFunction global_auth = NULL;
+
+static const char *
+cups_password_cb (const char *prompt)
+{
+	static char *hazard = NULL;
+
+	g_free (hazard);
+	hazard = NULL;
+
+	if (global_auth) {
+		char *password = NULL;
+		char *username = g_strdup (g_get_user_name ());
+
+		if (global_auth (prompt, &username, &password, NULL)) {
+
+			if (username) {
+				cupsSetUser (username);
+			} else {
+				cupsSetUser (g_get_user_name ());
+			}
+			hazard = password;
+		}
+		g_free (username);
+
+	} else {
+		g_warning ("Cannot prompt for password: '%s'", prompt);
+	}
+
+	return hazard;
+}
+
+typedef struct
+{
+	gboolean cancelled;
+	gboolean direct_callback;
+	guint id;
+	
+	ipp_t *response;
+	GError **error; 
+	char *server;
+	GnomeCupsAsyncRequestCallback callback;
+	gpointer cb_data;
+	GDestroyNotify destroy_notify;
+
+	ipp_t *request;
+	char *path;
+} GnomeCupsRequest;
+
+GStaticMutex request_mutex = G_STATIC_MUTEX_INIT;
+static guint request_serial_number = 0;
+static guint request_map_refcount = 0;
+static GHashTable *request_map = 0;
+
+void
+_gnome_cups_request_init (GnomeCupsAuthFunction auth_fn)
+{
+	global_auth = auth_fn;
+	cupsSetPasswordCB (cups_password_cb);
+	
+	g_static_mutex_lock (&request_mutex);
+	if (request_map_refcount == 0)
+		request_map = g_hash_table_new (NULL, NULL);
+	request_map_refcount++;
+	g_static_mutex_unlock (&request_mutex);
+}
+
+void
+_gnome_cups_request_shutdown (void)
+{
+	g_static_mutex_lock (&request_mutex);
+	request_map_refcount--;
+	if (request_map_refcount == 0)
+		g_hash_table_destroy (request_map);
+	g_static_mutex_unlock (&request_mutex);
+}
+
+static void
+gnome_cups_request_struct_free (GnomeCupsRequest *request)
+{
+	g_free (request->server);
+	g_free (request->path);
+	if (request->error && *request->error)
+		g_error_free (*request->error);
+	g_free (request);
+}	
+
+static gboolean
+idle_signal_request_complete (GnomeCupsRequest *request)
+{
+	if (!request->cancelled && request->callback)
+		request->callback (request->id, request->request,
+				   request->path,
+				   request->response,
+				   request->error,
+				   request->cb_data);
+	else {
+		ippDelete (request->request);
+		ippDelete (request->response);
+	}
+
+	g_static_mutex_lock (&request_mutex);
+	g_assert (g_hash_table_remove (request_map, GUINT_TO_POINTER (request->id)));
+	g_static_mutex_unlock (&request_mutex);
+
+	if (request->destroy_notify)
+		request->destroy_notify (request->cb_data);
+
+	gnome_cups_request_struct_free (request);
+
+	return FALSE;
+}
+
+static void
+do_signal_complete (GnomeCupsRequest *request)
+{
+	if (request->direct_callback)
+		idle_signal_request_complete (request);
+	else
+		g_idle_add ((GSourceFunc) idle_signal_request_complete, request);
+}
+
+static gpointer
+request_thread_main (GnomeCupsRequest *request)
+{
+	http_t *http;
+	ipp_t *response;
+	ipp_status_t status;
+	const char *server;
+
+	if (request->cancelled) {
+		do_signal_complete (request);
+		return NULL;
+	}
+
+	if (!request->server)
+		server = cupsServer();
+	else
+		server = request->server;
+
+	http = httpConnectEncrypt (server, ippPort(), cupsEncryption());
+
+	response = cupsDoRequest (http, request->request,
+				  request->path ? request->path : "/");
+
+	status = cupsLastError ();
+
+	httpClose (http);
+
+	if (status > IPP_OK_CONFLICT && request->error != NULL) {
+		*(request->error) = g_error_new (GNOME_CUPS_ERROR, 
+						 status,
+						 get_error_string (status));
+	}
+
+	request->response = response;
+	do_signal_complete (request);
+	
+	return NULL;
+}
+
+gboolean
+_gnome_cups_request_is_executing (void)
+{
+	gboolean ret;
+
+	g_static_mutex_lock (&request_mutex);
+	ret = g_hash_table_size (request_map) > 0;
+	g_static_mutex_unlock (&request_mutex);
+	return ret;
+}
+
 ipp_t *
 gnome_cups_request_new (int operation_id)
 {
@@ -105,7 +287,7 @@
 {
 	ipp_t *request;
 	char *printer_uri;
-	
+
 	request = gnome_cups_request_new (operation_id);
 
 	printer_uri = gnome_cups_get_printer_uri (printer_name);
@@ -155,71 +337,132 @@
 			      IPP_TAG_KEYWORD,
 			      "requested-attributes",
 			      n_attributes, NULL, NULL);
-	
+
 	for (i = 0; i < n_attributes; i++) {
 		attr->values[i].string.text = gnome_cups_strdup (attributes[i]);
 	}
 }
 
-static gboolean request_executing = FALSE;
+typedef struct
+{
+	GMutex *mutex;
+	GCond *cond;
+	gboolean done;
+	ipp_t *response;
+	GError **error;
+} GnomeCupsAsyncWrapperData;
 
-gboolean 
-_gnome_cups_request_is_executing (void) 
+static void
+async_wrapper_cb (guint id, ipp_t *request, const char *path,
+		  ipp_t *response, GError **error,
+		  GnomeCupsAsyncWrapperData *data)
 {
-	return request_executing;
+	g_mutex_lock (data->mutex);
+	data->done = TRUE;
+	data->response = response;
+	if (data->error && error && *error)
+		g_propagate_error (data->error, *error);
+	g_cond_signal (data->cond);
+	g_mutex_unlock (data->mutex);
 }
 
 ipp_t *
-gnome_cups_request_execute (ipp_t *request, const char *path, GError **err)
+gnome_cups_request_execute (ipp_t *request,
+			    const char *server,
+			    const char *path, GError **err)
 {
-	static http_t *main_http = NULL;
-	http_t *single_http = NULL;
-	http_t *http;
-	ipp_t *response;
-	ipp_status_t status;
+	guint id;
+	GnomeCupsAsyncWrapperData data;
 
-	g_return_val_if_fail (err == NULL || *err == NULL, NULL);
+	data.mutex = g_mutex_new ();
+	data.cond = g_cond_new ();
+	data.done = FALSE;
+	data.response = NULL;	
+	data.error = err;
 
-	/* FIXME: This sucks.  We want to try to keep everything on
-	 * one http connection, but due to reentrancy problems, we
-	 * can't always do that (if the request goes back to the main
-	 * loop during authentication, we can be called again, and
-	 * can't issue a new request on that connection. 
-	 * 
-	 * For right now, we keep a main http connection around, and use that 
-	 * if it's available.  Otherwise we do a connection per request. */
+	id = gnome_cups_request_execute_async_internal (request, server, path,
+							TRUE,
+							(GnomeCupsAsyncRequestCallback) async_wrapper_cb,
+							&data,
+							NULL);
+	if (id == 0)
+		goto out;
 
-	cupsSetUser (g_get_user_name ());
+	g_mutex_lock (data.mutex);
+	while (!data.done)
+		g_cond_wait (data.cond, data.mutex);
+	g_mutex_unlock (data.mutex);
 
-	if (!main_http) {
-		main_http = httpConnectEncrypt (cupsServer(), ippPort(), cupsEncryption());
-	}
+out:
+	g_mutex_free (data.mutex);
+	g_cond_free (data.cond);
+	
+	return data.response;
+}
 
-	if (!request_executing) {
-		http = main_http;
-	} else {
-		http = single_http = httpConnectEncrypt (cupsServer (),
-							 ippPort(),
-							 cupsEncryption());
-	}
+guint
+gnome_cups_request_execute_async (ipp_t      *request, 
+				  const char *server, 
+				  const char *path, 
+				  GnomeCupsAsyncRequestCallback callback,
+				  gpointer cb_data,
+				  GDestroyNotify destroy_notify)
+{
+	return gnome_cups_request_execute_async_internal (request, server,
+							  path, FALSE,
+							  callback, cb_data,
+							  destroy_notify);
+}
 
-	request_executing = TRUE;
+static guint
+gnome_cups_request_execute_async_internal (ipp_t      *request, 
+					   const char *server, 
+					   const char *path, 
+					   gboolean direct_callback,
+					   GnomeCupsAsyncRequestCallback callback,
+					   gpointer cb_data,
+					   GDestroyNotify destroy_notify)
+{
+	GnomeCupsRequest *req;
+	GError *thread_err = NULL;
 	
-	response = cupsDoRequest (http, request, path ? path : "/");
+	req = g_new0 (GnomeCupsRequest, 1);
+	req->cancelled = FALSE;
+	req->server = g_strdup (server);
+	req->request = request;
+	req->callback = callback;
+	req->cb_data = cb_data;
+	req->destroy_notify = destroy_notify;
+	req->path = g_strdup (path);
+	req->direct_callback = direct_callback;
+	req->error = NULL;
 
-	status = cupsLastError ();
+	g_static_mutex_lock (&request_mutex);
+	req->id = ++request_serial_number;
+	g_static_mutex_unlock (&request_mutex);
 
-	if (single_http) {
-		httpClose (single_http);
+	g_thread_create ((GThreadFunc) request_thread_main, req, FALSE, &thread_err);
+	if (thread_err != NULL) {
+		g_error_free (thread_err);
+		gnome_cups_request_struct_free (req);
+		return 0;
 	}
+	g_static_mutex_lock (&request_mutex);
+	g_hash_table_insert (request_map, GUINT_TO_POINTER (req->id), req);
+	g_static_mutex_unlock (&request_mutex);
 
-	request_executing = FALSE;
+	return req->id;
+	
+}
 
-	if (status > IPP_OK_CONFLICT && err != NULL) {
-		*err = g_error_new (GNOME_CUPS_ERROR, 
-				    status,
-				    get_error_string (status));
-	}
+void
+gnome_cups_request_cancel (guint request_id)
+{
+	GnomeCupsRequest *request;
 	
-	return response;
+	g_static_mutex_lock (&request_mutex);
+	if ((request = g_hash_table_lookup (request_map, GUINT_TO_POINTER (request_id))) != NULL) {
+		request->cancelled = TRUE;
+	}
+	g_static_mutex_unlock (&request_mutex);
 }
Index: libgnomecups/gnome-cups-request.h
===================================================================
RCS file: /cvs/gnome/libgnomecups/libgnomecups/gnome-cups-request.h,v
retrieving revision 1.4
diff -u -d -r1.4 gnome-cups-request.h
--- libgnomecups/gnome-cups-request.h	6 May 2003 22:14:27 -0000	1.4
+++ libgnomecups/gnome-cups-request.h	18 Jun 2004 17:57:57 -0000
@@ -3,6 +3,14 @@
 
 #include <cups/ipp.h>
 #include <glib.h>
+#include <gnome-cups-init.h>
+
+typedef void (*GnomeCupsAsyncRequestCallback) (guint id,
+					       ipp_t *request,
+					       const char *path,
+					       ipp_t *response,
+					       GError **error,
+					       gpointer cb_data);
 
 ipp_t *gnome_cups_request_new                      (int          operation_id);
 ipp_t *gnome_cups_request_new_for_printer          (int          operation_id,
@@ -14,11 +22,22 @@
 						    int          n_attributes,
 						    char       **attributes);
 ipp_t *gnome_cups_request_execute                  (ipp_t       *request,
+						    const char *server,
 						    const char  *path,
 						    GError     **err);
+guint gnome_cups_request_execute_async             (ipp_t       *request,
+						    const char *server,
+						    const char  *path,
+						    GnomeCupsAsyncRequestCallback callback,
+						    gpointer cb_data,
+						    GDestroyNotify destroy_notify);
+
+void gnome_cups_request_cancel (guint request_id);
 
 /* private */
 gboolean _gnome_cups_request_is_executing (void);
+void _gnome_cups_request_init (GnomeCupsAuthFunction authfn);
+void _gnome_cups_request_shutdown (void);
 
 
 
cvs server: Diffing po

Attachment: signature.asc
Description: This is a digitally signed message part



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]