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