mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 23:16:14 +01:00
GMainContext: reorganize source list to avoid O(n) behavior
Rather than having a single priority-ordered list of GSources, store a list of queues of each priority level. This means that adding a source is now O(n) in the number of unique priority levels currently being used, rather than O(n) in the total number of sources. https://bugzilla.gnome.org/show_bug.cgi?id=619329
This commit is contained in:
parent
aaaaab91de
commit
55bac5da0a
156
glib/gmain.c
156
glib/gmain.c
@ -195,6 +195,14 @@ typedef enum
|
|||||||
G_SOURCE_BLOCKED = 1 << (G_HOOK_FLAG_USER_SHIFT + 2)
|
G_SOURCE_BLOCKED = 1 << (G_HOOK_FLAG_USER_SHIFT + 2)
|
||||||
} GSourceFlags;
|
} GSourceFlags;
|
||||||
|
|
||||||
|
typedef struct _GSourceList GSourceList;
|
||||||
|
|
||||||
|
struct _GSourceList
|
||||||
|
{
|
||||||
|
GSource *head, *tail;
|
||||||
|
gint priority;
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct _GMainWaiter GMainWaiter;
|
typedef struct _GMainWaiter GMainWaiter;
|
||||||
|
|
||||||
struct _GMainWaiter
|
struct _GMainWaiter
|
||||||
@ -232,7 +240,7 @@ struct _GMainContext
|
|||||||
gint timeout; /* Timeout for current iteration */
|
gint timeout; /* Timeout for current iteration */
|
||||||
|
|
||||||
guint next_id;
|
guint next_id;
|
||||||
GSource *source_list;
|
GList *source_lists;
|
||||||
gint in_check_or_prepare;
|
gint in_check_or_prepare;
|
||||||
|
|
||||||
GPollRec *poll_records, *poll_records_tail;
|
GPollRec *poll_records, *poll_records_tail;
|
||||||
@ -313,6 +321,7 @@ typedef struct _GSourceIter
|
|||||||
{
|
{
|
||||||
GMainContext *context;
|
GMainContext *context;
|
||||||
gboolean may_modify;
|
gboolean may_modify;
|
||||||
|
GList *current_list;
|
||||||
GSource *source;
|
GSource *source;
|
||||||
} GSourceIter;
|
} GSourceIter;
|
||||||
|
|
||||||
@ -551,7 +560,7 @@ g_main_context_new (void)
|
|||||||
|
|
||||||
context->next_id = 1;
|
context->next_id = 1;
|
||||||
|
|
||||||
context->source_list = NULL;
|
context->source_lists = NULL;
|
||||||
|
|
||||||
context->poll_func = g_poll;
|
context->poll_func = g_poll;
|
||||||
|
|
||||||
@ -823,6 +832,7 @@ g_source_iter_init (GSourceIter *iter,
|
|||||||
gboolean may_modify)
|
gboolean may_modify)
|
||||||
{
|
{
|
||||||
iter->context = context;
|
iter->context = context;
|
||||||
|
iter->current_list = NULL;
|
||||||
iter->source = NULL;
|
iter->source = NULL;
|
||||||
iter->may_modify = may_modify;
|
iter->may_modify = may_modify;
|
||||||
}
|
}
|
||||||
@ -834,14 +844,33 @@ g_source_iter_next (GSourceIter *iter, GSource **source)
|
|||||||
GSource *next_source;
|
GSource *next_source;
|
||||||
|
|
||||||
if (iter->source)
|
if (iter->source)
|
||||||
{
|
next_source = iter->source->next;
|
||||||
next_source = iter->source->next;
|
|
||||||
if (iter->may_modify)
|
|
||||||
SOURCE_UNREF (iter->source, iter->context);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
next_source = iter->context->source_list;
|
next_source = NULL;
|
||||||
|
|
||||||
|
if (!next_source)
|
||||||
|
{
|
||||||
|
if (iter->current_list)
|
||||||
|
iter->current_list = iter->current_list->next;
|
||||||
|
else
|
||||||
|
iter->current_list = iter->context->source_lists;
|
||||||
|
|
||||||
|
if (iter->current_list)
|
||||||
|
{
|
||||||
|
GSourceList *source_list = iter->current_list->data;
|
||||||
|
|
||||||
|
next_source = source_list->head;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: unreffing iter->source could potentially cause its
|
||||||
|
* GSourceList to be removed from source_lists (if iter->source is
|
||||||
|
* the only source in its list, and it is destroyed), so we have to
|
||||||
|
* keep it reffed until after we advance iter->current_list, above.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (iter->source && iter->may_modify)
|
||||||
|
SOURCE_UNREF (iter->source, iter->context);
|
||||||
iter->source = next_source;
|
iter->source = next_source;
|
||||||
if (iter->source && iter->may_modify)
|
if (iter->source && iter->may_modify)
|
||||||
iter->source->ref_count++;
|
iter->source->ref_count++;
|
||||||
@ -865,56 +894,121 @@ g_source_iter_clear (GSourceIter *iter)
|
|||||||
|
|
||||||
/* Holds context's lock
|
/* Holds context's lock
|
||||||
*/
|
*/
|
||||||
static void
|
static GSourceList *
|
||||||
g_source_list_add (GSource *source,
|
find_source_list_for_priority (GMainContext *context,
|
||||||
GMainContext *context)
|
gint priority,
|
||||||
|
gboolean create)
|
||||||
{
|
{
|
||||||
|
GList *iter, *last;
|
||||||
|
GSourceList *source_list;
|
||||||
|
|
||||||
|
last = NULL;
|
||||||
|
for (iter = context->source_lists; iter != NULL; last = iter, iter = iter->next)
|
||||||
|
{
|
||||||
|
source_list = iter->data;
|
||||||
|
|
||||||
|
if (source_list->priority == priority)
|
||||||
|
return source_list;
|
||||||
|
|
||||||
|
if (source_list->priority > priority)
|
||||||
|
{
|
||||||
|
if (!create)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
source_list = g_slice_new0 (GSourceList);
|
||||||
|
source_list->priority = priority;
|
||||||
|
context->source_lists = g_list_insert_before (context->source_lists,
|
||||||
|
iter,
|
||||||
|
source_list);
|
||||||
|
return source_list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!create)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
source_list = g_slice_new0 (GSourceList);
|
||||||
|
source_list->priority = priority;
|
||||||
|
|
||||||
|
if (!last)
|
||||||
|
context->source_lists = g_list_append (NULL, source_list);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* This just appends source_list to the end of
|
||||||
|
* context->source_lists without having to walk the list again.
|
||||||
|
*/
|
||||||
|
last = g_list_append (last, source_list);
|
||||||
|
}
|
||||||
|
return source_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Holds context's lock
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
source_add_to_context (GSource *source,
|
||||||
|
GMainContext *context)
|
||||||
|
{
|
||||||
|
GSourceList *source_list;
|
||||||
GSource *prev, *next;
|
GSource *prev, *next;
|
||||||
|
|
||||||
|
source_list = find_source_list_for_priority (context, source->priority, TRUE);
|
||||||
|
|
||||||
if (source->priv->parent_source)
|
if (source->priv->parent_source)
|
||||||
{
|
{
|
||||||
|
g_assert (source_list->head != NULL);
|
||||||
|
|
||||||
/* Put the source immediately before its parent */
|
/* Put the source immediately before its parent */
|
||||||
prev = source->priv->parent_source->prev;
|
prev = source->priv->parent_source->prev;
|
||||||
next = source->priv->parent_source;
|
next = source->priv->parent_source;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
prev = NULL;
|
prev = source_list->tail;
|
||||||
next = context->source_list;
|
next = NULL;
|
||||||
while (next && next->priority <= source->priority)
|
|
||||||
{
|
|
||||||
prev = next;
|
|
||||||
next = next->next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
source->next = next;
|
source->next = next;
|
||||||
if (next)
|
if (next)
|
||||||
next->prev = source;
|
next->prev = source;
|
||||||
|
else
|
||||||
|
source_list->tail = source;
|
||||||
|
|
||||||
source->prev = prev;
|
source->prev = prev;
|
||||||
if (prev)
|
if (prev)
|
||||||
prev->next = source;
|
prev->next = source;
|
||||||
else
|
else
|
||||||
context->source_list = source;
|
source_list->head = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Holds context's lock
|
/* Holds context's lock
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
g_source_list_remove (GSource *source,
|
source_remove_from_context (GSource *source,
|
||||||
GMainContext *context)
|
GMainContext *context)
|
||||||
{
|
{
|
||||||
|
GSourceList *source_list;
|
||||||
|
|
||||||
|
source_list = find_source_list_for_priority (context, source->priority, FALSE);
|
||||||
|
g_return_if_fail (source_list != NULL);
|
||||||
|
|
||||||
if (source->prev)
|
if (source->prev)
|
||||||
source->prev->next = source->next;
|
source->prev->next = source->next;
|
||||||
else
|
else
|
||||||
context->source_list = source->next;
|
source_list->head = source->next;
|
||||||
|
|
||||||
if (source->next)
|
if (source->next)
|
||||||
source->next->prev = source->prev;
|
source->next->prev = source->prev;
|
||||||
|
else
|
||||||
|
source_list->tail = source->prev;
|
||||||
|
|
||||||
source->prev = NULL;
|
source->prev = NULL;
|
||||||
source->next = NULL;
|
source->next = NULL;
|
||||||
|
|
||||||
|
if (source_list->head == NULL)
|
||||||
|
{
|
||||||
|
context->source_lists = g_list_remove (context->source_lists, source_list);
|
||||||
|
g_slice_free (GSourceList, source_list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static guint
|
static guint
|
||||||
@ -928,7 +1022,7 @@ g_source_attach_unlocked (GSource *source,
|
|||||||
result = source->source_id = context->next_id++;
|
result = source->source_id = context->next_id++;
|
||||||
|
|
||||||
source->ref_count++;
|
source->ref_count++;
|
||||||
g_source_list_add (source, context);
|
source_add_to_context (source, context);
|
||||||
|
|
||||||
tmp_list = source->poll_fds;
|
tmp_list = source->poll_fds;
|
||||||
while (tmp_list)
|
while (tmp_list)
|
||||||
@ -1429,15 +1523,19 @@ g_source_set_priority_unlocked (GSource *source,
|
|||||||
g_return_if_fail (source->priv->parent_source == NULL ||
|
g_return_if_fail (source->priv->parent_source == NULL ||
|
||||||
source->priv->parent_source->priority == priority);
|
source->priv->parent_source->priority == priority);
|
||||||
|
|
||||||
|
if (context)
|
||||||
|
{
|
||||||
|
/* 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->priority = priority;
|
source->priority = priority;
|
||||||
|
|
||||||
if (context)
|
if (context)
|
||||||
{
|
{
|
||||||
/* Remove the source from the context's source and then
|
source_add_to_context (source, source->context);
|
||||||
* add it back so it is sorted in the correct place
|
|
||||||
*/
|
|
||||||
g_source_list_remove (source, source->context);
|
|
||||||
g_source_list_add (source, source->context);
|
|
||||||
|
|
||||||
if (!SOURCE_BLOCKED (source))
|
if (!SOURCE_BLOCKED (source))
|
||||||
{
|
{
|
||||||
@ -1693,7 +1791,7 @@ g_source_unref_internal (GSource *source,
|
|||||||
{
|
{
|
||||||
if (!SOURCE_DESTROYED (source))
|
if (!SOURCE_DESTROYED (source))
|
||||||
g_warning (G_STRLOC ": ref_count == 0, but source was still attached to a context!");
|
g_warning (G_STRLOC ": ref_count == 0, but source was still attached to a context!");
|
||||||
g_source_list_remove (source, context);
|
source_remove_from_context (source, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source->source_funcs->finalize)
|
if (source->source_funcs->finalize)
|
||||||
|
Loading…
Reference in New Issue
Block a user