mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-27 07:56:14 +01:00
Add g_source_set_dispose_function() for setting a dispose function for GSource
This allows GSource implementors to safely clear any other references to the GSource while the GSource is still valid, unlike when doing the same from the finalize function. After the dispose function has run, it is valid for the reference count of the GSource to be > 0 again to allow the case where another thread in the mean-time got access to the GSource reference before the dispose function was called. This allows fixing a thread-safety issue in the GCancellable, GstBus and various other GSource implementations.
This commit is contained in:
parent
691485fb7c
commit
0adf5cae35
57
glib/gmain.c
57
glib/gmain.c
@ -361,6 +361,8 @@ struct _GSourcePrivate
|
|||||||
* let it remain empty on Windows) to avoid #ifdef all over the place.
|
* let it remain empty on Windows) to avoid #ifdef all over the place.
|
||||||
*/
|
*/
|
||||||
GSList *fds;
|
GSList *fds;
|
||||||
|
|
||||||
|
GSourceDisposeFunc dispose;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _GSourceIter
|
typedef struct _GSourceIter
|
||||||
@ -926,6 +928,39 @@ g_source_new (GSourceFuncs *source_funcs,
|
|||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_source_set_dispose_function:
|
||||||
|
* @source: A #GSource to set the dispose function on
|
||||||
|
* @dispose: #GSourceDisposeFunc to set on the source
|
||||||
|
*
|
||||||
|
* Set @dispose as dispose function on @source. @dispose will be called once
|
||||||
|
* the reference count of @source reaches 0 but before any of the state of the
|
||||||
|
* source is freed, especially before the finalize function is called.
|
||||||
|
*
|
||||||
|
* This means that at this point @source is still a valid #GSource and it is
|
||||||
|
* allow for the reference count to increase again until @dispose returns.
|
||||||
|
*
|
||||||
|
* The dispose function can be used to clear any "weak" references to the
|
||||||
|
* @source in other data structures in a thread-safe way where it is possible
|
||||||
|
* for another thread to increase the reference count of @source again while
|
||||||
|
* it is being freed.
|
||||||
|
*
|
||||||
|
* The finalize function can not be used for this purpose as at that point
|
||||||
|
* @source is already partially freed and not valid anymore.
|
||||||
|
*
|
||||||
|
* This should only ever be called from #GSource implementations.
|
||||||
|
*
|
||||||
|
* Since: 2.64
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
g_source_set_dispose_function (GSource *source,
|
||||||
|
GSourceDisposeFunc dispose)
|
||||||
|
{
|
||||||
|
g_return_if_fail (source != NULL);
|
||||||
|
g_return_if_fail (source->priv->dispose == NULL);
|
||||||
|
source->priv->dispose = dispose;
|
||||||
|
}
|
||||||
|
|
||||||
/* Holds context's lock */
|
/* Holds context's lock */
|
||||||
static void
|
static void
|
||||||
g_source_iter_init (GSourceIter *iter,
|
g_source_iter_init (GSourceIter *iter,
|
||||||
@ -2094,6 +2129,28 @@ g_source_unref_internal (GSource *source,
|
|||||||
|
|
||||||
if (g_atomic_int_dec_and_test (&source->ref_count))
|
if (g_atomic_int_dec_and_test (&source->ref_count))
|
||||||
{
|
{
|
||||||
|
/* If there's a dispose function, call this first */
|
||||||
|
if (source->priv->dispose)
|
||||||
|
{
|
||||||
|
/* Temporarily increase the ref count again so that GSource methods
|
||||||
|
* can be called from dispose(). */
|
||||||
|
g_atomic_int_inc (&source->ref_count);
|
||||||
|
if (context)
|
||||||
|
UNLOCK_CONTEXT (context);
|
||||||
|
source->priv->dispose (source);
|
||||||
|
if (context)
|
||||||
|
LOCK_CONTEXT (context);
|
||||||
|
|
||||||
|
/* Now the reference count might be bigger than 0 again, in which
|
||||||
|
* case we simply return from here before freeing the source */
|
||||||
|
if (!g_atomic_int_dec_and_test (&source->ref_count))
|
||||||
|
{
|
||||||
|
if (!have_lock && context)
|
||||||
|
UNLOCK_CONTEXT (context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TRACE (GLIB_SOURCE_BEFORE_FREE (source, context,
|
TRACE (GLIB_SOURCE_BEFORE_FREE (source, context,
|
||||||
source->source_funcs->finalize));
|
source->source_funcs->finalize));
|
||||||
|
|
||||||
|
21
glib/gmain.h
21
glib/gmain.h
@ -204,6 +204,20 @@ typedef gboolean (*GSourceFunc) (gpointer user_data);
|
|||||||
typedef void (*GChildWatchFunc) (GPid pid,
|
typedef void (*GChildWatchFunc) (GPid pid,
|
||||||
gint status,
|
gint status,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GSourceDisposeFunc:
|
||||||
|
* @source: #GSource that is currently being disposed
|
||||||
|
*
|
||||||
|
* Dispose function for @source. See g_source_set_dispose_function() for
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* Since: 2.64
|
||||||
|
*/
|
||||||
|
GLIB_AVAILABLE_TYPE_IN_2_64
|
||||||
|
typedef void (*GSourceDisposeFunc) (GSource *source);
|
||||||
|
|
||||||
struct _GSource
|
struct _GSource
|
||||||
{
|
{
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
@ -536,6 +550,13 @@ GMainContext *g_main_loop_get_context (GMainLoop *loop);
|
|||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
GSource *g_source_new (GSourceFuncs *source_funcs,
|
GSource *g_source_new (GSourceFuncs *source_funcs,
|
||||||
guint struct_size);
|
guint struct_size);
|
||||||
|
|
||||||
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||||
|
GLIB_AVAILABLE_IN_2_64
|
||||||
|
void g_source_set_dispose_function (GSource *source,
|
||||||
|
GSourceDisposeFunc dispose);
|
||||||
|
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||||
|
|
||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
GSource *g_source_ref (GSource *source);
|
GSource *g_source_ref (GSource *source);
|
||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
|
Loading…
Reference in New Issue
Block a user