mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-03-15 04:05:11 +01:00
gsource: Add support for file descriptors on UNIX
Adding file descriptors to a GSource provides similar functionality to the old g_source_add_poll() API with two main differences. First: the list of handles is managed internally and therefore users are prevented from randomly modifying the ->events field. This prepares us for an epoll future where changing the event mask is a syscall. Second: keeping the list internally allows us to check the ->revents for events for ourselves, allowing the source to skip implementing check/prepare. This also prepares us for the future by allowing an implementation that doesn't need to iterate over all of the sources every time. https://bugzilla.gnome.org/show_bug.cgi?id=686853
This commit is contained in:
parent
db7d5306cc
commit
cbf68cb22d
@ -530,6 +530,10 @@ GSourceFunc
|
|||||||
g_source_set_callback_indirect
|
g_source_set_callback_indirect
|
||||||
g_source_set_ready_time
|
g_source_set_ready_time
|
||||||
g_source_get_ready_time
|
g_source_get_ready_time
|
||||||
|
g_source_add_unix_fd
|
||||||
|
g_source_remove_unix_fd
|
||||||
|
g_source_modify_unix_fd
|
||||||
|
g_source_query_unix_fd
|
||||||
g_source_add_poll
|
g_source_add_poll
|
||||||
g_source_remove_poll
|
g_source_remove_poll
|
||||||
g_source_add_child_source
|
g_source_add_child_source
|
||||||
|
@ -628,6 +628,7 @@ g_main_loop_ref
|
|||||||
g_main_loop_run
|
g_main_loop_run
|
||||||
g_main_loop_unref
|
g_main_loop_unref
|
||||||
g_source_add_child_source
|
g_source_add_child_source
|
||||||
|
g_source_add_unix_fd
|
||||||
g_source_add_poll
|
g_source_add_poll
|
||||||
g_source_attach
|
g_source_attach
|
||||||
g_source_destroy
|
g_source_destroy
|
||||||
@ -639,12 +640,15 @@ g_source_get_id
|
|||||||
g_source_get_name
|
g_source_get_name
|
||||||
g_source_get_priority
|
g_source_get_priority
|
||||||
g_source_get_ready_time
|
g_source_get_ready_time
|
||||||
|
g_source_modify_unix_fd
|
||||||
g_source_new
|
g_source_new
|
||||||
|
g_source_query_unix_fd
|
||||||
g_source_ref
|
g_source_ref
|
||||||
g_source_remove
|
g_source_remove
|
||||||
g_source_remove_by_funcs_user_data
|
g_source_remove_by_funcs_user_data
|
||||||
g_source_remove_by_user_data
|
g_source_remove_by_user_data
|
||||||
g_source_remove_child_source
|
g_source_remove_child_source
|
||||||
|
g_source_remove_unix_fd
|
||||||
g_source_remove_poll
|
g_source_remove_poll
|
||||||
g_source_set_callback
|
g_source_set_callback
|
||||||
g_source_set_callback_indirect
|
g_source_set_callback_indirect
|
||||||
|
219
glib/gmain.c
219
glib/gmain.c
@ -318,6 +318,11 @@ struct _GSourcePrivate
|
|||||||
GSource *parent_source;
|
GSource *parent_source;
|
||||||
|
|
||||||
gint64 ready_time;
|
gint64 ready_time;
|
||||||
|
|
||||||
|
/* This is currently only used on UNIX, but we always declare it (and
|
||||||
|
* let it remain empty on Windows) to avoid #ifdef all over the place.
|
||||||
|
*/
|
||||||
|
GSList *fds;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _GSourceIter
|
typedef struct _GSourceIter
|
||||||
@ -1116,6 +1121,9 @@ g_source_attach_unlocked (GSource *source,
|
|||||||
tmp_list = tmp_list->next;
|
tmp_list = tmp_list->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
|
||||||
|
g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
|
||||||
|
|
||||||
tmp_list = source->priv->child_sources;
|
tmp_list = source->priv->child_sources;
|
||||||
while (tmp_list)
|
while (tmp_list)
|
||||||
{
|
{
|
||||||
@ -1201,6 +1209,9 @@ g_source_destroy_internal (GSource *source,
|
|||||||
g_main_context_remove_poll_unlocked (context, tmp_list->data);
|
g_main_context_remove_poll_unlocked (context, tmp_list->data);
|
||||||
tmp_list = tmp_list->next;
|
tmp_list = tmp_list->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
|
||||||
|
g_main_context_remove_poll_unlocked (context, tmp_list->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (source->priv->child_sources)
|
while (source->priv->child_sources)
|
||||||
@ -1301,6 +1312,10 @@ g_source_get_context (GSource *source)
|
|||||||
* event source. The event source's check function will typically test
|
* event source. The event source's check function will typically test
|
||||||
* the @revents field in the #GPollFD struct and return %TRUE if events need
|
* the @revents field in the #GPollFD struct and return %TRUE if events need
|
||||||
* to be processed.
|
* to be processed.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
**/
|
**/
|
||||||
void
|
void
|
||||||
g_source_add_poll (GSource *source,
|
g_source_add_poll (GSource *source,
|
||||||
@ -1640,6 +1655,12 @@ g_source_set_priority_unlocked (GSource *source,
|
|||||||
|
|
||||||
tmp_list = tmp_list->next;
|
tmp_list = tmp_list->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
|
||||||
|
{
|
||||||
|
g_main_context_remove_poll_unlocked (context, tmp_list->data);
|
||||||
|
g_main_context_add_poll_unlocked (context, priority, tmp_list->data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1969,6 +1990,8 @@ g_source_unref_internal (GSource *source,
|
|||||||
g_slist_free (source->poll_fds);
|
g_slist_free (source->poll_fds);
|
||||||
source->poll_fds = NULL;
|
source->poll_fds = NULL;
|
||||||
|
|
||||||
|
g_slist_free_full (source->priv->fds, g_free);
|
||||||
|
|
||||||
g_slice_free (GSourcePrivate, source->priv);
|
g_slice_free (GSourcePrivate, source->priv);
|
||||||
source->priv = NULL;
|
source->priv = NULL;
|
||||||
|
|
||||||
@ -2222,6 +2245,174 @@ g_source_remove_by_funcs_user_data (GSourceFuncs *funcs,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef G_OS_UNIX
|
||||||
|
/**
|
||||||
|
* g_source_add_unix_fd:
|
||||||
|
* @source: a #GSource
|
||||||
|
* @fd: the fd to monitor
|
||||||
|
* @events: an event mask
|
||||||
|
*
|
||||||
|
* Monitors @fd for the IO events in @events.
|
||||||
|
*
|
||||||
|
* The tag returned by this function can be used to remove or modify the
|
||||||
|
* monitoring of the fd using g_source_remove_unix_fd() or
|
||||||
|
* g_source_modify_unix_fd().
|
||||||
|
*
|
||||||
|
* It is not necessary to remove the fd before destroying the source; it
|
||||||
|
* will be cleaned up automatically.
|
||||||
|
*
|
||||||
|
* As the name suggests, this function is not available on Windows.
|
||||||
|
*
|
||||||
|
* Returns: an opaque tag
|
||||||
|
*
|
||||||
|
* Since: 2.36
|
||||||
|
**/
|
||||||
|
gpointer
|
||||||
|
g_source_add_unix_fd (GSource *source,
|
||||||
|
gint fd,
|
||||||
|
GIOCondition events)
|
||||||
|
{
|
||||||
|
GMainContext *context;
|
||||||
|
GPollFD *poll_fd;
|
||||||
|
|
||||||
|
g_return_val_if_fail (source != NULL, NULL);
|
||||||
|
g_return_val_if_fail (!SOURCE_DESTROYED (source), NULL);
|
||||||
|
|
||||||
|
poll_fd = g_new (GPollFD, 1);
|
||||||
|
poll_fd->fd = fd;
|
||||||
|
poll_fd->events = events;
|
||||||
|
poll_fd->revents = 0;
|
||||||
|
|
||||||
|
context = source->context;
|
||||||
|
|
||||||
|
if (context)
|
||||||
|
LOCK_CONTEXT (context);
|
||||||
|
|
||||||
|
source->priv->fds = g_slist_prepend (source->priv->fds, poll_fd);
|
||||||
|
|
||||||
|
if (context)
|
||||||
|
{
|
||||||
|
if (!SOURCE_BLOCKED (source))
|
||||||
|
g_main_context_add_poll_unlocked (context, source->priority, poll_fd);
|
||||||
|
UNLOCK_CONTEXT (context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return poll_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_source_modify_unix_fd:
|
||||||
|
* @source: a #GSource
|
||||||
|
* @tag: the tag from g_source_add_unix_fd()
|
||||||
|
* @new_events: the new event mask to watch
|
||||||
|
*
|
||||||
|
* Updates the event mask to watch for the fd identified by @tag.
|
||||||
|
*
|
||||||
|
* @tag is the tag returned from g_source_add_unix_fd().
|
||||||
|
*
|
||||||
|
* If you want to remove a fd, don't set its event mask to zero.
|
||||||
|
* Instead, call g_source_remove_unix_fd().
|
||||||
|
*
|
||||||
|
* As the name suggests, this function is not available on Windows.
|
||||||
|
*
|
||||||
|
* Since: 2.36
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
g_source_modify_unix_fd (GSource *source,
|
||||||
|
gpointer tag,
|
||||||
|
GIOCondition new_events)
|
||||||
|
{
|
||||||
|
GMainContext *context;
|
||||||
|
GPollFD *poll_fd;
|
||||||
|
|
||||||
|
g_return_if_fail (source != NULL);
|
||||||
|
g_return_if_fail (g_slist_find (source->priv->fds, tag));
|
||||||
|
|
||||||
|
context = source->context;
|
||||||
|
poll_fd = tag;
|
||||||
|
|
||||||
|
poll_fd->events = new_events;
|
||||||
|
|
||||||
|
if (context)
|
||||||
|
g_main_context_wakeup (context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_source_remove_unix_fd:
|
||||||
|
* @source: a #GSource
|
||||||
|
* @tag: the tag from g_source_add_unix_fd()
|
||||||
|
*
|
||||||
|
* Reverses the effect of a previous call to g_source_add_unix_fd().
|
||||||
|
*
|
||||||
|
* You only need to call this if you want to remove an fd from being
|
||||||
|
* watched while keeping the same source around. In the normal case you
|
||||||
|
* will just want to destroy the source.
|
||||||
|
*
|
||||||
|
* As the name suggests, this function is not available on Windows.
|
||||||
|
*
|
||||||
|
* Since: 2.36
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
g_source_remove_unix_fd (GSource *source,
|
||||||
|
gpointer tag)
|
||||||
|
{
|
||||||
|
GMainContext *context;
|
||||||
|
GPollFD *poll_fd;
|
||||||
|
|
||||||
|
g_return_if_fail (source != NULL);
|
||||||
|
g_return_if_fail (g_slist_find (source->priv->fds, tag));
|
||||||
|
|
||||||
|
context = source->context;
|
||||||
|
poll_fd = tag;
|
||||||
|
|
||||||
|
if (context)
|
||||||
|
LOCK_CONTEXT (context);
|
||||||
|
|
||||||
|
source->priv->fds = g_slist_remove (source->priv->fds, poll_fd);
|
||||||
|
|
||||||
|
if (context)
|
||||||
|
{
|
||||||
|
if (!SOURCE_BLOCKED (source))
|
||||||
|
g_main_context_remove_poll_unlocked (context, poll_fd);
|
||||||
|
|
||||||
|
UNLOCK_CONTEXT (context);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (poll_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_source_query_unix_fd:
|
||||||
|
* @source: a #GSource
|
||||||
|
* @tag: the tag from g_source_add_unix_fd()
|
||||||
|
*
|
||||||
|
* Queries the events reported for the fd corresponding to @tag on
|
||||||
|
* @source during the last poll.
|
||||||
|
*
|
||||||
|
* The return value of this function is only defined when the function
|
||||||
|
* is called from the check or dispatch functions for @source.
|
||||||
|
*
|
||||||
|
* As the name suggests, this function is not available on Windows.
|
||||||
|
*
|
||||||
|
* Returns: the conditions reported on the fd
|
||||||
|
*
|
||||||
|
* Since: 2.36
|
||||||
|
**/
|
||||||
|
GIOCondition
|
||||||
|
g_source_query_unix_fd (GSource *source,
|
||||||
|
gpointer tag)
|
||||||
|
{
|
||||||
|
GPollFD *poll_fd;
|
||||||
|
|
||||||
|
g_return_val_if_fail (source != NULL, 0);
|
||||||
|
g_return_val_if_fail (g_slist_find (source->priv->fds, tag), 0);
|
||||||
|
|
||||||
|
poll_fd = tag;
|
||||||
|
|
||||||
|
return poll_fd->revents;
|
||||||
|
}
|
||||||
|
#endif /* G_OS_UNIX */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_get_current_time:
|
* g_get_current_time:
|
||||||
* @result: #GTimeVal structure in which to store current time.
|
* @result: #GTimeVal structure in which to store current time.
|
||||||
@ -2747,6 +2938,9 @@ block_source (GSource *source)
|
|||||||
tmp_list = tmp_list->next;
|
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);
|
||||||
|
|
||||||
if (source->priv && source->priv->child_sources)
|
if (source->priv && source->priv->child_sources)
|
||||||
{
|
{
|
||||||
tmp_list = source->priv->child_sources;
|
tmp_list = source->priv->child_sources;
|
||||||
@ -2776,6 +2970,9 @@ unblock_source (GSource *source)
|
|||||||
tmp_list = tmp_list->next;
|
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);
|
||||||
|
|
||||||
if (source->priv && source->priv->child_sources)
|
if (source->priv && source->priv->child_sources)
|
||||||
{
|
{
|
||||||
tmp_list = source->priv->child_sources;
|
tmp_list = source->priv->child_sources;
|
||||||
@ -3347,6 +3544,26 @@ g_main_context_check (GMainContext *context,
|
|||||||
else
|
else
|
||||||
result = FALSE;
|
result = FALSE;
|
||||||
|
|
||||||
|
if (result == FALSE)
|
||||||
|
{
|
||||||
|
GSList *tmp_list;
|
||||||
|
|
||||||
|
/* If not already explicitly flagged ready by ->check()
|
||||||
|
* (or if we have no check) then we can still be ready if
|
||||||
|
* any of our fds poll as ready.
|
||||||
|
*/
|
||||||
|
for (tmp_list = source->priv->fds; tmp_list; tmp_list = tmp_list->next)
|
||||||
|
{
|
||||||
|
GPollFD *pollfd = tmp_list->data;
|
||||||
|
|
||||||
|
if (pollfd->revents)
|
||||||
|
{
|
||||||
|
result = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (result == FALSE && source->priv->ready_time != -1)
|
if (result == FALSE && source->priv->ready_time != -1)
|
||||||
{
|
{
|
||||||
if (!context->time_is_fresh)
|
if (!context->time_is_fresh)
|
||||||
@ -3838,7 +4055,7 @@ g_main_context_poll (GMainContext *context,
|
|||||||
*
|
*
|
||||||
* Adds a file descriptor to the set of file descriptors polled for
|
* Adds a file descriptor to the set of file descriptors polled for
|
||||||
* this context. This will very seldom be used directly. Instead
|
* this context. This will very seldom be used directly. Instead
|
||||||
* a typical event source will use g_source_add_poll() instead.
|
* a typical event source will use g_source_add_unix_fd() instead.
|
||||||
**/
|
**/
|
||||||
void
|
void
|
||||||
g_main_context_add_poll (GMainContext *context,
|
g_main_context_add_poll (GMainContext *context,
|
||||||
|
17
glib/gmain.h
17
glib/gmain.h
@ -470,6 +470,23 @@ void g_source_set_ready_time (GSource *source,
|
|||||||
GLIB_AVAILABLE_IN_2_36
|
GLIB_AVAILABLE_IN_2_36
|
||||||
gint64 g_source_get_ready_time (GSource *source);
|
gint64 g_source_get_ready_time (GSource *source);
|
||||||
|
|
||||||
|
#ifdef G_OS_UNIX
|
||||||
|
GLIB_AVAILABLE_IN_2_36
|
||||||
|
gpointer g_source_add_unix_fd (GSource *source,
|
||||||
|
gint fd,
|
||||||
|
GIOCondition events);
|
||||||
|
GLIB_AVAILABLE_IN_2_36
|
||||||
|
void g_source_modify_unix_fd (GSource *source,
|
||||||
|
gpointer tag,
|
||||||
|
GIOCondition new_events);
|
||||||
|
GLIB_AVAILABLE_IN_2_36
|
||||||
|
void g_source_remove_unix_fd (GSource *source,
|
||||||
|
gpointer tag);
|
||||||
|
GLIB_AVAILABLE_IN_2_36
|
||||||
|
GIOCondition g_source_query_unix_fd (GSource *source,
|
||||||
|
gpointer tag);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Used to implement g_source_connect_closure and internally*/
|
/* Used to implement g_source_connect_closure and internally*/
|
||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
void g_source_set_callback_indirect (GSource *source,
|
void g_source_set_callback_indirect (GSource *source,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user