glib-aux: add nm_g_source_sentinel_get() util

This helper is useful to get a dummy GSource instance that can be
refed, unrefed and destroyed. It can act as a replacement for
a timeout source with infinite timeout.
This commit is contained in:
Thomas Haller 2021-05-19 23:17:43 +02:00
parent a9776e7ed9
commit ce7c28c514
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
3 changed files with 105 additions and 0 deletions

View file

@ -4851,6 +4851,33 @@ nm_utils_parse_debug_string(const char *string, const GDebugKey *keys, guint nke
/*****************************************************************************/
GSource *_nm_g_source_sentinel[] = {
NULL,
};
GSource *
_nm_g_source_sentinel_get_init(GSource **p_source)
{
static const GSourceFuncs source_funcs = {
NULL,
};
GSource *source;
again:
source = g_source_new((GSourceFuncs *) &source_funcs, sizeof(GSource));
g_source_set_priority(source, G_PRIORITY_DEFAULT_IDLE);
g_source_set_name(source, "nm_g_source_sentinel");
if (!g_atomic_pointer_compare_and_exchange(p_source, NULL, source)) {
g_source_unref(source);
goto again;
}
return source;
}
/*****************************************************************************/
GSource *
nm_g_idle_source_new(int priority,
GSourceFunc func,

View file

@ -1594,6 +1594,31 @@ nm_source_func_unref_gobject(gpointer user_data)
return G_SOURCE_REMOVE;
}
extern GSource *_nm_g_source_sentinel[1];
GSource *_nm_g_source_sentinel_get_init(GSource **p_source);
/* Get a GSource sentinel (dummy instance). This instance should never be
* attached to a GMainContext. The only currently known purpose is to use it
* as dummy value instead of an infinity timeout. That is, if we configurably
* want to schedule a timeout that might be infinity, we might set the GSource
* instance to nm_g_source_sentinel_get(). On this instance, we still may
* call g_source_ref(), g_source_unref() and g_source_destroy(). But nothing
* else. */
#define nm_g_source_sentinel_get(idx) \
({ \
GSource *_s; \
\
G_STATIC_ASSERT((idx) >= 0); \
G_STATIC_ASSERT((idx) < G_N_ELEMENTS(_nm_g_source_sentinel)); \
\
_s = g_atomic_pointer_get(&_nm_g_source_sentinel[idx]); \
if (G_UNLIKELY(!_s)) \
_s = _nm_g_source_sentinel_get_init(&_nm_g_source_sentinel[idx]); \
\
_s; \
})
GSource *nm_g_idle_source_new(int priority,
GSourceFunc func,
gpointer user_data,

View file

@ -1293,6 +1293,58 @@ again:
/*****************************************************************************/
static void
test_nm_g_source_sentinel(void)
{
GSource *s1;
GSource *s2;
int n;
int i;
int refs;
s1 = nm_g_source_sentinel_get(0);
g_assert_nonnull(s1);
g_assert_cmpint(g_atomic_int_get(&s1->ref_count), ==, 1);
s2 = nm_g_source_sentinel_get(0);
g_assert_nonnull(s2);
g_assert(s2 == s1);
g_assert_cmpint(g_atomic_int_get(&s1->ref_count), ==, 1);
n = nmtst_get_rand_uint32() % 7;
for (refs = 0, i = 0; i < n; i++) {
if (nmtst_get_rand_bool()) {
refs++;
g_source_ref(s1);
}
if (nmtst_get_rand_bool())
g_source_destroy(s1);
if (refs > 0 && nmtst_get_rand_bool()) {
refs--;
g_source_unref(s1);
}
if (nmtst_get_rand_bool()) {
s2 = nm_g_source_sentinel_get(0);
g_assert(s2 == s1);
g_assert_cmpint(g_atomic_int_get(&s1->ref_count), >=, 1);
}
}
for (; refs > 0;) {
if (nmtst_get_rand_bool())
g_source_destroy(s1);
if (nmtst_get_rand_bool()) {
refs--;
g_source_unref(s1);
}
}
g_assert_cmpint(g_atomic_int_get(&s1->ref_count), ==, 1);
}
/*****************************************************************************/
NMTST_DEFINE();
int
@ -1323,6 +1375,7 @@ main(int argc, char **argv)
g_test_add_func("/general/test_is_specific_hostname", test_is_specific_hostname);
g_test_add_func("/general/test_strv_dup_packed", test_strv_dup_packed);
g_test_add_func("/general/test_utils_hashtable_cmp", test_utils_hashtable_cmp);
g_test_add_func("/general/test_nm_g_source_sentinel", test_nm_g_source_sentinel);
return g_test_run();
}