mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-22 17:08:53 +02:00
Use GSource dispose function for safely disconnecting the GCancellable source cancelled signal handler
If not doing this it might happen that the cancelled signal is emitted between reaching a reference count of 0 and finalizing the GSource, at which point part of the GSource is already freed and calling any GSource functions is dangerous. Instead do this from the dispose function. At this time the GSource is not partially freed yet and calling any GSource API is safe as long as we ensure that we have a strong reference to the GSource before calling any GSource API.
This commit is contained in:
@@ -642,20 +642,19 @@ typedef struct {
|
|||||||
GSource source;
|
GSource source;
|
||||||
|
|
||||||
GCancellable *cancellable;
|
GCancellable *cancellable;
|
||||||
guint cancelled_handler;
|
gulong cancelled_handler;
|
||||||
} GCancellableSource;
|
} GCancellableSource;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can't guarantee that the source still has references, so we are
|
* The reference count of the GSource might be 0 at this point but it is not
|
||||||
* relying on the fact that g_source_set_ready_time() no longer makes
|
* finalized yet and its dispose function did not run yet, or otherwise we
|
||||||
* assertions about the reference count - the source might be in the
|
* would have disconnected the signal handler already and due to the signal
|
||||||
* window between last-unref and finalize, during which its refcount
|
* emission lock it would be impossible to call the signal handler at that
|
||||||
* is officially 0. However, we *can* guarantee that it's OK to
|
* point. That is: at this point we either have a fully valid GSource, or
|
||||||
* dereference it in a limited way, because we know we haven't yet reached
|
* it's not disposed or finalized yet and we can still resurrect it as needed.
|
||||||
* cancellable_source_finalize() - if we had, then we would have waited
|
*
|
||||||
* for signal emission to finish, then disconnected the signal handler
|
* As such we first ensure that we have a strong reference to the GSource in
|
||||||
* under the lock.
|
* here before calling any other GSource API.
|
||||||
* See https://bugzilla.gnome.org/show_bug.cgi?id=791754
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
cancellable_source_cancelled (GCancellable *cancellable,
|
cancellable_source_cancelled (GCancellable *cancellable,
|
||||||
@@ -663,7 +662,9 @@ cancellable_source_cancelled (GCancellable *cancellable,
|
|||||||
{
|
{
|
||||||
GSource *source = user_data;
|
GSource *source = user_data;
|
||||||
|
|
||||||
|
g_source_ref (source);
|
||||||
g_source_set_ready_time (source, 0);
|
g_source_set_ready_time (source, 0);
|
||||||
|
g_source_unref (source);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@@ -679,15 +680,15 @@ cancellable_source_dispatch (GSource *source,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cancellable_source_finalize (GSource *source)
|
cancellable_source_dispose (GSource *source)
|
||||||
{
|
{
|
||||||
GCancellableSource *cancellable_source = (GCancellableSource *)source;
|
GCancellableSource *cancellable_source = (GCancellableSource *)source;
|
||||||
|
|
||||||
if (cancellable_source->cancellable)
|
if (cancellable_source->cancellable)
|
||||||
{
|
{
|
||||||
g_cancellable_disconnect (cancellable_source->cancellable,
|
g_clear_signal_handler (&cancellable_source->cancelled_handler,
|
||||||
cancellable_source->cancelled_handler);
|
cancellable_source->cancellable);
|
||||||
g_object_unref (cancellable_source->cancellable);
|
g_clear_object (&cancellable_source->cancellable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,7 +721,7 @@ static GSourceFuncs cancellable_source_funcs =
|
|||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
cancellable_source_dispatch,
|
cancellable_source_dispatch,
|
||||||
cancellable_source_finalize,
|
NULL,
|
||||||
(GSourceFunc)cancellable_source_closure_callback,
|
(GSourceFunc)cancellable_source_closure_callback,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -750,6 +751,7 @@ g_cancellable_source_new (GCancellable *cancellable)
|
|||||||
|
|
||||||
source = g_source_new (&cancellable_source_funcs, sizeof (GCancellableSource));
|
source = g_source_new (&cancellable_source_funcs, sizeof (GCancellableSource));
|
||||||
g_source_set_name (source, "GCancellable");
|
g_source_set_name (source, "GCancellable");
|
||||||
|
g_source_set_dispose_function (source, cancellable_source_dispose);
|
||||||
cancellable_source = (GCancellableSource *)source;
|
cancellable_source = (GCancellableSource *)source;
|
||||||
|
|
||||||
if (cancellable)
|
if (cancellable)
|
||||||
|
Reference in New Issue
Block a user