* 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>
*/
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 GPrivate current_cancellable;
-G_LOCK_DEFINE_STATIC(cancellable);
-static GCond *cancellable_cond = NULL;
+static GMutex cancellable_mutex;
+static GCond cancellable_cond;
static void
g_cancellable_finalize (GObject *object)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- g_type_class_add_private (klass, sizeof (GCancellablePrivate));
-
- if (cancellable_cond == NULL)
- 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);
}
/**
*
* 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)
*
* 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)
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_LOCK_NAME (cancellable));
+ g_cond_wait (&cancellable_cond, &cancellable_mutex);
}
if (priv->cancelled)
priv->cancelled = FALSE;
}
- G_UNLOCK(cancellable);
+
+ g_mutex_unlock (&cancellable_mutex);
}
/**
/**
* 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++;
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)
{
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;
}
if (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_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.
+ * The new #GSource will hold a reference to the #GCancellable.
+ *
+ * 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;