mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-24 21:16:15 +01:00
gmain: Handle case where source id overflows
0 is not a valid source id, but for long-lived programs that rapidly create/destroy sources, it's possible for the source id to overflow. We should handle this, because the documentation implies we will. https://bugzilla.gnome.org/show_bug.cgi?id=687098
This commit is contained in:
parent
606aa26acf
commit
b98a1c8df3
@ -40,7 +40,8 @@ glib__private__ (void)
|
|||||||
|
|
||||||
g_get_worker_context,
|
g_get_worker_context,
|
||||||
|
|
||||||
g_check_setuid
|
g_check_setuid,
|
||||||
|
g_main_context_new_with_next_id
|
||||||
};
|
};
|
||||||
|
|
||||||
return &table;
|
return &table;
|
||||||
|
@ -27,6 +27,8 @@ G_GNUC_INTERNAL
|
|||||||
GMainContext * g_get_worker_context (void);
|
GMainContext * g_get_worker_context (void);
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
gboolean g_check_setuid (void);
|
gboolean g_check_setuid (void);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
GMainContext * g_main_context_new_with_next_id (guint next_id);
|
||||||
|
|
||||||
#define GLIB_PRIVATE_CALL(symbol) (glib__private__()->symbol)
|
#define GLIB_PRIVATE_CALL(symbol) (glib__private__()->symbol)
|
||||||
|
|
||||||
@ -41,9 +43,10 @@ typedef struct {
|
|||||||
|
|
||||||
/* See gmain.c */
|
/* See gmain.c */
|
||||||
GMainContext * (* g_get_worker_context) (void);
|
GMainContext * (* g_get_worker_context) (void);
|
||||||
/* Add other private functions here, initialize them in glib-private.c */
|
|
||||||
|
|
||||||
gboolean (* g_check_setuid) (void);
|
gboolean (* g_check_setuid) (void);
|
||||||
|
GMainContext * (* g_main_context_new_with_next_id) (guint next_id);
|
||||||
|
/* Add other private functions here, initialize them in glib-private.c */
|
||||||
} GLibPrivateVTable;
|
} GLibPrivateVTable;
|
||||||
|
|
||||||
GLibPrivateVTable *glib__private__ (void);
|
GLibPrivateVTable *glib__private__ (void);
|
||||||
|
76
glib/gmain.c
76
glib/gmain.c
@ -240,6 +240,7 @@ struct _GMainContext
|
|||||||
gint timeout; /* Timeout for current iteration */
|
gint timeout; /* Timeout for current iteration */
|
||||||
|
|
||||||
guint next_id;
|
guint next_id;
|
||||||
|
GHashTable *overflow_used_source_ids; /* set<guint> */
|
||||||
GList *source_lists;
|
GList *source_lists;
|
||||||
gint in_check_or_prepare;
|
gint in_check_or_prepare;
|
||||||
|
|
||||||
@ -528,6 +529,9 @@ g_main_context_unref (GMainContext *context)
|
|||||||
}
|
}
|
||||||
g_list_free (context->source_lists);
|
g_list_free (context->source_lists);
|
||||||
|
|
||||||
|
if (context->overflow_used_source_ids)
|
||||||
|
g_hash_table_destroy (context->overflow_used_source_ids);
|
||||||
|
|
||||||
g_mutex_clear (&context->mutex);
|
g_mutex_clear (&context->mutex);
|
||||||
|
|
||||||
g_ptr_array_free (context->pending_dispatches, TRUE);
|
g_ptr_array_free (context->pending_dispatches, TRUE);
|
||||||
@ -541,6 +545,18 @@ g_main_context_unref (GMainContext *context)
|
|||||||
g_free (context);
|
g_free (context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Helper function used by mainloop/overflow test.
|
||||||
|
*/
|
||||||
|
GMainContext *
|
||||||
|
g_main_context_new_with_next_id (guint next_id)
|
||||||
|
{
|
||||||
|
GMainContext *ret = g_main_context_new ();
|
||||||
|
|
||||||
|
ret->next_id = next_id;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_main_context_new:
|
* g_main_context_new:
|
||||||
*
|
*
|
||||||
@ -1025,18 +1041,70 @@ source_remove_from_context (GSource *source,
|
|||||||
context->source_lists = g_list_remove (context->source_lists, source_list);
|
context->source_lists = g_list_remove (context->source_lists, source_list);
|
||||||
g_slice_free (GSourceList, source_list);
|
g_slice_free (GSourceList, source_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context->overflow_used_source_ids)
|
||||||
|
g_hash_table_remove (context->overflow_used_source_ids,
|
||||||
|
GUINT_TO_POINTER (source->source_id));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
assign_source_id_unlocked (GMainContext *context,
|
||||||
|
GSource *source)
|
||||||
|
{
|
||||||
|
guint id;
|
||||||
|
|
||||||
|
/* Are we about to overflow back to 0?
|
||||||
|
* See https://bugzilla.gnome.org/show_bug.cgi?id=687098
|
||||||
|
*/
|
||||||
|
if (G_UNLIKELY (context->next_id == G_MAXUINT &&
|
||||||
|
context->overflow_used_source_ids == NULL))
|
||||||
|
{
|
||||||
|
GSourceIter iter;
|
||||||
|
GSource *source;
|
||||||
|
|
||||||
|
context->overflow_used_source_ids = g_hash_table_new (NULL, NULL);
|
||||||
|
|
||||||
|
g_source_iter_init (&iter, context, FALSE);
|
||||||
|
while (g_source_iter_next (&iter, &source))
|
||||||
|
{
|
||||||
|
g_hash_table_add (context->overflow_used_source_ids,
|
||||||
|
GUINT_TO_POINTER (source->source_id));
|
||||||
|
}
|
||||||
|
id = G_MAXUINT;
|
||||||
|
}
|
||||||
|
else if (context->overflow_used_source_ids == NULL)
|
||||||
|
{
|
||||||
|
id = context->next_id++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If we overran G_MAXUINT, we fall back to randomly probing the
|
||||||
|
* source ids for the current context. This will be slower the more
|
||||||
|
* sources there are, but we're mainly concerned right now about
|
||||||
|
* correctness and code size. There's time for a more clever solution
|
||||||
|
* later.
|
||||||
|
*/
|
||||||
|
do
|
||||||
|
id = g_random_int ();
|
||||||
|
while (id == 0 ||
|
||||||
|
g_hash_table_contains (context->overflow_used_source_ids,
|
||||||
|
GUINT_TO_POINTER (id)));
|
||||||
|
g_hash_table_add (context->overflow_used_source_ids, GUINT_TO_POINTER (id));
|
||||||
|
}
|
||||||
|
|
||||||
|
source->source_id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static guint
|
static guint
|
||||||
g_source_attach_unlocked (GSource *source,
|
g_source_attach_unlocked (GSource *source,
|
||||||
GMainContext *context)
|
GMainContext *context)
|
||||||
{
|
{
|
||||||
guint result = 0;
|
|
||||||
GSList *tmp_list;
|
GSList *tmp_list;
|
||||||
|
|
||||||
source->context = context;
|
source->context = context;
|
||||||
result = source->source_id = context->next_id++;
|
assign_source_id_unlocked (context, source);
|
||||||
|
|
||||||
source->ref_count++;
|
source->ref_count++;
|
||||||
source_add_to_context (source, context);
|
source_add_to_context (source, context);
|
||||||
|
|
||||||
@ -1054,7 +1122,7 @@ g_source_attach_unlocked (GSource *source,
|
|||||||
tmp_list = tmp_list->next;
|
tmp_list = tmp_list->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return source->source_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
#include "glib-private.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
static gboolean cb (gpointer data)
|
static gboolean cb (gpointer data)
|
||||||
{
|
{
|
||||||
@ -677,6 +679,90 @@ test_source_time (void)
|
|||||||
g_main_context_unref (data.ctx);
|
g_main_context_unref (data.ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
guint outstanding_ops;
|
||||||
|
GMainLoop *loop;
|
||||||
|
} TestOverflowData;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
on_source_fired_cb (gpointer user_data)
|
||||||
|
{
|
||||||
|
TestOverflowData *data = user_data;
|
||||||
|
GSource *current_source;
|
||||||
|
GMainContext *current_context;
|
||||||
|
guint source_id;
|
||||||
|
|
||||||
|
data->outstanding_ops--;
|
||||||
|
|
||||||
|
current_source = g_main_current_source ();
|
||||||
|
current_context = g_source_get_context (current_source);
|
||||||
|
source_id = g_source_get_id (current_source);
|
||||||
|
g_assert (g_main_context_find_source_by_id (current_context, source_id) != NULL);
|
||||||
|
g_source_destroy (current_source);
|
||||||
|
g_assert (g_main_context_find_source_by_id (current_context, source_id) == NULL);
|
||||||
|
|
||||||
|
if (data->outstanding_ops == 0)
|
||||||
|
g_main_loop_quit (data->loop);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSource *
|
||||||
|
add_idle_source (GMainContext *ctx,
|
||||||
|
TestOverflowData *data)
|
||||||
|
{
|
||||||
|
GSource *source;
|
||||||
|
|
||||||
|
source = g_idle_source_new ();
|
||||||
|
g_source_set_callback (source, on_source_fired_cb, data, NULL);
|
||||||
|
g_source_attach (source, ctx);
|
||||||
|
g_source_unref (source);
|
||||||
|
data->outstanding_ops++;
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* https://bugzilla.gnome.org/show_bug.cgi?id=687098 */
|
||||||
|
static void
|
||||||
|
test_mainloop_overflow (void)
|
||||||
|
{
|
||||||
|
GMainContext *ctx;
|
||||||
|
GMainLoop *loop;
|
||||||
|
GSource *source;
|
||||||
|
TestOverflowData data;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
memset (&data, 0, sizeof (data));
|
||||||
|
|
||||||
|
ctx = GLIB_PRIVATE_CALL (g_main_context_new_with_next_id) (G_MAXUINT-1);
|
||||||
|
|
||||||
|
loop = g_main_loop_new (ctx, TRUE);
|
||||||
|
data.outstanding_ops = 0;
|
||||||
|
data.loop = loop;
|
||||||
|
|
||||||
|
source = add_idle_source (ctx, &data);
|
||||||
|
g_assert_cmpint (source->source_id, ==, G_MAXUINT-1);
|
||||||
|
|
||||||
|
source = add_idle_source (ctx, &data);
|
||||||
|
g_assert_cmpint (source->source_id, ==, G_MAXUINT);
|
||||||
|
|
||||||
|
source = add_idle_source (ctx, &data);
|
||||||
|
g_assert_cmpint (source->source_id, !=, 0);
|
||||||
|
|
||||||
|
/* Now, a lot more sources */
|
||||||
|
for (i = 0; i < 50; i++)
|
||||||
|
{
|
||||||
|
source = add_idle_source (ctx, &data);
|
||||||
|
g_assert_cmpint (source->source_id, !=, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_main_loop_run (loop);
|
||||||
|
g_assert_cmpint (data.outstanding_ops, ==, 0);
|
||||||
|
|
||||||
|
g_main_loop_unref (loop);
|
||||||
|
g_main_context_unref (ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc, char *argv[])
|
main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@ -691,6 +777,7 @@ main (int argc, char *argv[])
|
|||||||
g_test_add_func ("/mainloop/recursive_child_sources", test_recursive_child_sources);
|
g_test_add_func ("/mainloop/recursive_child_sources", test_recursive_child_sources);
|
||||||
g_test_add_func ("/mainloop/swapping_child_sources", test_swapping_child_sources);
|
g_test_add_func ("/mainloop/swapping_child_sources", test_swapping_child_sources);
|
||||||
g_test_add_func ("/mainloop/source_time", test_source_time);
|
g_test_add_func ("/mainloop/source_time", test_source_time);
|
||||||
|
g_test_add_func ("/mainloop/overflow", test_mainloop_overflow);
|
||||||
|
|
||||||
return g_test_run ();
|
return g_test_run ();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user