Re: asynchronous libgnomecups implementation



[ seeing how many times I can follow up to myself ;) ]

Ok, I was noticing a little bit of slowness with the patch, and I
realized that in the case where a request was started on a server which
already had an outstanding request in progress, the main thread would
grab a mutex (to increment the connection refcount) which would wait
until the request completed from the other thread.  I fixed this by just
using glib atomic integers for refcounts.

Scrolling the print dialog now feels quite consistently smooth even for
just our 8 printers here at the office.
cvs server: Diffing .
Index: configure.in
===================================================================
RCS file: /cvs/gnome/libgnomecups/configure.in,v
retrieving revision 1.28
diff -u -d -r1.28 configure.in
--- configure.in	18 Jun 2004 18:32:42 -0000	1.28
+++ configure.in	18 Jun 2004 23:47:39 -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 23:47:39 -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 23:47:39 -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.19
diff -u -d -r1.19 gnome-cups-printer.c
--- libgnomecups/gnome-cups-printer.c	18 Jun 2004 18:27:24 -0000	1.19
+++ libgnomecups/gnome-cups-printer.c	18 Jun 2004 23:47:39 -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,22 @@
 	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,
+		      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, NULL, "/", &error);
-	
+	printer = GNOME_CUPS_PRINTER (cb_data);
+
 	changed = FALSE;
 
 	if (!error && response) {
@@ -260,9 +250,7 @@
 		}
 	}
 	ippDelete (response);
-	if (error) {
-		g_error_free (error);
-	}
+	g_clear_error (error);
 
 	if (changed) {
 		g_free (printer->details->full_state);
@@ -270,8 +258,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, "/",
+						  attributes_update_cb,
+						  g_object_ref (printer),
+						  g_object_unref);
+	
 }
 
 static char *
@@ -504,7 +526,7 @@
 {
 	/* To avoid unneccessary calls during authentication, check
 	 * if a request is currently executing */
-	if (!_gnome_cups_request_is_executing ()) {
+	if (_gnome_cups_outstanding_request_count () == 0) {
 		update_printers ();
 	}
 	
@@ -1233,6 +1255,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 +1496,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.12
diff -u -d -r1.12 gnome-cups-queue.c
--- libgnomecups/gnome-cups-queue.c	18 Jun 2004 18:27:24 -0000	1.12
+++ libgnomecups/gnome-cups-queue.c	18 Jun 2004 23:47:39 -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,33 @@
 #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,
+	     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, NULL, "/", &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 +245,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 +267,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, "/",
+						  get_jobs_cb,
+						  g_object_ref (queue),
+						  (GDestroyNotify) g_object_unref);
 }
 
 static GnomeCupsJob *
@@ -273,11 +321,24 @@
 	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, NULL, "/", &error);
+
+	response = gnome_cups_request_execute (request, server, "/", &error);
+
 	if (error) {
 		ippDelete (response);
 		g_error_free (error);
@@ -357,32 +418,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
@@ -418,7 +472,7 @@
 	
 	/* To avoid unneccessary calls during authentication, check
 	 * if a request is currently executing */
-	if (_gnome_cups_request_is_executing ()) {
+	if (_gnome_cups_outstanding_request_count () > 0) {
 		return TRUE;
 	}
 
@@ -642,6 +696,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.12
diff -u -d -r1.12 gnome-cups-request.c
--- libgnomecups/gnome-cups-request.c	18 Jun 2004 18:27:24 -0000	1.12
+++ libgnomecups/gnome-cups-request.c	18 Jun 2004 23:47:39 -0000
@@ -1,5 +1,6 @@
 #include <config.h>
 #include "gnome-cups-request.h"
+#include <glib.h>
 
 #include <stdlib.h>
 #include <cups/cups.h>
@@ -10,6 +11,49 @@
 #include "gnome-cups-util.h"
 #include "gnome-cups-i18n.h"
 
+/* Arbitrary. */
+#define MAX_REQUEST_THREADS 10
+#define STOP_UNUSED_THREADS_TIMEOUT 60
+#define CLOSE_UNUSED_CONNECTIONS_TIMEOUT 30
+
+typedef struct
+{
+	GMutex *mutex;
+	gint refcount;
+	char *server;
+	GTimeVal use_time;
+	http_t *http;
+} GnomeCupsConnection;
+
+typedef struct
+{
+	gboolean cancelled;
+	gboolean direct_callback;
+	guint id;
+	GnomeCupsConnection *connection;
+	
+	ipp_t *response;
+	GError **error; 
+	GnomeCupsAsyncRequestCallback callback;
+	gpointer cb_data;
+	GDestroyNotify destroy_notify;
+
+	ipp_t *request;
+	char *path;
+} GnomeCupsRequest;
+
+static void request_thread_main (GnomeCupsRequest *request, gpointer unused);
+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 void gnome_cups_request_connection_destroy (GnomeCupsConnection *conn);
+static gboolean idle_stop_unused_threads (gpointer unused);
+static gboolean idle_close_unused_connections (gpointer unused);
+
 static const char *
 get_error_string (ipp_status_t error)
 {  
@@ -77,6 +121,258 @@
 	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;
+}
+
+GStaticMutex request_mutex = G_STATIC_MUTEX_INIT;
+static guint request_serial_number = 0;
+static guint request_system_refcount = 0;
+static guint idle_stop_unused_threads_id = 0;
+static guint idle_close_unused_connections_id = 0;
+static GThreadPool *request_thread_pool;
+static GHashTable *request_map = 0;
+static GHashTable *connection_cache_map = 0;
+
+void
+_gnome_cups_request_init (GnomeCupsAuthFunction auth_fn)
+{
+	GError *error = NULL;
+	global_auth = auth_fn;
+	cupsSetPasswordCB (cups_password_cb);
+	
+	g_static_mutex_lock (&request_mutex);
+	if (request_system_refcount == 0) {
+		request_map = g_hash_table_new (NULL, NULL);
+		connection_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
+							      (GDestroyNotify) g_free,
+							      (GDestroyNotify) gnome_cups_request_connection_destroy);
+		request_thread_pool = g_thread_pool_new ((GFunc) request_thread_main,
+							 NULL,
+							 MAX_REQUEST_THREADS,
+							 FALSE,
+							 &error);
+		idle_stop_unused_threads_id = g_timeout_add (STOP_UNUSED_THREADS_TIMEOUT * 1000, (GSourceFunc) idle_stop_unused_threads, NULL);
+		idle_close_unused_connections_id = g_timeout_add (CLOSE_UNUSED_CONNECTIONS_TIMEOUT * 1000, (GSourceFunc) idle_close_unused_connections, NULL);
+	}
+	request_system_refcount++;
+	g_static_mutex_unlock (&request_mutex);
+
+	if (error != NULL) {
+		g_critical ("Error creating thread pool: %s", error->message);
+		_gnome_cups_request_shutdown ();
+	}
+}
+
+void
+_gnome_cups_request_shutdown (void)
+{
+	g_static_mutex_lock (&request_mutex);
+	request_system_refcount--;
+	if (request_system_refcount == 0) {
+		g_hash_table_destroy (request_map);
+		g_hash_table_destroy (connection_cache_map);
+		g_source_remove (idle_stop_unused_threads_id);
+		g_source_remove (idle_close_unused_connections_id);
+		g_thread_pool_free (request_thread_pool, TRUE, TRUE);
+	}
+	g_static_mutex_unlock (&request_mutex);
+}
+
+static gboolean
+idle_stop_unused_threads (gpointer unused)
+{
+	g_static_mutex_lock (&request_mutex);
+
+	if (request_system_refcount == 0) {
+		g_static_mutex_unlock (&request_mutex);
+		return FALSE;
+	}
+
+	g_thread_pool_stop_unused_threads ();
+
+	g_static_mutex_unlock (&request_mutex);
+	return TRUE;
+}
+
+static gboolean
+close_unused_connection (const char *server,
+			 GnomeCupsConnection *connection,
+			 GTimeVal *current_time)
+{
+	gboolean ret;
+
+	g_mutex_lock (connection->mutex);
+	ret = (g_atomic_int_get (&connection->refcount) == 0
+	       && (current_time->tv_sec - connection->use_time.tv_sec > 30));
+	g_mutex_unlock (connection->mutex);
+	return ret;
+}
+
+static gboolean
+idle_close_unused_connections (gpointer unused)
+{
+	GTimeVal current_time;
+
+	g_static_mutex_lock (&request_mutex);
+
+	if (request_system_refcount == 0) {
+		g_static_mutex_unlock (&request_mutex);
+		return FALSE;
+	}
+
+	g_get_current_time (&current_time);
+	g_hash_table_foreach_remove (connection_cache_map,
+				     (GHRFunc) close_unused_connection,
+				     &current_time);
+
+	g_static_mutex_unlock (&request_mutex);
+	return TRUE;
+}
+
+static void
+gnome_cups_request_struct_free (GnomeCupsRequest *request)
+{
+	g_free (request->path);
+	if (request->error && *request->error)
+		g_error_free (*request->error);
+	g_free (request);
+}	
+
+static void
+gnome_cups_request_connection_destroy (GnomeCupsConnection *connection)
+{
+	g_mutex_lock (connection->mutex);
+	if (connection->http)
+		httpClose (connection->http);
+	g_free (connection->server);
+	g_mutex_unlock (connection->mutex);
+	g_mutex_free (connection->mutex);
+	g_free (connection);
+}
+
+static gboolean
+idle_signal_request_complete (GnomeCupsRequest *request)
+{
+	if (!request->cancelled && request->callback)
+		request->callback (request->id,
+				   request->path,
+				   request->response,
+				   request->error,
+				   request->cb_data);
+	else {
+		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 void
+request_thread_main (GnomeCupsRequest *request,
+		     gpointer unused)
+{
+	ipp_t *response;
+	ipp_status_t status;
+
+	if (request->cancelled) {
+		do_signal_complete (request);
+		return;
+	}
+
+	g_mutex_lock (request->connection->mutex);
+	
+	g_get_current_time (&request->connection->use_time);
+
+	/* This is a deferred open for the first connection */
+	if (!request->connection->http)
+		request->connection->http = httpConnectEncrypt (request->connection->server, ippPort(), cupsEncryption());
+
+
+	response = cupsDoRequest (request->connection->http, request->request,
+				  request->path);
+
+	/* FIXME - not currently threadsafe, but cups returns NULL on
+	 * any error.  Thus we just set the status to an internal error
+	 * for now.
+	 */
+	status = cupsLastError ();
+	if (response == NULL)
+		status = IPP_INTERNAL_ERROR;
+
+	g_atomic_int_dec_and_test (&request->connection->refcount);
+	g_mutex_unlock (request->connection->mutex);
+
+	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;
+}
+
+guint
+_gnome_cups_outstanding_request_count (void)
+{
+	guint ret;
+
+	g_static_mutex_lock (&request_mutex);
+	ret = g_hash_table_size (request_map);
+	g_static_mutex_unlock (&request_mutex);
+
+	return ret;
+}
+
 ipp_t *
 gnome_cups_request_new (int operation_id)
 {
@@ -105,7 +401,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 +451,140 @@
 			      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, const char *path,
+		  ipp_t *response, GError **error,
+		  gpointer user_data)
 {
-	return request_executing;
+	GnomeCupsAsyncWrapperData *data = user_data;
+	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 *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,
+							async_wrapper_cb,
+							&data,
+							NULL);
+	if (id > 0) {
+		g_mutex_lock (data.mutex);
+		while (!data.done)
+			g_cond_wait (data.cond, data.mutex);
+		g_mutex_unlock (data.mutex);
+	}
 
-	cupsSetUser (g_get_user_name ());
+	g_mutex_free (data.mutex);
+	g_cond_free (data.cond);
+	
+	return data.response;
+}
 
-	if (!main_http) {
-		main_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);
+}
 
-	if (!request_executing && !server) {
-		http = main_http;
-	} else {
-		http = single_http = httpConnectEncrypt (server ? server : cupsServer(),
-							 ippPort(),
-							 cupsEncryption());
-	}
+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)
+{
+	GnomeCupsConnection *connection;
+	GnomeCupsRequest *req;
 
-	request_executing = TRUE;
-	
-	response = cupsDoRequest (http, request, path ? path : "/");
+	if (!server)
+		server = cupsServer();
+	if (!path)
+		path = "/";
 
-	status = cupsLastError ();
+	g_static_mutex_lock (&request_mutex);
 
-	if (single_http) {
-		httpClose (single_http);
+	if ((connection = g_hash_table_lookup (connection_cache_map, server)) == NULL) {
+		connection = g_new0 (GnomeCupsConnection, 1);
+		connection->mutex = g_mutex_new ();
+		connection->server = g_strdup (server);
+		/* Let the thread actually make the HTTP connection */
+		connection->http = NULL;
+		connection->refcount = 0;
+		g_hash_table_insert (connection_cache_map, g_strdup (server),
+				     connection);
 	}
+	g_atomic_int_add (&connection->refcount, 1);
+	
+	req = g_new0 (GnomeCupsRequest, 1);
+	req->connection = connection;
+	req->cancelled = FALSE;
+	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;
 
-	request_executing = FALSE;
+	req->id = ++request_serial_number;
 
-	if (status > IPP_OK_CONFLICT && err != NULL) {
-		*err = g_error_new (GNOME_CUPS_ERROR, 
-				    status,
-				    get_error_string (status));
-	}
+	g_thread_pool_push (request_thread_pool, req, NULL);
+
+	g_hash_table_insert (request_map, GUINT_TO_POINTER (req->id), req);
+	g_static_mutex_unlock (&request_mutex);
+
+	return req->id;
+}
+
+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.5
diff -u -d -r1.5 gnome-cups-request.h
--- libgnomecups/gnome-cups-request.h	18 Jun 2004 18:27:24 -0000	1.5
+++ libgnomecups/gnome-cups-request.h	18 Jun 2004 23:47:39 -0000
@@ -3,6 +3,13 @@
 
 #include <cups/ipp.h>
 #include <glib.h>
+#include <gnome-cups-init.h>
+
+typedef void (*GnomeCupsAsyncRequestCallback) (guint id,
+					       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,
@@ -17,9 +24,19 @@
 						    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);
+guint _gnome_cups_outstanding_request_count (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]