mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-07-31 06:13:29 +02:00
Merge branch 'simultaneous-main-context-source-destruction' into 'main'
glib: add a proper weak-ref-like relationship between GSource and GMainContext Closes #803 See merge request GNOME/glib!2187
This commit is contained in:
273
glib/gmain.c
273
glib/gmain.c
@@ -391,7 +391,17 @@ static gboolean g_idle_dispatch (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data);
|
||||
|
||||
static void block_source (GSource *source);
|
||||
static void block_source (GSource *source,
|
||||
GMainContext *context);
|
||||
static GMainContext *source_dup_main_context (GSource *source);
|
||||
|
||||
/* Lock for serializing access for safe execution of
|
||||
* g_main_context_unref() with concurrent use of
|
||||
* g_source_destroy() and g_source_unref().
|
||||
*
|
||||
* Locking order is source_destroy_lock, then context lock.
|
||||
*/
|
||||
static GRWLock source_destroy_lock;
|
||||
|
||||
static GMainContext *glib_worker_context;
|
||||
|
||||
@@ -437,8 +447,6 @@ GSourceFuncs g_unix_signal_funcs =
|
||||
NULL, NULL
|
||||
};
|
||||
#endif /* !G_OS_WIN32 */
|
||||
G_LOCK_DEFINE_STATIC (main_context_list);
|
||||
static GSList *main_context_list = NULL;
|
||||
|
||||
GSourceFuncs g_timeout_funcs =
|
||||
{
|
||||
@@ -510,20 +518,42 @@ g_main_context_unref (GMainContext *context)
|
||||
GSList *s_iter, *remaining_sources = NULL;
|
||||
GSourceList *list;
|
||||
guint i;
|
||||
guint old_ref;
|
||||
GSource **pending_dispatches;
|
||||
gsize pending_dispatches_len;
|
||||
|
||||
g_return_if_fail (context != NULL);
|
||||
g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0);
|
||||
|
||||
if (!g_atomic_int_dec_and_test (&context->ref_count))
|
||||
return;
|
||||
retry_decrement:
|
||||
old_ref = g_atomic_int_get (&context->ref_count);
|
||||
if (old_ref > 1)
|
||||
{
|
||||
if (!g_atomic_int_compare_and_exchange (&context->ref_count, old_ref, old_ref - 1))
|
||||
goto retry_decrement;
|
||||
|
||||
G_LOCK (main_context_list);
|
||||
main_context_list = g_slist_remove (main_context_list, context);
|
||||
G_UNLOCK (main_context_list);
|
||||
return;
|
||||
}
|
||||
|
||||
g_rw_lock_writer_lock (&source_destroy_lock);
|
||||
|
||||
/* if a weak ref got to the source_destroy lock first, we need to retry */
|
||||
old_ref = g_atomic_int_add (&context->ref_count, -1);
|
||||
if (old_ref != 1)
|
||||
{
|
||||
g_rw_lock_writer_unlock (&source_destroy_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
LOCK_CONTEXT (context);
|
||||
pending_dispatches = (GSource **) g_ptr_array_steal (context->pending_dispatches, &pending_dispatches_len);
|
||||
UNLOCK_CONTEXT (context);
|
||||
|
||||
/* Free pending dispatches */
|
||||
for (i = 0; i < context->pending_dispatches->len; i++)
|
||||
g_source_unref_internal (context->pending_dispatches->pdata[i], context, FALSE);
|
||||
for (i = 0; i < pending_dispatches_len; i++)
|
||||
g_source_unref_internal (pending_dispatches[i], context, FALSE);
|
||||
|
||||
g_clear_pointer (&pending_dispatches, g_free);
|
||||
|
||||
/* g_source_iter_next() assumes the context is locked. */
|
||||
LOCK_CONTEXT (context);
|
||||
@@ -554,6 +584,11 @@ g_main_context_unref (GMainContext *context)
|
||||
g_source_destroy_internal (source, context, TRUE);
|
||||
}
|
||||
|
||||
g_rw_lock_writer_unlock (&source_destroy_lock);
|
||||
|
||||
/* the context is going to die now */
|
||||
g_return_if_fail (old_ref > 0);
|
||||
|
||||
sl_iter = context->source_lists.head;
|
||||
while (sl_iter != NULL)
|
||||
{
|
||||
@@ -562,20 +597,29 @@ g_main_context_unref (GMainContext *context)
|
||||
g_slice_free (GSourceList, list);
|
||||
}
|
||||
|
||||
g_hash_table_destroy (context->sources);
|
||||
g_hash_table_remove_all (context->sources);
|
||||
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_mutex_clear (&context->mutex);
|
||||
|
||||
g_ptr_array_free (context->pending_dispatches, TRUE);
|
||||
g_free (context->cached_poll_array);
|
||||
/* if the object has been reffed meanwhile by an internal weak ref, keep the
|
||||
* resources alive until the last reference is gone.
|
||||
*/
|
||||
if (old_ref == 1)
|
||||
{
|
||||
g_mutex_clear (&context->mutex);
|
||||
|
||||
poll_rec_list_free (context, context->poll_records);
|
||||
g_ptr_array_free (context->pending_dispatches, TRUE);
|
||||
g_free (context->cached_poll_array);
|
||||
|
||||
g_wakeup_free (context->wakeup);
|
||||
g_cond_clear (&context->cond);
|
||||
poll_rec_list_free (context, context->poll_records);
|
||||
|
||||
g_free (context);
|
||||
g_wakeup_free (context->wakeup);
|
||||
g_cond_clear (&context->cond);
|
||||
|
||||
g_hash_table_unref (context->sources);
|
||||
|
||||
g_free (context);
|
||||
}
|
||||
|
||||
/* And now finally get rid of our references to the sources. This will cause
|
||||
* them to be freed unless something else still has a reference to them. Due
|
||||
@@ -671,16 +715,11 @@ g_main_context_new_with_flags (GMainContextFlags flags)
|
||||
g_wakeup_get_pollfd (context->wakeup, &context->wake_up_rec);
|
||||
g_main_context_add_poll_unlocked (context, 0, &context->wake_up_rec);
|
||||
|
||||
G_LOCK (main_context_list);
|
||||
main_context_list = g_slist_append (main_context_list, context);
|
||||
|
||||
#ifdef G_MAIN_POLL_DEBUG
|
||||
if (_g_main_poll_debug)
|
||||
g_print ("created context=%p\n", context);
|
||||
#endif
|
||||
|
||||
G_UNLOCK (main_context_list);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -1333,6 +1372,22 @@ g_source_destroy_internal (GSource *source,
|
||||
UNLOCK_CONTEXT (context);
|
||||
}
|
||||
|
||||
static GMainContext *
|
||||
source_dup_main_context (GSource *source)
|
||||
{
|
||||
GMainContext *ret = NULL;
|
||||
|
||||
g_rw_lock_reader_lock (&source_destroy_lock);
|
||||
|
||||
ret = source->context;
|
||||
if (ret)
|
||||
g_atomic_int_inc (&ret->ref_count);
|
||||
|
||||
g_rw_lock_reader_unlock (&source_destroy_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_source_destroy:
|
||||
* @source: a #GSource
|
||||
@@ -1361,10 +1416,13 @@ g_source_destroy (GSource *source)
|
||||
g_return_if_fail (source != NULL);
|
||||
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
|
||||
|
||||
context = source->context;
|
||||
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
if (context)
|
||||
g_source_destroy_internal (source, context, FALSE);
|
||||
{
|
||||
g_source_destroy_internal (source, context, FALSE);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
else
|
||||
g_atomic_int_and (&source->flags, ~G_HOOK_FLAG_ACTIVE);
|
||||
}
|
||||
@@ -1390,15 +1448,19 @@ guint
|
||||
g_source_get_id (GSource *source)
|
||||
{
|
||||
guint result;
|
||||
GMainContext *context;
|
||||
|
||||
g_return_val_if_fail (source != NULL, 0);
|
||||
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, 0);
|
||||
g_return_val_if_fail (source->context != NULL, 0);
|
||||
context = source_dup_main_context (source);
|
||||
g_return_val_if_fail (context != NULL, 0);
|
||||
|
||||
LOCK_CONTEXT (source->context);
|
||||
LOCK_CONTEXT (context);
|
||||
result = source->source_id;
|
||||
UNLOCK_CONTEXT (source->context);
|
||||
UNLOCK_CONTEXT (context);
|
||||
|
||||
g_main_context_unref (context);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1415,6 +1477,10 @@ g_source_get_id (GSource *source)
|
||||
* [func@GLib.main_current_source]. But calling this function on a source
|
||||
* whose [struct@GLib.MainContext] has been destroyed is an error.
|
||||
*
|
||||
* If the associated [struct@GLib.MainContext] could be destroy concurrently from
|
||||
* a different thread, then this function is not safe to call and
|
||||
* [method@GLib.Source.dup_context] should be used instead.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): the #GMainContext with which the
|
||||
* source is associated, or %NULL if the context has not
|
||||
* yet been added to a source.
|
||||
@@ -1429,6 +1495,31 @@ g_source_get_context (GSource *source)
|
||||
return source->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_source_dup_context:
|
||||
* @source: a #GSource
|
||||
*
|
||||
* Gets the [struct@GLib.MainContext] with which the source is associated.
|
||||
*
|
||||
* You can call this on a source that has been destroyed. You can
|
||||
* always call this function on the source returned from
|
||||
* [func@GLib.main_current_source].
|
||||
*
|
||||
* Returns: (transfer full) (nullable): the [struct@GLib.MainContext] with which the
|
||||
* source is associated, or `NULL`.
|
||||
*
|
||||
* Since: 2.86
|
||||
**/
|
||||
GMainContext *
|
||||
g_source_dup_context (GSource *source)
|
||||
{
|
||||
g_return_val_if_fail (source != NULL, NULL);
|
||||
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, NULL);
|
||||
g_return_val_if_fail (source->context != NULL || !SOURCE_DESTROYED (source), NULL);
|
||||
|
||||
return source_dup_main_context (source);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_source_add_poll:
|
||||
* @source:a #GSource
|
||||
@@ -1459,7 +1550,7 @@ g_source_add_poll (GSource *source,
|
||||
g_return_if_fail (fd != NULL);
|
||||
g_return_if_fail (!SOURCE_DESTROYED (source));
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
if (context)
|
||||
LOCK_CONTEXT (context);
|
||||
@@ -1471,6 +1562,7 @@ g_source_add_poll (GSource *source,
|
||||
if (!SOURCE_BLOCKED (source))
|
||||
g_main_context_add_poll_unlocked (context, source->priority, fd);
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1496,7 +1588,7 @@ g_source_remove_poll (GSource *source,
|
||||
g_return_if_fail (fd != NULL);
|
||||
g_return_if_fail (!SOURCE_DESTROYED (source));
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
if (context)
|
||||
LOCK_CONTEXT (context);
|
||||
@@ -1508,6 +1600,7 @@ g_source_remove_poll (GSource *source,
|
||||
if (!SOURCE_BLOCKED (source))
|
||||
g_main_context_remove_poll_unlocked (context, fd);
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1552,7 +1645,7 @@ g_source_add_child_source (GSource *source,
|
||||
g_return_if_fail (child_source->context == NULL);
|
||||
g_return_if_fail (child_source->priv->parent_source == NULL);
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
if (context)
|
||||
LOCK_CONTEXT (context);
|
||||
@@ -1564,12 +1657,13 @@ g_source_add_child_source (GSource *source,
|
||||
child_source->priv->parent_source = source;
|
||||
g_source_set_priority_unlocked (child_source, NULL, source->priority);
|
||||
if (SOURCE_BLOCKED (source))
|
||||
block_source (child_source);
|
||||
block_source (child_source, NULL);
|
||||
|
||||
if (context)
|
||||
{
|
||||
g_source_attach_unlocked (child_source, context, TRUE);
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1614,7 +1708,7 @@ g_source_remove_child_source (GSource *source,
|
||||
g_return_if_fail (!SOURCE_DESTROYED (source));
|
||||
g_return_if_fail (!SOURCE_DESTROYED (child_source));
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
if (context)
|
||||
LOCK_CONTEXT (context);
|
||||
@@ -1622,7 +1716,10 @@ g_source_remove_child_source (GSource *source,
|
||||
g_child_source_remove_internal (child_source, context);
|
||||
|
||||
if (context)
|
||||
UNLOCK_CONTEXT (context);
|
||||
{
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1695,7 +1792,7 @@ g_source_set_callback_indirect (GSource *source,
|
||||
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
|
||||
g_return_if_fail (callback_funcs != NULL || callback_data == NULL);
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
if (context)
|
||||
LOCK_CONTEXT (context);
|
||||
@@ -1715,8 +1812,11 @@ g_source_set_callback_indirect (GSource *source,
|
||||
source->callback_funcs = callback_funcs;
|
||||
|
||||
if (context)
|
||||
UNLOCK_CONTEXT (context);
|
||||
|
||||
{
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
|
||||
if (old_cb_funcs)
|
||||
old_cb_funcs->unref (old_cb_data);
|
||||
}
|
||||
@@ -1813,14 +1913,14 @@ g_source_set_priority_unlocked (GSource *source,
|
||||
/* Remove the source from the context's source and then
|
||||
* add it back after so it is sorted in the correct place
|
||||
*/
|
||||
source_remove_from_context (source, source->context);
|
||||
source_remove_from_context (source, context);
|
||||
}
|
||||
|
||||
source->priority = priority;
|
||||
|
||||
if (context)
|
||||
{
|
||||
source_add_to_context (source, source->context);
|
||||
source_add_to_context (source, context);
|
||||
|
||||
if (!SOURCE_BLOCKED (source))
|
||||
{
|
||||
@@ -1876,13 +1976,16 @@ g_source_set_priority (GSource *source,
|
||||
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
|
||||
g_return_if_fail (source->priv->parent_source == NULL);
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
if (context)
|
||||
LOCK_CONTEXT (context);
|
||||
g_source_set_priority_unlocked (source, context, priority);
|
||||
if (context)
|
||||
UNLOCK_CONTEXT (context);
|
||||
{
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1942,7 +2045,7 @@ g_source_set_ready_time (GSource *source,
|
||||
g_return_if_fail (source != NULL);
|
||||
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
if (context)
|
||||
LOCK_CONTEXT (context);
|
||||
@@ -1950,8 +2053,10 @@ g_source_set_ready_time (GSource *source,
|
||||
if (source->priv->ready_time == ready_time)
|
||||
{
|
||||
if (context)
|
||||
UNLOCK_CONTEXT (context);
|
||||
|
||||
{
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1965,6 +2070,7 @@ g_source_set_ready_time (GSource *source,
|
||||
if (!SOURCE_BLOCKED (source))
|
||||
g_wakeup_signal (context->wakeup);
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2008,7 +2114,7 @@ g_source_set_can_recurse (GSource *source,
|
||||
g_return_if_fail (source != NULL);
|
||||
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
if (context)
|
||||
LOCK_CONTEXT (context);
|
||||
@@ -2019,7 +2125,10 @@ g_source_set_can_recurse (GSource *source,
|
||||
g_atomic_int_and (&source->flags, ~G_SOURCE_CAN_RECURSE);
|
||||
|
||||
if (context)
|
||||
UNLOCK_CONTEXT (context);
|
||||
{
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2050,7 +2159,7 @@ g_source_set_name_full (GSource *source,
|
||||
g_return_if_fail (source != NULL);
|
||||
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
if (context)
|
||||
LOCK_CONTEXT (context);
|
||||
@@ -2073,7 +2182,10 @@ g_source_set_name_full (GSource *source,
|
||||
source->priv->static_name = is_static;
|
||||
|
||||
if (context)
|
||||
UNLOCK_CONTEXT (context);
|
||||
{
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2369,10 +2481,17 @@ retry_beginning:
|
||||
void
|
||||
g_source_unref (GSource *source)
|
||||
{
|
||||
GMainContext *context;
|
||||
|
||||
g_return_if_fail (source != NULL);
|
||||
/* refcount is checked inside g_source_unref_internal() */
|
||||
|
||||
g_source_unref_internal (source, source->context, FALSE);
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
g_source_unref_internal (source, context, FALSE);
|
||||
|
||||
if (context)
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2690,7 +2809,7 @@ g_source_add_unix_fd (GSource *source,
|
||||
poll_fd->events = events;
|
||||
poll_fd->revents = 0;
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
|
||||
if (context)
|
||||
LOCK_CONTEXT (context);
|
||||
@@ -2702,6 +2821,7 @@ g_source_add_unix_fd (GSource *source,
|
||||
if (!SOURCE_BLOCKED (source))
|
||||
g_main_context_add_poll_unlocked (context, source->priority, poll_fd);
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
|
||||
return poll_fd;
|
||||
@@ -2739,13 +2859,16 @@ g_source_modify_unix_fd (GSource *source,
|
||||
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
|
||||
g_return_if_fail (g_slist_find (source->priv->fds, tag));
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
poll_fd = tag;
|
||||
|
||||
poll_fd->events = new_events;
|
||||
|
||||
if (context)
|
||||
g_main_context_wakeup (context);
|
||||
{
|
||||
g_main_context_wakeup (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2777,7 +2900,7 @@ g_source_remove_unix_fd (GSource *source,
|
||||
g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
|
||||
g_return_if_fail (g_slist_find (source->priv->fds, tag));
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
poll_fd = tag;
|
||||
|
||||
if (context)
|
||||
@@ -2791,6 +2914,7 @@ g_source_remove_unix_fd (GSource *source,
|
||||
g_main_context_remove_poll_unlocked (context, poll_fd);
|
||||
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
|
||||
g_free (poll_fd);
|
||||
@@ -3270,7 +3394,8 @@ g_source_is_destroyed (GSource *source)
|
||||
*/
|
||||
/* HOLDS: source->context's lock */
|
||||
static void
|
||||
block_source (GSource *source)
|
||||
block_source (GSource *source,
|
||||
GMainContext *context)
|
||||
{
|
||||
GSList *tmp_list;
|
||||
|
||||
@@ -3278,17 +3403,17 @@ block_source (GSource *source)
|
||||
|
||||
g_atomic_int_or (&source->flags, G_SOURCE_BLOCKED);
|
||||
|
||||
if (source->context)
|
||||
if (context)
|
||||
{
|
||||
tmp_list = source->poll_fds;
|
||||
while (tmp_list)
|
||||
{
|
||||
g_main_context_remove_poll_unlocked (source->context, tmp_list->data);
|
||||
g_main_context_remove_poll_unlocked (context, tmp_list->data);
|
||||
tmp_list = tmp_list->next;
|
||||
}
|
||||
|
||||
for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
|
||||
g_main_context_remove_poll_unlocked (source->context, tmp_list->data);
|
||||
g_main_context_remove_poll_unlocked (context, tmp_list->data);
|
||||
}
|
||||
|
||||
if (source->priv && source->priv->child_sources)
|
||||
@@ -3296,7 +3421,7 @@ block_source (GSource *source)
|
||||
tmp_list = source->priv->child_sources;
|
||||
while (tmp_list)
|
||||
{
|
||||
block_source (tmp_list->data);
|
||||
block_source (tmp_list->data, context);
|
||||
tmp_list = tmp_list->next;
|
||||
}
|
||||
}
|
||||
@@ -3304,7 +3429,8 @@ block_source (GSource *source)
|
||||
|
||||
/* HOLDS: source->context's lock */
|
||||
static void
|
||||
unblock_source (GSource *source)
|
||||
unblock_source (GSource *source,
|
||||
GMainContext *context)
|
||||
{
|
||||
GSList *tmp_list;
|
||||
|
||||
@@ -3316,19 +3442,19 @@ unblock_source (GSource *source)
|
||||
tmp_list = source->poll_fds;
|
||||
while (tmp_list)
|
||||
{
|
||||
g_main_context_add_poll_unlocked (source->context, source->priority, tmp_list->data);
|
||||
g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
|
||||
tmp_list = tmp_list->next;
|
||||
}
|
||||
|
||||
for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
|
||||
g_main_context_add_poll_unlocked (source->context, source->priority, tmp_list->data);
|
||||
g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
|
||||
|
||||
if (source->priv && source->priv->child_sources)
|
||||
{
|
||||
tmp_list = source->priv->child_sources;
|
||||
while (tmp_list)
|
||||
{
|
||||
unblock_source (tmp_list->data);
|
||||
unblock_source (tmp_list->data, context);
|
||||
tmp_list = tmp_list->next;
|
||||
}
|
||||
}
|
||||
@@ -3373,7 +3499,7 @@ g_main_dispatch (GMainContext *context)
|
||||
cb_funcs->ref (cb_data);
|
||||
|
||||
if ((g_atomic_int_get (&source->flags) & G_SOURCE_CAN_RECURSE) == 0)
|
||||
block_source (source);
|
||||
block_source (source, context);
|
||||
|
||||
was_in_call = g_atomic_int_or (&source->flags,
|
||||
(GSourceFlags) G_HOOK_FLAG_IN_CALL) &
|
||||
@@ -3417,7 +3543,7 @@ g_main_dispatch (GMainContext *context)
|
||||
g_atomic_int_and (&source->flags, ~G_HOOK_FLAG_IN_CALL);
|
||||
|
||||
if (SOURCE_BLOCKED (source) && !SOURCE_DESTROYED (source))
|
||||
unblock_source (source);
|
||||
unblock_source (source, context);
|
||||
|
||||
/* Note: this depends on the fact that we can't switch
|
||||
* sources from one main context to another
|
||||
@@ -4877,9 +5003,8 @@ g_source_get_time (GSource *source)
|
||||
|
||||
g_return_val_if_fail (source != NULL, 0);
|
||||
g_return_val_if_fail (g_atomic_int_get (&source->ref_count) > 0, 0);
|
||||
g_return_val_if_fail (source->context != NULL, 0);
|
||||
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
g_return_val_if_fail (context != NULL, 0);
|
||||
|
||||
LOCK_CONTEXT (context);
|
||||
|
||||
@@ -4892,6 +5017,7 @@ g_source_get_time (GSource *source)
|
||||
result = context->time;
|
||||
|
||||
UNLOCK_CONTEXT (context);
|
||||
g_main_context_unref (context);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -5582,8 +5708,8 @@ wake_source (GSource *source)
|
||||
* - the GMainContext will either be NULL or point to a live
|
||||
* GMainContext
|
||||
*
|
||||
* - the GMainContext will remain valid since we hold the
|
||||
* main_context_list lock
|
||||
* - the GMainContext will remain valid since source_dup_main_context()
|
||||
* gave us a ref or NULL
|
||||
*
|
||||
* Since we are holding a lot of locks here, don't try to enter any
|
||||
* more GMainContext functions for fear of dealock -- just hit the
|
||||
@@ -5591,11 +5717,12 @@ wake_source (GSource *source)
|
||||
* unsafe with some very minor changes in the future, and signal
|
||||
* handling is not the most well-tested codepath.
|
||||
*/
|
||||
G_LOCK(main_context_list);
|
||||
context = source->context;
|
||||
context = source_dup_main_context (source);
|
||||
if (context)
|
||||
g_wakeup_signal (context->wakeup);
|
||||
G_UNLOCK(main_context_list);
|
||||
|
||||
if (context)
|
||||
g_main_context_unref (context);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -726,6 +726,8 @@ guint g_source_get_id (GSource *source);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GMainContext *g_source_get_context (GSource *source);
|
||||
GLIB_AVAILABLE_IN_2_86
|
||||
GMainContext *g_source_dup_context (GSource *source);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_source_set_callback (GSource *source,
|
||||
|
@@ -64,7 +64,7 @@ static GSourceFuncs global_funcs = {
|
||||
static void
|
||||
test_maincontext_basic (void)
|
||||
{
|
||||
GMainContext *ctx;
|
||||
GMainContext *ctx, *source_ctx;
|
||||
GSource *source;
|
||||
guint id;
|
||||
gpointer data = &global_funcs;
|
||||
@@ -103,6 +103,10 @@ test_maincontext_basic (void)
|
||||
g_source_destroy (source);
|
||||
g_assert_true (g_source_get_context (source) == ctx);
|
||||
g_assert_null (g_main_context_find_source_by_id (ctx, id));
|
||||
source_ctx = g_source_dup_context (source);
|
||||
g_assert_true (source_ctx == ctx);
|
||||
g_main_context_unref (source_ctx);
|
||||
source_ctx = NULL;
|
||||
|
||||
g_main_context_unref (ctx);
|
||||
|
||||
@@ -2481,6 +2485,166 @@ test_steal_fd (void)
|
||||
g_free (tmpfile);
|
||||
}
|
||||
|
||||
typedef enum
|
||||
{
|
||||
INITIAL = 0,
|
||||
MAIN_CONTEXT_READY = (1 << 0),
|
||||
SOURCE_READY = (1 << 1),
|
||||
} SimultaneousDestructionTestState;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SimultaneousDestructionTestState state; /* (mutex lock) */
|
||||
GMainContext *main_context; /* (mutex lock) */
|
||||
GSource *source; /* (mutex lock) */
|
||||
GMutex lock;
|
||||
GCond cond;
|
||||
GThread *main_context_thread;
|
||||
GThread *source_thread; /* (mutex lock) */
|
||||
} SimultaneousDestructionTest;
|
||||
|
||||
static SimultaneousDestructionTest *
|
||||
create_simultaneous_destruction_test (void)
|
||||
{
|
||||
SimultaneousDestructionTest *test;
|
||||
|
||||
test = g_new0 (SimultaneousDestructionTest, 1);
|
||||
|
||||
g_mutex_init (&test->lock);
|
||||
g_cond_init (&test->cond);
|
||||
|
||||
return test;
|
||||
}
|
||||
|
||||
static void
|
||||
free_simultaneous_destruction_test (SimultaneousDestructionTest * test)
|
||||
{
|
||||
g_mutex_clear (&test->lock);
|
||||
g_cond_clear (&test->cond);
|
||||
|
||||
/* Should have already been cleared in wait_simultaneous_destruction_test() */
|
||||
g_assert (test->main_context == NULL);
|
||||
g_assert (test->source == NULL);
|
||||
g_assert (test->main_context_thread == NULL);
|
||||
g_assert (test->source_thread == NULL);
|
||||
|
||||
g_free (test);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
source_create_unref_thread_func (gpointer data)
|
||||
{
|
||||
SimultaneousDestructionTest *test = data;
|
||||
|
||||
g_mutex_lock (&test->lock);
|
||||
test->source = g_timeout_source_new_seconds (100);
|
||||
g_source_attach (test->source, test->main_context);
|
||||
test->state |= SOURCE_READY;
|
||||
g_cond_broadcast (&test->cond);
|
||||
while ((test->state & MAIN_CONTEXT_READY) == 0)
|
||||
g_cond_wait (&test->cond, &test->lock);
|
||||
g_mutex_unlock (&test->lock);
|
||||
|
||||
g_thread_yield ();
|
||||
g_source_destroy (test->source);
|
||||
|
||||
g_mutex_lock (&test->lock);
|
||||
g_source_unref (test->source);
|
||||
test->source = NULL;
|
||||
g_cond_broadcast (&test->cond);
|
||||
g_mutex_unlock (&test->lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
main_context_create_unref_thread_func (gpointer data)
|
||||
{
|
||||
SimultaneousDestructionTest *test = data;
|
||||
|
||||
g_mutex_lock (&test->lock);
|
||||
test->main_context = g_main_context_new ();
|
||||
test->source_thread = g_thread_new (NULL, source_create_unref_thread_func, test);
|
||||
|
||||
test->state |= MAIN_CONTEXT_READY;
|
||||
while ((test->state & SOURCE_READY) == 0)
|
||||
g_cond_wait (&test->cond, &test->lock);
|
||||
g_mutex_unlock (&test->lock);
|
||||
|
||||
g_thread_yield ();
|
||||
g_main_context_unref (test->main_context);
|
||||
|
||||
g_mutex_lock (&test->lock);
|
||||
test->main_context = NULL;
|
||||
g_cond_broadcast (&test->cond);
|
||||
g_mutex_unlock (&test->lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
start_simultaneous_destruction_test (SimultaneousDestructionTest * test)
|
||||
{
|
||||
test->main_context_thread = g_thread_new (NULL, main_context_create_unref_thread_func, test);
|
||||
|
||||
g_mutex_lock (&test->lock);
|
||||
while (test->main_context)
|
||||
g_cond_wait (&test->cond, &test->lock);
|
||||
g_mutex_unlock (&test->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
wait_simultaneous_destruction_test (SimultaneousDestructionTest * test)
|
||||
{
|
||||
g_mutex_lock (&test->lock);
|
||||
while (test->main_context || test->source)
|
||||
g_cond_wait (&test->cond, &test->lock);
|
||||
g_mutex_unlock (&test->lock);
|
||||
|
||||
g_thread_join (g_steal_pointer (&test->main_context_thread));
|
||||
g_thread_join (g_steal_pointer (&test->source_thread));
|
||||
}
|
||||
|
||||
static void
|
||||
test_simultaneous_source_context_destruction (void)
|
||||
{
|
||||
guint64 n_concurrent = 120, n_iterations = 100;
|
||||
SimultaneousDestructionTest **test;
|
||||
guint64 i;
|
||||
|
||||
if (g_test_thorough ())
|
||||
{
|
||||
n_concurrent = 512;
|
||||
n_iterations = 2000;
|
||||
}
|
||||
|
||||
test = g_new0 (SimultaneousDestructionTest *, n_concurrent);
|
||||
|
||||
for (i = 0; i < n_iterations; i++)
|
||||
{
|
||||
gsize j = 0;
|
||||
for (j = 0; j < n_concurrent; j++)
|
||||
test[j] = create_simultaneous_destruction_test ();
|
||||
|
||||
for (j = 0; j < n_concurrent; j++)
|
||||
start_simultaneous_destruction_test (test[j]);
|
||||
|
||||
for (j = 0; j < n_concurrent; j++)
|
||||
{
|
||||
wait_simultaneous_destruction_test (test[j]);
|
||||
free_simultaneous_destruction_test (test[j]);
|
||||
}
|
||||
|
||||
if (g_test_verbose() && i % 100 == 0)
|
||||
g_test_message ("# %" G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT, i, n_iterations);
|
||||
}
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_test_message ("%" G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT, n_iterations, n_iterations);
|
||||
|
||||
g_free (test);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
@@ -2535,6 +2699,7 @@ main (int argc, char *argv[])
|
||||
g_test_add_func ("/mainloop/steal-fd", test_steal_fd);
|
||||
g_test_add_data_func ("/mainloop/ownerless-polling/attach-first", GINT_TO_POINTER (TRUE), test_ownerless_polling);
|
||||
g_test_add_data_func ("/mainloop/ownerless-polling/pop-first", GINT_TO_POINTER (FALSE), test_ownerless_polling);
|
||||
g_test_add_func ("/mainloop/simultaneous-source-context-destruction", test_simultaneous_source_context_destruction);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Reference in New Issue
Block a user