Ok, Here's the second pass at asynchronous work. It does two major things: we use a GThreadPool, and in addition cache open HTTP connections for 30 seconds. That way if we poll every 7 seconds, we won't open a new HTTP connection each time. This also incorporates Jody's feedback.
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:05:08 -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:05:08 -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:05:08 -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:05:08 -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:05:08 -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:05:08 -0000
@@ -10,6 +10,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;
+ guint 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 gpointer request_thread_main (GnomeCupsRequest *request);
+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 +120,261 @@
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_print ("stopping unused threads\n");
+ 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 = (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_print ("closing unused connections\n");
+ g_get_current_time (¤t_time);
+ g_hash_table_foreach_remove (connection_cache_map,
+ (GHRFunc) close_unused_connection,
+ ¤t_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);
+ g_print ("destroying connection to %s\n", connection->server);
+ 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)
+{
+ g_print ("signalling completion\n");
+ 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 gpointer
+request_thread_main (GnomeCupsRequest *request)
+{
+ ipp_t *response;
+ ipp_status_t status;
+
+ if (request->cancelled) {
+ do_signal_complete (request);
+ return NULL;
+ }
+
+ 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;
+
+ 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 NULL;
+}
+
+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 +403,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 +453,145 @@
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) {
+ g_print ("creating new connection to %s\n", server);
+ 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_mutex_lock (connection->mutex);
+ connection->refcount++;
+ g_print ("connection %s refcount: %d\n", server, connection->refcount);
+ g_mutex_unlock (connection->mutex);
+
+ 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_print ("pushing request id %d\n", req->id);
+ 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:05:08 -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