From ce7c28c514c8ae1f1a9f5d9b56fbe503ec815109 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 19 May 2021 23:17:43 +0200 Subject: [PATCH] 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. --- src/libnm-glib-aux/nm-shared-utils.c | 27 ++++++++++ src/libnm-glib-aux/nm-shared-utils.h | 25 +++++++++ .../tests/test-shared-general.c | 53 +++++++++++++++++++ 3 files changed, 105 insertions(+) diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index 2a7432751d..2da7402e23 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -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, diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index 82af7a052f..ef159576f3 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -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, diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index afdb8871de..4987b5a901 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -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(); }