Merge branch 'source-attach-trigger-wakeup' into 'main'

Add g_main_context_new_with_flags() and ownerless polling option

See merge request GNOME/glib!1960
This commit is contained in:
Philip Withnall 2021-11-01 18:39:56 +00:00
commit 48af1cbddc
4 changed files with 112 additions and 2 deletions

View File

@ -841,7 +841,9 @@ G_SOURCE_REMOVE
<SUBSECTION>
GMainContext
GMainContextFlags
g_main_context_new
g_main_context_new_with_flags
g_main_context_ref
g_main_context_unref
g_main_context_default

View File

@ -177,6 +177,15 @@
* g_main_context_prepare(), g_main_context_query(),
* g_main_context_check() and g_main_context_dispatch().
*
* If the event loop thread releases #GMainContext ownership until the results
* required by g_main_context_check() are ready you must create a context with
* the flag %G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING or else you'll lose
* g_source_attach() notifications. This happens for instance when you integrate
* the GLib event loop into implementations that follow the proactor pattern
* (i.e. in these contexts the `poll()` implementation will reclaim the thread for
* other tasks until the results are ready). One example of the proactor pattern
* is the Boost.Asio library.
*
* ## State of a Main Context # {#mainloop-states}
*
* The operation of these functions can best be seen in terms
@ -270,6 +279,7 @@ struct _GMainContext
GCond cond;
GThread *owner;
guint owner_count;
GMainContextFlags flags;
GSList *waiters;
gint ref_count; /* (atomic) */
@ -658,6 +668,23 @@ g_main_context_new_with_next_id (guint next_id)
**/
GMainContext *
g_main_context_new (void)
{
return g_main_context_new_with_flags (G_MAIN_CONTEXT_FLAGS_NONE);
}
/**
* g_main_context_new_with_flags:
* @flags: a bitwise-OR combination of #GMainContextFlags flags that can only be
* set at creation time.
*
* Creates a new #GMainContext structure.
*
* Returns: (transfer full): the new #GMainContext
*
* Since: 2.72
*/
GMainContext *
g_main_context_new_with_flags (GMainContextFlags flags)
{
static gsize initialised;
GMainContext *context;
@ -681,6 +708,7 @@ g_main_context_new (void)
context->sources = g_hash_table_new (NULL, NULL);
context->owner = NULL;
context->flags = flags;
context->waiters = NULL;
context->ref_count = 1;
@ -1248,8 +1276,12 @@ g_source_attach_unlocked (GSource *source,
/* If another thread has acquired the context, wake it up since it
* might be in poll() right now.
*/
if (do_wakeup && context->owner && context->owner != G_THREAD_SELF)
g_wakeup_signal (context->wakeup);
if (do_wakeup &&
(context->flags & G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING ||
(context->owner && context->owner != G_THREAD_SELF)))
{
g_wakeup_signal (context->wakeup);
}
g_trace_mark (G_TRACE_CURRENT_TIME, 0,
"GLib", "g_source_attach",

View File

@ -38,6 +38,26 @@ typedef enum /*< flags >*/
G_IO_NVAL GLIB_SYSDEF_POLLNVAL
} GIOCondition;
/**
* GMainContextFlags:
* @G_MAIN_CONTEXT_FLAGS_NONE: Default behaviour.
* @G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING: Assume that polling for events will
* free the thread to process other jobs. That's useful if you're using
* `g_main_context_{prepare,query,check,dispatch}` to integrate GMainContext in
* other event loops.
*
* Flags to pass to g_main_context_new_with_flags() which affect the behaviour
* of a #GMainContext.
*
* Since: 2.72
*/
GLIB_AVAILABLE_TYPE_IN_2_72
typedef enum /*< flags >*/
{
G_MAIN_CONTEXT_FLAGS_NONE = 0,
G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING = 1
} GMainContextFlags;
/**
* GMainContext:
@ -358,6 +378,10 @@ struct _GSourceFuncs
GLIB_AVAILABLE_IN_ALL
GMainContext *g_main_context_new (void);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
GLIB_AVAILABLE_IN_2_72
GMainContext *g_main_context_new_with_flags (GMainContextFlags flags);
G_GNUC_END_IGNORE_DEPRECATIONS
GLIB_AVAILABLE_IN_ALL
GMainContext *g_main_context_ref (GMainContext *context);
GLIB_AVAILABLE_IN_ALL

View File

@ -158,6 +158,56 @@ test_mainloop_basic (void)
g_main_loop_unref (loop);
}
static void
test_ownerless_polling (gconstpointer test_data)
{
gboolean attach_first = GPOINTER_TO_INT (test_data);
GMainContext *ctx = g_main_context_new_with_flags (
G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING);
GPollFD fds[20];
gint fds_size;
gint max_priority;
GSource *source = NULL;
g_assert_true (ctx != g_main_context_default ());
g_main_context_push_thread_default (ctx);
/* Drain events */
for (;;)
{
gboolean ready_to_dispatch = g_main_context_prepare (ctx, &max_priority);
gint timeout, nready;
fds_size = g_main_context_query (ctx, max_priority, &timeout, fds, G_N_ELEMENTS (fds));
nready = g_poll (fds, fds_size, /*timeout=*/0);
if (!ready_to_dispatch && nready == 0)
{
if (timeout == -1)
break;
else
g_usleep (timeout * 1000);
}
ready_to_dispatch = g_main_context_check (ctx, max_priority, fds, fds_size);
if (ready_to_dispatch)
g_main_context_dispatch (ctx);
}
if (!attach_first)
g_main_context_pop_thread_default (ctx);
source = g_idle_source_new ();
g_source_attach (source, ctx);
g_source_unref (source);
if (attach_first)
g_main_context_pop_thread_default (ctx);
g_assert_cmpint (g_poll (fds, fds_size, 0), >, 0);
g_main_context_unref (ctx);
}
static gint a;
static gint b;
static gint c;
@ -2145,6 +2195,8 @@ main (int argc, char *argv[])
#endif
g_test_add_func ("/mainloop/nfds", test_nfds);
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);
return g_test_run ();
}