* 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 <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include "glib.h"
#include <gioerror.h>
-#include "gwakeup.h"
+#include "glib-private.h"
#include "gcancellable.h"
#include "glibintl.h"
static guint signals[LAST_SIGNAL] = { 0 };
-G_DEFINE_TYPE (GCancellable, g_cancellable, G_TYPE_OBJECT);
+G_DEFINE_TYPE_WITH_PRIVATE (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 GPrivate current_cancellable;
+static GMutex cancellable_mutex;
+static GCond cancellable_cond;
static void
g_cancellable_finalize (GObject *object)
GCancellable *cancellable = G_CANCELLABLE (object);
if (cancellable->priv->wakeup)
- g_wakeup_free (cancellable->priv->wakeup);
+ GLIB_PRIVATE_CALL (g_wakeup_free) (cancellable->priv->wakeup);
G_OBJECT_CLASS (g_cancellable_parent_class)->finalize (object);
}
{
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;
/**
*
* 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
- * <emphasis>after</emphasis> 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
* 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))
+ * |[<!-- language="C" -->
+ * // 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;
* 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);
* ]|
*
static void
g_cancellable_init (GCancellable *cancellable)
{
- cancellable->priv = G_TYPE_INSTANCE_GET_PRIVATE (cancellable,
- G_TYPE_CANCELLABLE,
- GCancellablePrivate);
+ cancellable->priv = g_cancellable_get_instance_private (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.
**/
* @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.
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);
}
/**
{
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);
}
/**
*
* 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;
* 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.
**/
void
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)
{
if (priv->wakeup)
- g_wakeup_acknowledge (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)
/**
* 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))
{
_("Operation was cancelled"));
return TRUE;
}
-
+
return FALSE;
}
/**
* 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
return FALSE;
g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
- G_LOCK(cancellable);
+ g_mutex_lock (&cancellable_mutex);
cancellable->priv->fd_refcount++;
if (cancellable->priv->wakeup == NULL)
{
- cancellable->priv->wakeup = g_wakeup_new ();
+ cancellable->priv->wakeup = GLIB_PRIVATE_CALL (g_wakeup_new) ();
if (cancellable->priv->cancelled)
- g_wakeup_signal (cancellable->priv->wakeup);
+ GLIB_PRIVATE_CALL (g_wakeup_signal) (cancellable->priv->wakeup);
}
- g_wakeup_get_pollfd (cancellable->priv->wakeup, pollfd);
+ GLIB_PRIVATE_CALL (g_wakeup_get_pollfd) (cancellable->priv->wakeup, pollfd);
- G_UNLOCK(cancellable);
+ g_mutex_unlock (&cancellable_mutex);
return TRUE;
}
priv = cancellable->priv;
- G_LOCK (cancellable);
+ g_mutex_lock (&cancellable_mutex);
+
priv->fd_refcount--;
if (priv->fd_refcount == 0)
{
- g_wakeup_free (priv->wakeup);
+ GLIB_PRIVATE_CALL (g_wakeup_free) (priv->wakeup);
priv->wakeup = NULL;
}
- G_UNLOCK (cancellable);
+
+ g_mutex_unlock (&cancellable_mutex);
}
/**
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_running = TRUE;
if (priv->wakeup)
- g_wakeup_signal (priv->wakeup);
+ GLIB_PRIVATE_CALL (g_wakeup_signal) (priv->wakeup);
- G_UNLOCK(cancellable);
+ 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);
}
* @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
*
* 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.
*
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;
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
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
GCancellableSourceFunc func = (GCancellableSourceFunc)callback;
GCancellableSource *cancellable_source = (GCancellableSource *)source;
+ g_source_set_ready_time (source, -1);
return (*func) (cancellable_source->cancellable, user_data);
}
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
{
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);
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)g_cclosure_marshal_generic,
};
/**
* 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
* 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.
+ * Returns: (transfer full): the new #GSource.
*
* Since: 2.28
*/
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;