X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgcancellable.c;h=258b88da56a8c9260a6ef422772c999da54d3ad3;hb=2138deb07ebb7d7e541c0cd35b966e107d1bf800;hp=3573ee82cabb92fc39fc72eb262fc10e45609153;hpb=542215b78ac52ffb96386ef63ee782981a1b8e49;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gcancellable.c b/gio/gcancellable.c index 3573ee8..258b88d 100644 --- a/gio/gcancellable.c +++ b/gio/gcancellable.c @@ -13,25 +13,16 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. + * Public License along with this library; if not, see . * * Author: Alexander Larsson */ #include "config.h" #include "glib.h" -#ifdef G_OS_UNIX -#include "glib-unix.h" -#endif #include -#ifdef G_OS_WIN32 -#include -#include -#endif +#include "glib-private.h" #include "gcancellable.h" -#include "gio-marshal.h" #include "glibintl.h" @@ -57,55 +48,24 @@ struct _GCancellablePrivate guint cancelled_running_waiting : 1; guint fd_refcount; - int cancel_pipe[2]; - -#ifdef G_OS_WIN32 - HANDLE event; -#endif + GWakeup *wakeup; }; static guint signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_TYPE (GCancellable, g_cancellable, G_TYPE_OBJECT); - -static GStaticPrivate current_cancellable = G_STATIC_PRIVATE_INIT; -G_LOCK_DEFINE_STATIC(cancellable); -static GCond *cancellable_cond = NULL; - -static void -g_cancellable_close_pipe (GCancellable *cancellable) -{ - GCancellablePrivate *priv; - - priv = cancellable->priv; - - if (priv->cancel_pipe[0] != -1) - { - close (priv->cancel_pipe[0]); - priv->cancel_pipe[0] = -1; - } - - if (priv->cancel_pipe[1] != -1) - { - close (priv->cancel_pipe[1]); - priv->cancel_pipe[1] = -1; - } +G_DEFINE_TYPE_WITH_PRIVATE (GCancellable, g_cancellable, G_TYPE_OBJECT) -#ifdef G_OS_WIN32 - if (priv->event) - { - CloseHandle (priv->event); - priv->event = NULL; - } -#endif -} +static GPrivate current_cancellable; +static GMutex cancellable_mutex; +static GCond cancellable_cond; static void g_cancellable_finalize (GObject *object) { GCancellable *cancellable = G_CANCELLABLE (object); - g_cancellable_close_pipe (cancellable); + if (cancellable->priv->wakeup) + GLIB_PRIVATE_CALL (g_wakeup_free) (cancellable->priv->wakeup); G_OBJECT_CLASS (g_cancellable_parent_class)->finalize (object); } @@ -115,11 +75,6 @@ g_cancellable_class_init (GCancellableClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - g_type_class_add_private (klass, sizeof (GCancellablePrivate)); - - if (cancellable_cond == NULL && g_thread_supported ()) - cancellable_cond = g_cond_new (); - gobject_class->finalize = g_cancellable_finalize; /** @@ -135,16 +90,14 @@ g_cancellable_class_init (GCancellableClass *klass) * * Note that disconnecting from this signal (or any signal) in a * multi-threaded program is prone to race conditions. For instance - * it is possible that a signal handler may be invoked even - * after a call to - * g_signal_handler_disconnect() for that handler has already - * returned. + * it is possible that a signal handler may be invoked even after + * a call to g_signal_handler_disconnect() for that handler has + * already returned. * - * There is also a problem when cancellation happen - * right before connecting to the signal. If this happens the - * signal will unexpectedly not be emitted, and checking before - * connecting to the signal leaves a race condition where this is - * still happening. + * There is also a problem when cancellation happens right before + * connecting to the signal. If this happens the signal will + * unexpectedly not be emitted, and checking before connecting to + * the signal leaves a race condition where this is still happening. * * In order to make it safe and easy to connect handlers there * are two helper functions: g_cancellable_connect() and @@ -152,13 +105,13 @@ g_cancellable_class_init (GCancellableClass *klass) * like this. * * An example of how to us this: - * |[ - * /* Make sure we don't do any unnecessary work if already cancelled */ - * if (g_cancellable_set_error_if_cancelled (cancellable)) + * |[ + * // Make sure we don't do unnecessary work if already cancelled + * if (g_cancellable_set_error_if_cancelled (cancellable, error)) * return; * - * /* Set up all the data needed to be able to - * * handle cancellation of the operation */ + * // Set up all the data needed to be able to handle cancellation + * // of the operation * my_data = my_data_new (...); * * id = 0; @@ -167,12 +120,12 @@ g_cancellable_class_init (GCancellableClass *klass) * G_CALLBACK (cancelled_handler) * data, NULL); * - * /* cancellable operation here... */ + * // cancellable operation here... * * g_cancellable_disconnect (cancellable, id); * - * /* cancelled_handler is never called after this, it - * * is now safe to free the data */ + * // cancelled_handler is never called after this, it is now safe + * // to free the data * my_data_free (my_data); * ]| * @@ -191,43 +144,10 @@ g_cancellable_class_init (GCancellableClass *klass) } -#ifndef G_OS_WIN32 - -static void -g_cancellable_open_pipe (GCancellable *cancellable) -{ - GCancellablePrivate *priv; - - priv = cancellable->priv; - if (g_unix_open_pipe (priv->cancel_pipe, FD_CLOEXEC, NULL)) - { - /* Make them nonblocking, just to be sure we don't block - * on errors and stuff - */ - g_unix_set_fd_nonblocking (priv->cancel_pipe[0], TRUE, NULL); - g_unix_set_fd_nonblocking (priv->cancel_pipe[1], TRUE, NULL); - - if (priv->cancelled) - { - const char ch = 'x'; - gssize c; - - do - c = write (priv->cancel_pipe[1], &ch, 1); - while (c == -1 && errno == EINTR); - } - } -} -#endif - static void g_cancellable_init (GCancellable *cancellable) { - cancellable->priv = G_TYPE_INSTANCE_GET_PRIVATE (cancellable, - G_TYPE_CANCELLABLE, - GCancellablePrivate); - cancellable->priv->cancel_pipe[0] = -1; - cancellable->priv->cancel_pipe[1] = -1; + cancellable->priv = g_cancellable_get_instance_private (cancellable); } /** @@ -240,7 +160,7 @@ g_cancellable_init (GCancellable *cancellable) * and pass it to the operations. * * One #GCancellable can be used in multiple consecutive - * operations, but not in multiple concurrent operations. + * operations or in multiple concurrent operations. * * Returns: a #GCancellable. **/ @@ -255,7 +175,7 @@ g_cancellable_new (void) * @cancellable: a #GCancellable object * * Pushes @cancellable onto the cancellable stack. The current - * cancellable can then be recieved using g_cancellable_get_current(). + * cancellable can then be received using g_cancellable_get_current(). * * This is useful when implementing cancellable operations in * code that does not allow you to pass down the cancellable object. @@ -270,9 +190,9 @@ g_cancellable_push_current (GCancellable *cancellable) g_return_if_fail (cancellable != NULL); - l = g_static_private_get (¤t_cancellable); + l = g_private_get (¤t_cancellable); l = g_slist_prepend (l, cancellable); - g_static_private_set (¤t_cancellable, l, NULL); + g_private_set (¤t_cancellable, l); } /** @@ -287,13 +207,13 @@ g_cancellable_pop_current (GCancellable *cancellable) { GSList *l; - l = g_static_private_get (¤t_cancellable); + l = g_private_get (¤t_cancellable); g_return_if_fail (l != NULL); g_return_if_fail (l->data == cancellable); l = g_slist_delete_link (l, l); - g_static_private_set (¤t_cancellable, l, NULL); + g_private_set (¤t_cancellable, l); } /** @@ -301,15 +221,15 @@ g_cancellable_pop_current (GCancellable *cancellable) * * Gets the top cancellable from the stack. * - * Returns: (transfer none): a #GCancellable from the top of the stack, or %NULL - * if the stack is empty. + * Returns: (nullable) (transfer none): a #GCancellable from the top + * of the stack, or %NULL if the stack is empty. **/ GCancellable * g_cancellable_get_current (void) { GSList *l; - l = g_static_private_get (¤t_cancellable); + l = g_private_get (¤t_cancellable); if (l == NULL) return NULL; @@ -320,7 +240,17 @@ g_cancellable_get_current (void) * g_cancellable_reset: * @cancellable: a #GCancellable object. * - * Resets @cancellable to its uncancelled state. + * Resets @cancellable to its uncancelled state. + * + * If cancellable is currently in use by any cancellable operation + * then the behavior of this function is undefined. + * + * Note that it is generally not a good idea to reuse an existing + * cancellable for more operations after it has been cancelled once, + * as this function might tempt you to do. The recommended practice + * is to drop the reference to a cancellable after cancelling it, + * and let it die with the outstanding async operations. You should + * create a fresh cancellable for further async operations. **/ void g_cancellable_reset (GCancellable *cancellable) @@ -329,48 +259,35 @@ g_cancellable_reset (GCancellable *cancellable) g_return_if_fail (G_IS_CANCELLABLE (cancellable)); - G_LOCK(cancellable); + g_mutex_lock (&cancellable_mutex); priv = cancellable->priv; - + while (priv->cancelled_running) { priv->cancelled_running_waiting = TRUE; - g_cond_wait (cancellable_cond, - g_static_mutex_get_mutex (& G_LOCK_NAME (cancellable))); + g_cond_wait (&cancellable_cond, &cancellable_mutex); } - + if (priv->cancelled) { - /* Make sure we're not leaving old cancel state around */ - -#ifdef G_OS_WIN32 - if (priv->event) - ResetEvent (priv->event); -#endif - if (priv->cancel_pipe[0] != -1) - { - gssize c; - char ch; - - do - c = read (priv->cancel_pipe[0], &ch, 1); - while (c == -1 && errno == EINTR); - } + if (priv->wakeup) + GLIB_PRIVATE_CALL (g_wakeup_acknowledge) (priv->wakeup); priv->cancelled = FALSE; } - G_UNLOCK(cancellable); + + g_mutex_unlock (&cancellable_mutex); } /** * g_cancellable_is_cancelled: - * @cancellable: a #GCancellable or NULL. - * + * @cancellable: (allow-none): a #GCancellable or %NULL + * * Checks if a cancellable job has been cancelled. - * - * Returns: %TRUE if @cancellable is cancelled, - * FALSE if called with %NULL or if item is not cancelled. + * + * Returns: %TRUE if @cancellable is cancelled, + * FALSE if called with %NULL or if item is not cancelled. **/ gboolean g_cancellable_is_cancelled (GCancellable *cancellable) @@ -380,17 +297,17 @@ g_cancellable_is_cancelled (GCancellable *cancellable) /** * g_cancellable_set_error_if_cancelled: - * @cancellable: a #GCancellable object. - * @error: #GError to append error state to. - * + * @cancellable: (allow-none): a #GCancellable or %NULL + * @error: #GError to append error state to + * * If the @cancellable is cancelled, sets the error to notify * that the operation was cancelled. - * - * Returns: %TRUE if @cancellable was cancelled, %FALSE if it was not. - **/ + * + * Returns: %TRUE if @cancellable was cancelled, %FALSE if it was not + */ gboolean g_cancellable_set_error_if_cancelled (GCancellable *cancellable, - GError **error) + GError **error) { if (g_cancellable_is_cancelled (cancellable)) { @@ -400,7 +317,7 @@ g_cancellable_set_error_if_cancelled (GCancellable *cancellable, _("Operation was cancelled")); return TRUE; } - + return FALSE; } @@ -428,32 +345,23 @@ g_cancellable_set_error_if_cancelled (GCancellable *cancellable, int g_cancellable_get_fd (GCancellable *cancellable) { - GCancellablePrivate *priv; - int fd; + GPollFD pollfd; if (cancellable == NULL) - return -1; - - priv = cancellable->priv; + return -1; #ifdef G_OS_WIN32 - return -1; + pollfd.fd = -1; #else - G_LOCK(cancellable); - if (priv->cancel_pipe[0] == -1) - g_cancellable_open_pipe (cancellable); - fd = priv->cancel_pipe[0]; - if (fd != -1) - priv->fd_refcount++; - G_UNLOCK(cancellable); + g_cancellable_make_pollfd (cancellable, &pollfd); #endif - return fd; + return pollfd.fd; } /** * g_cancellable_make_pollfd: - * @cancellable: a #GCancellable or %NULL + * @cancellable: (allow-none): a #GCancellable or %NULL * @pollfd: a pointer to a #GPollFD * * Creates a #GPollFD corresponding to @cancellable; this can be passed @@ -488,39 +396,21 @@ g_cancellable_make_pollfd (GCancellable *cancellable, GPollFD *pollfd) return FALSE; g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE); - { -#ifdef G_OS_WIN32 - GCancellablePrivate *priv; - - priv = cancellable->priv; - G_LOCK(cancellable); - if (priv->event == NULL) - { - /* A manual reset anonymous event, starting unset */ - priv->event = CreateEvent (NULL, TRUE, FALSE, NULL); - if (priv->event == NULL) - { - G_UNLOCK(cancellable); - return FALSE; - } - if (priv->cancelled) - SetEvent(priv->event); - } - priv->fd_refcount++; - G_UNLOCK(cancellable); - - pollfd->fd = (gintptr)priv->event; -#else /* !G_OS_WIN32 */ - int fd = g_cancellable_get_fd (cancellable); - - if (fd == -1) - return FALSE; - pollfd->fd = fd; -#endif /* G_OS_WIN32 */ - } - - pollfd->events = G_IO_IN; - pollfd->revents = 0; + g_mutex_lock (&cancellable_mutex); + + cancellable->priv->fd_refcount++; + + if (cancellable->priv->wakeup == NULL) + { + cancellable->priv->wakeup = GLIB_PRIVATE_CALL (g_wakeup_new) (); + + if (cancellable->priv->cancelled) + GLIB_PRIVATE_CALL (g_wakeup_signal) (cancellable->priv->wakeup); + } + + GLIB_PRIVATE_CALL (g_wakeup_get_pollfd) (cancellable->priv->wakeup, pollfd); + + g_mutex_unlock (&cancellable_mutex); return TRUE; } @@ -554,11 +444,16 @@ g_cancellable_release_fd (GCancellable *cancellable) priv = cancellable->priv; - G_LOCK (cancellable); + g_mutex_lock (&cancellable_mutex); + priv->fd_refcount--; if (priv->fd_refcount == 0) - g_cancellable_close_pipe (cancellable); - G_UNLOCK (cancellable); + { + GLIB_PRIVATE_CALL (g_wakeup_free) (priv->wakeup); + priv->wakeup = NULL; + } + + g_mutex_unlock (&cancellable_mutex); } /** @@ -591,41 +486,33 @@ g_cancellable_cancel (GCancellable *cancellable) priv = cancellable->priv; - G_LOCK(cancellable); + g_mutex_lock (&cancellable_mutex); + if (priv->cancelled) { - G_UNLOCK (cancellable); + g_mutex_unlock (&cancellable_mutex); return; } priv->cancelled = TRUE; priv->cancelled_running = TRUE; -#ifdef G_OS_WIN32 - if (priv->event) - SetEvent (priv->event); -#endif - if (priv->cancel_pipe[1] != -1) - { - const char ch = 'x'; - gssize c; - do - c = write (priv->cancel_pipe[1], &ch, 1); - while (c == -1 && errno == EINTR); - } - G_UNLOCK(cancellable); + if (priv->wakeup) + GLIB_PRIVATE_CALL (g_wakeup_signal) (priv->wakeup); + + g_mutex_unlock (&cancellable_mutex); g_object_ref (cancellable); g_signal_emit (cancellable, signals[CANCELLED], 0); - G_LOCK(cancellable); + g_mutex_lock (&cancellable_mutex); priv->cancelled_running = FALSE; if (priv->cancelled_running_waiting) - g_cond_broadcast (cancellable_cond); + g_cond_broadcast (&cancellable_cond); priv->cancelled_running_waiting = FALSE; - G_UNLOCK(cancellable); + g_mutex_unlock (&cancellable_mutex); g_object_unref (cancellable); } @@ -635,7 +522,7 @@ g_cancellable_cancel (GCancellable *cancellable) * @cancellable: A #GCancellable. * @callback: The #GCallback to connect. * @data: Data to pass to @callback. - * @data_destroy_func: Free function for @data or %NULL. + * @data_destroy_func: (allow-none): Free function for @data or %NULL. * * Convenience function to connect to the #GCancellable::cancelled * signal. Also handles the race condition that may happen @@ -651,6 +538,11 @@ g_cancellable_cancel (GCancellable *cancellable) * * See #GCancellable::cancelled for details on how to use this. * + * Since GLib 2.40, the lock protecting @cancellable is not held when + * @callback is invoked. This lifts a restriction in place for + * earlier GLib versions which now makes it easier to write cleanup + * code that unconditionally invokes e.g. g_cancellable_cancel(). + * * Returns: The id of the signal handler or 0 if @cancellable has already * been cancelled. * @@ -666,13 +558,15 @@ g_cancellable_connect (GCancellable *cancellable, g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), 0); - G_LOCK (cancellable); + g_mutex_lock (&cancellable_mutex); if (cancellable->priv->cancelled) { void (*_callback) (GCancellable *cancellable, gpointer user_data); + g_mutex_unlock (&cancellable_mutex); + _callback = (void *)callback; id = 0; @@ -687,15 +581,17 @@ g_cancellable_connect (GCancellable *cancellable, callback, data, (GClosureNotify) data_destroy_func, 0); + + g_mutex_unlock (&cancellable_mutex); } - G_UNLOCK (cancellable); + return id; } /** * g_cancellable_disconnect: - * @cancellable: A #GCancellable or %NULL. + * @cancellable: (allow-none): A #GCancellable or %NULL. * @handler_id: Handler id of the handler to be disconnected, or %0. * * Disconnects a handler from a cancellable instance similar to @@ -724,44 +620,36 @@ g_cancellable_disconnect (GCancellable *cancellable, if (handler_id == 0 || cancellable == NULL) return; - G_LOCK (cancellable); + g_mutex_lock (&cancellable_mutex); priv = cancellable->priv; while (priv->cancelled_running) { priv->cancelled_running_waiting = TRUE; - g_cond_wait (cancellable_cond, - g_static_mutex_get_mutex (& G_LOCK_NAME (cancellable))); + g_cond_wait (&cancellable_cond, &cancellable_mutex); } g_signal_handler_disconnect (cancellable, handler_id); - G_UNLOCK (cancellable); + + g_mutex_unlock (&cancellable_mutex); } typedef struct { GSource source; GCancellable *cancellable; - GPollFD pollfd; + guint cancelled_handler; } GCancellableSource; -static gboolean -cancellable_source_prepare (GSource *source, - gint *timeout) -{ - GCancellableSource *cancellable_source = (GCancellableSource *)source; - - *timeout = -1; - return g_cancellable_is_cancelled (cancellable_source->cancellable); -} - -static gboolean -cancellable_source_check (GSource *source) +static void +cancellable_source_cancelled (GCancellable *cancellable, + gpointer user_data) { - GCancellableSource *cancellable_source = (GCancellableSource *)source; + GSource *source = user_data; - return g_cancellable_is_cancelled (cancellable_source->cancellable); + if (!g_source_is_destroyed (source)) + g_source_set_ready_time (source, 0); } static gboolean @@ -772,6 +660,7 @@ cancellable_source_dispatch (GSource *source, GCancellableSourceFunc func = (GCancellableSourceFunc)callback; GCancellableSource *cancellable_source = (GCancellableSource *)source; + g_source_set_ready_time (source, -1); return (*func) (cancellable_source->cancellable, user_data); } @@ -781,7 +670,11 @@ cancellable_source_finalize (GSource *source) GCancellableSource *cancellable_source = (GCancellableSource *)source; if (cancellable_source->cancellable) - g_object_unref (cancellable_source->cancellable); + { + g_cancellable_disconnect (cancellable_source->cancellable, + cancellable_source->cancelled_handler); + g_object_unref (cancellable_source->cancellable); + } } static gboolean @@ -790,8 +683,8 @@ cancellable_source_closure_callback (GCancellable *cancellable, { GClosure *closure = data; - GValue params = { 0, }; - GValue result_value = { 0, }; + GValue params = G_VALUE_INIT; + GValue result_value = G_VALUE_INIT; gboolean result; g_value_init (&result_value, G_TYPE_BOOLEAN); @@ -810,17 +703,16 @@ cancellable_source_closure_callback (GCancellable *cancellable, static GSourceFuncs cancellable_source_funcs = { - cancellable_source_prepare, - cancellable_source_check, + NULL, + NULL, cancellable_source_dispatch, cancellable_source_finalize, (GSourceFunc)cancellable_source_closure_callback, - (GSourceDummyMarshal)_gio_marshal_BOOLEAN__VOID, }; /** * g_cancellable_source_new: (skip) - * @cancellable: a #GCancellable, or %NULL + * @cancellable: (allow-none): a #GCancellable, or %NULL * * Creates a source that triggers if @cancellable is cancelled and * calls its callback of type #GCancellableSourceFunc. This is @@ -830,7 +722,9 @@ static GSourceFuncs cancellable_source_funcs = * For convenience, you can call this with a %NULL #GCancellable, * in which case the source will never trigger. * - * Return value: (transfer full): the new #GSource. + * The new #GSource will hold a reference to the #GCancellable. + * + * Returns: (transfer full): the new #GSource. * * Since: 2.28 */ @@ -844,11 +738,19 @@ g_cancellable_source_new (GCancellable *cancellable) g_source_set_name (source, "GCancellable"); cancellable_source = (GCancellableSource *)source; - if (g_cancellable_make_pollfd (cancellable, - &cancellable_source->pollfd)) + if (cancellable) { cancellable_source->cancellable = g_object_ref (cancellable); - g_source_add_poll (source, &cancellable_source->pollfd); + + /* We intentionally don't use g_cancellable_connect() here, + * because we don't want the "at most once" behavior. + */ + cancellable_source->cancelled_handler = + g_signal_connect (cancellable, "cancelled", + G_CALLBACK (cancellable_source_cancelled), + source); + if (g_cancellable_is_cancelled (cancellable)) + g_source_set_ready_time (source, 0); } return source;