gint ref_count;
+ GHashTable *sources; /* guint -> GSource */
+
GPtrArray *pending_dispatches;
gint timeout; /* Timeout for current iteration */
guint next_id;
- GHashTable *overflow_used_source_ids; /* set<guint> */
GList *source_lists;
gint in_check_or_prepare;
- GPollRec *poll_records, *poll_records_tail;
+ GPollRec *poll_records;
guint n_poll_records;
GPollFD *cached_poll_array;
guint cached_poll_array_size;
GSource *source;
GList *sl_iter;
GSourceList *list;
- gint i;
+ guint i;
g_return_if_fail (context != NULL);
g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0);
}
g_list_free (context->source_lists);
- if (context->overflow_used_source_ids)
- g_hash_table_destroy (context->overflow_used_source_ids);
+ g_hash_table_destroy (context->sources);
g_mutex_clear (&context->mutex);
g_mutex_init (&context->mutex);
g_cond_init (&context->cond);
+ context->sources = g_hash_table_new (NULL, NULL);
context->owner = NULL;
context->waiters = NULL;
context->source_lists = g_list_remove (context->source_lists, 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;
- g_hash_table_add (context->overflow_used_source_ids, GUINT_TO_POINTER (id));
- }
- 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
gboolean do_wakeup)
{
GSList *tmp_list;
+ guint id;
+
+ /* The counter may have wrapped, so we must ensure that we do not
+ * reuse the source id of an existing source.
+ */
+ do
+ id = context->next_id++;
+ while (id == 0 || g_hash_table_contains (context->sources, GUINT_TO_POINTER (id)));
source->context = context;
- assign_source_id_unlocked (context, source);
+ source->source_id = id;
source->ref_count++;
+
+ g_hash_table_insert (context->sources, GUINT_TO_POINTER (id), source);
+
source_add_to_context (source, context);
if (!SOURCE_BLOCKED (source))
* the @revents field in the #GPollFD struct and return %TRUE if events need
* to be processed.
*
+ * This API is only intended to be used by implementations of #GSource.
+ * Do not call this API on a #GSource that you did not create.
+ *
* Using this API forces the linear scanning of event sources on each
* main loop iteration. Newly-written event sources should try to use
* g_source_add_unix_fd() instead of this API.
*
* Removes a file descriptor from the set of file descriptors polled for
* this source.
+ *
+ * This API is only intended to be used by implementations of #GSource.
+ * Do not call this API on a #GSource that you did not create.
**/
void
g_source_remove_poll (GSource *source,
* @source will hold a reference on @child_source while @child_source
* is attached to it.
*
+ * This API is only intended to be used by implementations of #GSource.
+ * Do not call this API on a #GSource that you did not create.
+ *
* Since: 2.28
**/
void
*
* Detaches @child_source from @source and destroys it.
*
+ * This API is only intended to be used by implementations of #GSource.
+ * Do not call this API on a #GSource that you did not create.
+ *
* Since: 2.28
**/
void
* for both sources is reached during the same main context iteration
* then the order of dispatch is undefined.
*
+ * This API is only intended to be used by implementations of #GSource.
+ * Do not call this API on a #GSource that you did not create.
+ *
* Since: 2.36
**/
void
*
* It is permitted to call this function multiple times, but is not
* recommended due to the potential performance impact. For example,
- * one could change the name in the "check" function of a #GSourceFuncs
+ * one could change the name in the "check" function of a #GSourceFuncs
* to include details like the event type in the source name.
*
+ * Use caution if changing the name while another thread may be
+ * accessing it with g_source_get_name(); that function does not copy
+ * the value, and changing the value will free it while the other thread
+ * may be attempting to use it.
+ *
* Since: 2.26
**/
void
g_source_set_name (GSource *source,
const char *name)
{
+ GMainContext *context;
+
g_return_if_fail (source != NULL);
+ context = source->context;
+
+ if (context)
+ LOCK_CONTEXT (context);
+
/* setting back to NULL is allowed, just because it's
* weird if get_name can return NULL but you can't
* set that.
g_free (source->name);
source->name = g_strdup (name);
+
+ if (context)
+ UNLOCK_CONTEXT (context);
}
/**
* g_source_get_name:
* @source: a #GSource
*
- * Gets a name for the source, used in debugging and profiling.
- * The name may be #NULL if it has never been set with
- * g_source_set_name().
+ * Gets a name for the source, used in debugging and profiling. The
+ * name may be #NULL if it has never been set with g_source_set_name().
*
* Returns: the name of the source
+ *
* Since: 2.26
**/
const char *
* This is a convenience utility to set source names from the return
* value of g_idle_add(), g_timeout_add(), etc.
*
+ * It is a programmer error to attempt to set the name of a non-existent
+ * source.
+ *
+ * More specifically: source IDs can be reissued after a source has been
+ * destroyed and therefore it is never valid to use this function with a
+ * source ID which may have already been removed. An example is when
+ * scheduling an idle to run in another thread with g_idle_add(): the
+ * idle may already have run and been removed by the time this function
+ * is called on its (now invalid) source ID. This source ID may have
+ * been reissued, leading to the operation being performed against the
+ * wrong source.
+ *
* Since: 2.26
**/
void
if (!SOURCE_DESTROYED (source))
g_warning (G_STRLOC ": ref_count == 0, but source was still attached to a context!");
source_remove_from_context (source, context);
+
+ g_hash_table_remove (context->sources, GUINT_TO_POINTER (source->source_id));
}
if (source->source_funcs->finalize)
g_slist_free_full (source->priv->fds, g_free);
+ while (source->priv->child_sources)
+ {
+ GSource *child_source = source->priv->child_sources->data;
+
+ source->priv->child_sources =
+ g_slist_remove (source->priv->child_sources, child_source);
+ child_source->priv->parent_source = NULL;
+
+ g_source_unref_internal (child_source, context, have_lock);
+ }
+
g_slice_free (GSourcePrivate, source->priv);
source->priv = NULL;
/**
* g_main_context_find_source_by_id:
* @context: (allow-none): a #GMainContext (if %NULL, the default context will be used)
- * @source_id: the source ID, as returned by g_source_get_id().
- *
+ * @source_id: the source ID, as returned by g_source_get_id().
+ *
* Finds a #GSource given a pair of context and ID.
- *
- * Returns: (transfer none): the #GSource if found, otherwise, %NULL
+ *
+ * It is a programmer error to attempt to lookup a non-existent source.
+ *
+ * More specifically: source IDs can be reissued after a source has been
+ * destroyed and therefore it is never valid to use this function with a
+ * source ID which may have already been removed. An example is when
+ * scheduling an idle to run in another thread with g_idle_add(): the
+ * idle may already have run and been removed by the time this function
+ * is called on its (now invalid) source ID. This source ID may have
+ * been reissued, leading to the operation being performed against the
+ * wrong source.
+ *
+ * Returns: (transfer none): the #GSource
**/
GSource *
g_main_context_find_source_by_id (GMainContext *context,
- guint source_id)
+ guint source_id)
{
- GSourceIter iter;
GSource *source;
-
+
g_return_val_if_fail (source_id > 0, NULL);
if (context == NULL)
context = g_main_context_default ();
-
- LOCK_CONTEXT (context);
-
- g_source_iter_init (&iter, context, FALSE);
- while (g_source_iter_next (&iter, &source))
- {
- if (!SOURCE_DESTROYED (source) &&
- source->source_id == source_id)
- break;
- }
- g_source_iter_clear (&iter);
+ LOCK_CONTEXT (context);
+ source = g_hash_table_lookup (context->sources, GUINT_TO_POINTER (source_id));
UNLOCK_CONTEXT (context);
+ if (source && SOURCE_DESTROYED (source))
+ source = NULL;
+
return source;
}
*
* It is a programmer error to attempt to remove a non-existent source.
*
+ * More specifically: source IDs can be reissued after a source has been
+ * destroyed and therefore it is never valid to use this function with a
+ * source ID which may have already been removed. An example is when
+ * scheduling an idle to run in another thread with g_idle_add(): the
+ * idle may already have run and been removed by the time this function
+ * is called on its (now invalid) source ID. This source ID may have
+ * been reissued, leading to the operation being performed against the
+ * wrong source.
+ *
* Returns: For historical reasons, this function always returns %TRUE
**/
gboolean
* It is not necessary to remove the fd before destroying the source; it
* will be cleaned up automatically.
*
+ * This API is only intended to be used by implementations of #GSource.
+ * Do not call this API on a #GSource that you did not create.
+ *
* As the name suggests, this function is not available on Windows.
*
* Returns: an opaque tag
* If you want to remove a fd, don't set its event mask to zero.
* Instead, call g_source_remove_unix_fd().
*
+ * This API is only intended to be used by implementations of #GSource.
+ * Do not call this API on a #GSource that you did not create.
+ *
* As the name suggests, this function is not available on Windows.
*
* Since: 2.36
* watched while keeping the same source around. In the normal case you
* will just want to destroy the source.
*
+ * This API is only intended to be used by implementations of #GSource.
+ * Do not call this API on a #GSource that you did not create.
+ *
* As the name suggests, this function is not available on Windows.
*
* Since: 2.36
* The return value of this function is only defined when the function
* is called from the check or dispatch functions for @source.
*
+ * This API is only intended to be used by implementations of #GSource.
+ * Do not call this API on a #GSource that you did not create.
+ *
* As the name suggests, this function is not available on Windows.
*
* Returns: the conditions reported on the fd
* @context: a #GMainContext
* @priority: location to store priority of highest priority
* source already ready.
- *
+ *
* Prepares to poll sources within a main loop. The resulting information
* for polling is determined by calling g_main_context_query ().
- *
+ *
+ * You must have successfully acquired the context with
+ * g_main_context_acquire() before you may call this function.
+ *
* Returns: %TRUE if some source is ready to be dispatched
* prior to polling.
**/
g_main_context_prepare (GMainContext *context,
gint *priority)
{
- gint i;
+ guint i;
gint n_ready = 0;
gint current_priority = G_MAXINT;
GSource *source;
* @fds: (out caller-allocates) (array length=n_fds): location to
* store #GPollFD records that need to be polled.
* @n_fds: length of @fds.
- *
+ *
* Determines information necessary to poll this main loop.
- *
+ *
+ * You must have successfully acquired the context with
+ * g_main_context_acquire() before you may call this function.
+ *
* Returns: the number of records actually stored in @fds,
* or, if more than @n_fds records need to be stored, the number
* of records that need to be stored.
gint n_fds)
{
gint n_poll;
- GPollRec *pollrec;
+ GPollRec *pollrec, *lastpollrec;
+ gushort events;
LOCK_CONTEXT (context);
- pollrec = context->poll_records;
n_poll = 0;
- while (pollrec && max_priority >= pollrec->priority)
+ lastpollrec = NULL;
+ for (pollrec = context->poll_records; pollrec; pollrec = pollrec->next)
{
- /* We need to include entries with fd->events == 0 in the array because
- * otherwise if the application changes fd->events behind our back and
- * makes it non-zero, we'll be out of sync when we check the fds[] array.
- * (Changing fd->events after adding an FD wasn't an anticipated use of
- * this API, but it occurs in practice.) */
- if (n_poll < n_fds)
- {
- fds[n_poll].fd = pollrec->fd->fd;
- /* In direct contradiction to the Unix98 spec, IRIX runs into
- * difficulty if you pass in POLLERR, POLLHUP or POLLNVAL
- * flags in the events field of the pollfd while it should
- * just ignoring them. So we mask them out here.
- */
- fds[n_poll].events = pollrec->fd->events & ~(G_IO_ERR|G_IO_HUP|G_IO_NVAL);
- fds[n_poll].revents = 0;
- }
+ if (pollrec->priority > max_priority)
+ continue;
+
+ /* In direct contradiction to the Unix98 spec, IRIX runs into
+ * difficulty if you pass in POLLERR, POLLHUP or POLLNVAL
+ * flags in the events field of the pollfd while it should
+ * just ignoring them. So we mask them out here.
+ */
+ events = pollrec->fd->events & ~(G_IO_ERR|G_IO_HUP|G_IO_NVAL);
+
+ if (lastpollrec && pollrec->fd->fd == lastpollrec->fd->fd)
+ {
+ if (n_poll - 1 < n_fds)
+ fds[n_poll - 1].events |= events;
+ }
+ else
+ {
+ if (n_poll < n_fds)
+ {
+ fds[n_poll].fd = pollrec->fd->fd;
+ fds[n_poll].events = events;
+ fds[n_poll].revents = 0;
+ }
+
+ n_poll++;
+ }
- pollrec = pollrec->next;
- n_poll++;
+ lastpollrec = pollrec;
}
context->poll_changed = FALSE;
* @fds: (array length=n_fds): array of #GPollFD's that was passed to
* the last call to g_main_context_query()
* @n_fds: return value of g_main_context_query()
- *
+ *
* Passes the results of polling back to the main loop.
- *
+ *
+ * You must have successfully acquired the context with
+ * g_main_context_acquire() before you may call this function.
+ *
* Returns: %TRUE if some sources are ready to be dispatched.
**/
gboolean
UNLOCK_CONTEXT (context);
return FALSE;
}
-
+
pollrec = context->poll_records;
i = 0;
- while (i < n_fds)
+ while (pollrec && i < n_fds)
{
- if (pollrec->fd->events)
- pollrec->fd->revents = fds[i].revents;
+ while (pollrec && pollrec->fd->fd == fds[i].fd)
+ {
+ if (pollrec->priority <= max_priority)
+ {
+ pollrec->fd->revents =
+ fds[i].revents & (pollrec->fd->events | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
+ }
+ pollrec = pollrec->next;
+ }
- pollrec = pollrec->next;
i++;
}
/**
* g_main_context_dispatch:
* @context: a #GMainContext
- *
+ *
* Dispatches all pending sources.
+ *
+ * You must have successfully acquired the context with
+ * g_main_context_acquire() before you may call this function.
**/
void
g_main_context_dispatch (GMainContext *context)
if (n_fds || timeout != 0)
{
#ifdef G_MAIN_POLL_DEBUG
+ poll_timer = NULL;
if (_g_main_poll_debug)
{
g_print ("polling context=%p n=%d timeout=%d\n",
newrec->fd = fd;
newrec->priority = priority;
- prevrec = context->poll_records_tail;
- nextrec = NULL;
- while (prevrec && priority < prevrec->priority)
+ prevrec = NULL;
+ nextrec = context->poll_records;
+ while (nextrec)
{
- nextrec = prevrec;
- prevrec = prevrec->prev;
+ if (nextrec->fd > fd)
+ break;
+ prevrec = nextrec;
+ nextrec = nextrec->next;
}
if (prevrec)
if (nextrec)
nextrec->prev = newrec;
- else
- context->poll_records_tail = newrec;
context->n_poll_records++;
if (nextrec != NULL)
nextrec->prev = prevrec;
- else
- context->poll_records_tail = prevrec;
g_slice_free (GPollRec, pollrec);