NetworkManager/shared/nm-glib-aux/nm-glib.h
Thomas Haller 7c60e984b6
shared: also reimplement g_atomic_pointer_set() macro
It's not strictly necessary, because contrary to g_atomic_pointer_get()
and g_atomic_pointer_compare_and_exchange(), glib's variant for the
setter is mostly fine.

Still, reimplement it, because we use typeof() eagerly and can thus add
more static checks than glib.
2020-11-03 15:39:00 +01:00

708 lines
27 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Copyright (C) 2008 - 2018 Red Hat, Inc.
*/
#ifndef __NM_GLIB_H__
#define __NM_GLIB_H__
/*****************************************************************************/
#ifndef __NM_MACROS_INTERNAL_H__
#error "nm-glib.h requires nm-macros-internal.h. Do not include this directly"
#endif
/*****************************************************************************/
#ifdef __clang__
#undef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
#undef G_GNUC_END_IGNORE_DEPRECATIONS
#define G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
#define G_GNUC_END_IGNORE_DEPRECATIONS _Pragma("clang diagnostic pop")
#endif
/*****************************************************************************/
static inline void
__g_type_ensure(GType type)
{
#if !GLIB_CHECK_VERSION(2, 34, 0)
if (G_UNLIKELY(type == (GType) -1))
g_error("can't happen");
#else
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
g_type_ensure(type);
G_GNUC_END_IGNORE_DEPRECATIONS;
#endif
}
#define g_type_ensure __g_type_ensure
/*****************************************************************************/
#if !GLIB_CHECK_VERSION(2, 34, 0)
#define g_clear_pointer(pp, destroy) \
G_STMT_START \
{ \
G_STATIC_ASSERT(sizeof *(pp) == sizeof(gpointer)); \
/* Only one access, please */ \
gpointer *_pp = (gpointer *) (pp); \
gpointer _p; \
/* This assignment is needed to avoid a gcc warning */ \
GDestroyNotify _destroy = (GDestroyNotify)(destroy); \
\
_p = *_pp; \
if (_p) { \
*_pp = NULL; \
_destroy(_p); \
} \
} \
G_STMT_END
#endif
/*****************************************************************************/
#if !GLIB_CHECK_VERSION(2, 34, 0)
/* These are used to clean up the output of test programs; we can just let
* them no-op in older glib.
*/
#define g_test_expect_message(log_domain, log_level, pattern)
#define g_test_assert_expected_messages()
#else
/* We build with -DGLIB_MAX_ALLOWED_VERSION set to 2.32 to make sure we don't
* accidentally use new API that we shouldn't. But we don't want warnings for
* the APIs that we emulate above.
*/
#define g_test_expect_message(domain, level, format...) \
G_STMT_START \
{ \
G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
g_test_expect_message(domain, level, format); \
G_GNUC_END_IGNORE_DEPRECATIONS \
} \
G_STMT_END
#define g_test_assert_expected_messages_internal(domain, file, line, func) \
G_STMT_START \
{ \
G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
g_test_assert_expected_messages_internal(domain, file, line, func); \
G_GNUC_END_IGNORE_DEPRECATIONS \
} \
G_STMT_END
#endif
/*****************************************************************************/
#if GLIB_CHECK_VERSION(2, 35, 0)
/* For glib >= 2.36, g_type_init() is deprecated.
* But since 2.35.1 (7c42ab23b55c43ab96d0ac2124b550bf1f49c1ec) this function
* does nothing. Replace the call with empty statement. */
#define nm_g_type_init() \
G_STMT_START \
{ \
(void) 0; \
} \
G_STMT_END
#else
#define nm_g_type_init() \
G_STMT_START \
{ \
g_type_init(); \
} \
G_STMT_END
#endif
/*****************************************************************************/
/* g_test_initialized() is only available since glib 2.36. */
#if !GLIB_CHECK_VERSION(2, 36, 0)
#define g_test_initialized() (g_test_config_vars->test_initialized)
#endif
/*****************************************************************************/
/* g_assert_cmpmem() is only available since glib 2.46. */
#if !GLIB_CHECK_VERSION(2, 45, 7)
#define g_assert_cmpmem(m1, l1, m2, l2) \
G_STMT_START \
{ \
gconstpointer __m1 = m1, __m2 = m2; \
int __l1 = l1, __l2 = l2; \
if (__l1 != __l2) \
g_assertion_message_cmpnum(G_LOG_DOMAIN, \
__FILE__, \
__LINE__, \
G_STRFUNC, \
#l1 " (len(" #m1 ")) == " #l2 " (len(" #m2 "))", \
__l1, \
"==", \
__l2, \
'i'); \
else if (memcmp(__m1, __m2, __l1) != 0) \
g_assertion_message(G_LOG_DOMAIN, \
__FILE__, \
__LINE__, \
G_STRFUNC, \
"assertion failed (" #m1 " == " #m2 ")"); \
} \
G_STMT_END
#endif
/*****************************************************************************/
/* Rumtime check for glib version. First do a compile time check which
* (if satisfied) shortcuts the runtime check. */
static inline gboolean
nm_glib_check_version(guint major, guint minor, guint micro)
{
return GLIB_CHECK_VERSION(major, minor, micro)
|| ((glib_major_version > major)
|| (glib_major_version == major && glib_minor_version > minor)
|| (glib_major_version == major && glib_minor_version == minor
&& glib_micro_version < micro));
}
/*****************************************************************************/
/* g_test_skip() is only available since glib 2.38. Add a compatibility wrapper. */
static inline void
__nmtst_g_test_skip(const char *msg)
{
#if GLIB_CHECK_VERSION(2, 38, 0)
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_test_skip(msg);
G_GNUC_END_IGNORE_DEPRECATIONS
#else
g_debug("%s", msg);
#endif
}
#define g_test_skip __nmtst_g_test_skip
/*****************************************************************************/
/* g_test_add_data_func_full() is only available since glib 2.34. Add a compatibility wrapper. */
static inline void
__g_test_add_data_func_full(const char * testpath,
gpointer test_data,
GTestDataFunc test_func,
GDestroyNotify data_free_func)
{
#if GLIB_CHECK_VERSION(2, 34, 0)
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_test_add_data_func_full(testpath, test_data, test_func, data_free_func);
G_GNUC_END_IGNORE_DEPRECATIONS
#else
g_return_if_fail(testpath != NULL);
g_return_if_fail(testpath[0] == '/');
g_return_if_fail(test_func != NULL);
g_test_add_vtable(testpath,
0,
test_data,
NULL,
(GTestFixtureFunc) test_func,
(GTestFixtureFunc) data_free_func);
#endif
}
#define g_test_add_data_func_full __g_test_add_data_func_full
/*****************************************************************************/
static inline gboolean
nm_g_hash_table_replace(GHashTable *hash, gpointer key, gpointer value)
{
/* glib 2.40 added a return value indicating whether the key already existed
* (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */
#if GLIB_CHECK_VERSION(2, 40, 0)
return g_hash_table_replace(hash, key, value);
#else
gboolean contained = g_hash_table_contains(hash, key);
g_hash_table_replace(hash, key, value);
return !contained;
#endif
}
static inline gboolean
nm_g_hash_table_insert(GHashTable *hash, gpointer key, gpointer value)
{
/* glib 2.40 added a return value indicating whether the key already existed
* (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */
#if GLIB_CHECK_VERSION(2, 40, 0)
return g_hash_table_insert(hash, key, value);
#else
gboolean contained = g_hash_table_contains(hash, key);
g_hash_table_insert(hash, key, value);
return !contained;
#endif
}
static inline gboolean
nm_g_hash_table_add(GHashTable *hash, gpointer key)
{
/* glib 2.40 added a return value indicating whether the key already existed
* (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */
#if GLIB_CHECK_VERSION(2, 40, 0)
return g_hash_table_add(hash, key);
#else
gboolean contained = g_hash_table_contains(hash, key);
g_hash_table_add(hash, key);
return !contained;
#endif
}
/*****************************************************************************/
#if !GLIB_CHECK_VERSION(2, 40, 0) || defined(NM_GLIB_COMPAT_H_TEST)
static inline void
_nm_g_ptr_array_insert(GPtrArray *array, int index_, gpointer data)
{
g_return_if_fail(array);
g_return_if_fail(index_ >= -1);
g_return_if_fail(index_ <= (int) array->len);
g_ptr_array_add(array, data);
if (index_ != -1 && index_ != (int) (array->len - 1)) {
memmove(&(array->pdata[index_ + 1]),
&(array->pdata[index_]),
(array->len - index_ - 1) * sizeof(gpointer));
array->pdata[index_] = data;
}
}
#endif
#if !GLIB_CHECK_VERSION(2, 40, 0)
#define g_ptr_array_insert(array, index, data) \
G_STMT_START \
{ \
_nm_g_ptr_array_insert(array, index, data); \
} \
G_STMT_END
#else
#define g_ptr_array_insert(array, index, data) \
G_STMT_START \
{ \
G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
g_ptr_array_insert(array, index, data); \
G_GNUC_END_IGNORE_DEPRECATIONS \
} \
G_STMT_END
#endif
/*****************************************************************************/
#if !GLIB_CHECK_VERSION(2, 40, 0)
static inline gboolean
_g_key_file_save_to_file(GKeyFile *key_file, const char *filename, GError **error)
{
char * contents;
gboolean success;
gsize length;
g_return_val_if_fail(key_file != NULL, FALSE);
g_return_val_if_fail(filename != NULL, FALSE);
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
contents = g_key_file_to_data(key_file, &length, NULL);
g_assert(contents != NULL);
success = g_file_set_contents(filename, contents, length, error);
g_free(contents);
return success;
}
#define g_key_file_save_to_file(key_file, filename, error) \
_g_key_file_save_to_file(key_file, filename, error)
#else
#define g_key_file_save_to_file(key_file, filename, error) \
({ \
gboolean _success; \
\
G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
_success = g_key_file_save_to_file(key_file, filename, error); \
G_GNUC_END_IGNORE_DEPRECATIONS \
_success; \
})
#endif
/*****************************************************************************/
#if GLIB_CHECK_VERSION(2, 36, 0)
#define g_credentials_get_unix_pid(creds, error) \
({ \
G_GNUC_BEGIN_IGNORE_DEPRECATIONS(g_credentials_get_unix_pid)((creds), (error)); \
G_GNUC_END_IGNORE_DEPRECATIONS \
})
#else
#define g_credentials_get_unix_pid(creds, error) \
({ \
struct ucred *native_creds; \
\
native_creds = g_credentials_get_native((creds), G_CREDENTIALS_TYPE_LINUX_UCRED); \
g_assert(native_creds); \
native_creds->pid; \
})
#endif
/*****************************************************************************/
#if !GLIB_CHECK_VERSION(2, 40, 0) || defined(NM_GLIB_COMPAT_H_TEST)
static inline gpointer *
_nm_g_hash_table_get_keys_as_array(GHashTable *hash_table, guint *length)
{
GHashTableIter iter;
gpointer key, *ret;
guint i = 0;
g_return_val_if_fail(hash_table, NULL);
ret = g_new0(gpointer, g_hash_table_size(hash_table) + 1);
g_hash_table_iter_init(&iter, hash_table);
while (g_hash_table_iter_next(&iter, &key, NULL))
ret[i++] = key;
ret[i] = NULL;
if (length)
*length = i;
return ret;
}
#endif
#if !GLIB_CHECK_VERSION(2, 40, 0)
#define g_hash_table_get_keys_as_array(hash_table, length) \
({ _nm_g_hash_table_get_keys_as_array(hash_table, length); })
#else
#define g_hash_table_get_keys_as_array(hash_table, length) \
({ \
G_GNUC_BEGIN_IGNORE_DEPRECATIONS(g_hash_table_get_keys_as_array) \
((hash_table), (length)); \
G_GNUC_END_IGNORE_DEPRECATIONS \
})
#endif
/*****************************************************************************/
#ifndef g_info
/* g_info was only added with 2.39.2 */
#define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
#endif
/*****************************************************************************/
static inline gpointer
_nm_g_steal_pointer(gpointer pp)
{
gpointer *ptr = (gpointer *) pp;
gpointer ref;
ref = *ptr;
*ptr = NULL;
return ref;
}
#if !GLIB_CHECK_VERSION(2, 44, 0)
static inline gpointer
g_steal_pointer(gpointer pp)
{
return _nm_g_steal_pointer(pp);
}
#endif
#ifdef g_steal_pointer
#undef g_steal_pointer
#endif
#define g_steal_pointer(pp) ((typeof(*(pp))) _nm_g_steal_pointer(pp))
/*****************************************************************************/
static inline gboolean
_nm_g_strv_contains(const char *const *strv, const char *str)
{
#if !GLIB_CHECK_VERSION(2, 44, 0)
g_return_val_if_fail(strv != NULL, FALSE);
g_return_val_if_fail(str != NULL, FALSE);
for (; *strv != NULL; strv++) {
if (g_str_equal(str, *strv))
return TRUE;
}
return FALSE;
#else
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
return g_strv_contains(strv, str);
G_GNUC_END_IGNORE_DEPRECATIONS
#endif
}
#define g_strv_contains _nm_g_strv_contains
/*****************************************************************************/
static inline GVariant *
_nm_g_variant_new_take_string(char *string)
{
#if !GLIB_CHECK_VERSION(2, 36, 0)
GVariant *value;
g_return_val_if_fail(string != NULL, NULL);
g_return_val_if_fail(g_utf8_validate(string, -1, NULL), NULL);
value = g_variant_new_string(string);
g_free(string);
return value;
#elif !GLIB_CHECK_VERSION(2, 38, 0)
GVariant *value;
GBytes * bytes;
g_return_val_if_fail(string != NULL, NULL);
g_return_val_if_fail(g_utf8_validate(string, -1, NULL), NULL);
bytes = g_bytes_new_take(string, strlen(string) + 1);
value = g_variant_new_from_bytes(G_VARIANT_TYPE_STRING, bytes, TRUE);
g_bytes_unref(bytes);
return value;
#else
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
return g_variant_new_take_string(string);
G_GNUC_END_IGNORE_DEPRECATIONS
#endif
}
#define g_variant_new_take_string _nm_g_variant_new_take_string
/*****************************************************************************/
#if !GLIB_CHECK_VERSION(2, 38, 0)
_nm_printf(1, 2) static inline GVariant *_nm_g_variant_new_printf(const char *format_string, ...)
{
char * string;
va_list ap;
g_return_val_if_fail(format_string, NULL);
va_start(ap, format_string);
string = g_strdup_vprintf(format_string, ap);
va_end(ap);
return g_variant_new_take_string(string);
}
#define g_variant_new_printf(...) _nm_g_variant_new_printf(__VA_ARGS__)
#else
#define g_variant_new_printf(...) \
({ \
GVariant *_v; \
\
G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
_v = g_variant_new_printf(__VA_ARGS__); \
G_GNUC_END_IGNORE_DEPRECATIONS \
_v; \
})
#endif
/*****************************************************************************/
/* Recent glib also casts the results to typeof(Obj), but only if
*
* ( defined(g_has_typeof) && GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_56 )
*
* Since we build NetworkManager with older GLIB_VERSION_MAX_ALLOWED, it's
* not taking effect.
*
* Override this. */
#undef g_object_ref
#undef g_object_ref_sink
#define g_object_ref(Obj) ((typeof(Obj)) g_object_ref(Obj))
#define g_object_ref_sink(Obj) ((typeof(Obj)) g_object_ref_sink(Obj))
/*****************************************************************************/
#ifndef g_autofree
/* we still don't rely on recent glib to provide g_autofree. Hence, we continue
* to use our gs_* free macros that we took from libgsystem.
*
* To ease migration towards g_auto*, add a compat define for g_autofree. */
#define g_autofree gs_free
#endif
/*****************************************************************************/
#if !GLIB_CHECK_VERSION(2, 47, 1)
/* Older versions of g_value_unset() only allowed to unset a GValue which
* was initialized previously. This was relaxed ([1], [2], [3]).
*
* Our nm_auto_unset_gvalue macro requires to be able to call g_value_unset().
* Also, it is our general practice to allow for that. Add a compat implementation.
*
* [1] https://gitlab.gnome.org/GNOME/glib/commit/4b2d92a864f1505f1b08eb639d74293fa32681da
* [2] commit "Allow passing unset GValues to g_value_unset()"
* [3] https://bugzilla.gnome.org/show_bug.cgi?id=755766
*/
static inline void
_nm_g_value_unset(GValue *value)
{
g_return_if_fail(value);
if (value->g_type != 0)
g_value_unset(value);
}
#define g_value_unset _nm_g_value_unset
#endif
/* G_PID_FORMAT was added only in 2.53.5. Define it ourself.
*
* If this was about "pid_t", we would check SIZEOF_PID_T, and set
* PRIi32/PRIi16, like systemd does. But it's actually about
* GPid, which glib typedefs as an "int".
*
* There is a test_gpid() that check that GPid is really a typedef
* for int. */
#undef G_PID_FORMAT
#define G_PID_FORMAT "i"
/*****************************************************************************/
/* G_SOURCE_FUNC was added in 2.57.2. */
#undef G_SOURCE_FUNC
#define G_SOURCE_FUNC(f) ((GSourceFunc)(void (*)(void))(f))
/*****************************************************************************/
/* g_atomic_pointer_get() is implemented as a macro, and it is also used for
* (gsize *) arguments. However, that leads to compiler warnings in certain
* configurations. Work around it, by redefining the macro. */
static inline gpointer
_g_atomic_pointer_get(void **atomic)
{
return g_atomic_pointer_get(atomic);
}
#undef g_atomic_pointer_get
#define g_atomic_pointer_get(atomic) \
({ \
typeof(*atomic) *const _atomic = (atomic); \
\
/* g_atomic_pointer_get() is used by glib also for (gsize *) pointers,
* not only pointers to pointers. We thus don't enforce that (*atomic)
* is a pointer, but of suitable size/alignment. */ \
\
G_STATIC_ASSERT(sizeof(*_atomic) == sizeof(gpointer)); \
G_STATIC_ASSERT(_nm_alignof(*_atomic) == _nm_alignof(gpointer)); \
(void) (0 ? (gpointer) * (_atomic) : NULL); \
\
(typeof(*_atomic)) _g_atomic_pointer_get((void **) _atomic); \
})
/* Reimplement g_atomic_pointer_set() macro too. Our variant does more type
* checks. */
static inline void
_g_atomic_pointer_set(void **atomic, void *newval)
{
return g_atomic_pointer_set(atomic, newval);
}
#undef g_atomic_pointer_set
#define g_atomic_pointer_set(atomic, newval) \
({ \
typeof(*atomic) *const _atomic = (atomic); \
typeof(*_atomic) const _newval = (newval); \
_nm_unused gconstpointer const _val_type_check = _newval; \
\
(void) (0 ? (gpointer) * (_atomic) : NULL); \
\
_g_atomic_pointer_set((void **) _atomic, (void *) _newval); \
})
/* Glib implements g_atomic_pointer_compare_and_exchange() as a macro.
* For one, to inline the atomic operation and also to perform some type checks
* on the arguments.
* Depending on compiler and glib version, glib passes the arguments as they
* are to __atomic_compare_exchange_n(). Some clang version don't accept const
* pointers there. Reimplement the macro to get that right, but with stronger
* type checks (as we use typeof()). Had one job. */
static inline gboolean
_g_atomic_pointer_compare_and_exchange(void **atomic, void *oldval, void *newval)
{
return g_atomic_pointer_compare_and_exchange(atomic, oldval, newval);
}
#undef g_atomic_pointer_compare_and_exchange
#define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \
({ \
typeof(*atomic) *const _atomic = (atomic); \
typeof(*_atomic) const _oldval = (oldval); \
typeof(*_atomic) const _newval = (newval); \
_nm_unused gconstpointer const _val_type_check = _oldval; \
\
(void) (0 ? (gpointer) * (_atomic) : NULL); \
\
_g_atomic_pointer_compare_and_exchange((void **) _atomic, \
(void *) _oldval, \
(void *) _newval); \
})
/*****************************************************************************/
#if !GLIB_CHECK_VERSION(2, 58, 0)
static inline gboolean
g_hash_table_steal_extended(GHashTable * hash_table,
gconstpointer lookup_key,
gpointer * stolen_key,
gpointer * stolen_value)
{
g_assert(stolen_key);
g_assert(stolen_value);
if (g_hash_table_lookup_extended(hash_table, lookup_key, stolen_key, stolen_value)) {
g_hash_table_steal(hash_table, lookup_key);
return TRUE;
}
*stolen_key = NULL;
*stolen_value = NULL;
return FALSE;
}
#else
#define g_hash_table_steal_extended(hash_table, lookup_key, stolen_key, stolen_value) \
({ \
gpointer *_stolen_key = (stolen_key); \
gpointer *_stolen_value = (stolen_value); \
\
/* we cannot allow NULL arguments, because then we would leak the values in
* the compat implementation. */ \
g_assert(_stolen_key); \
g_assert(_stolen_value); \
\
G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
g_hash_table_steal_extended(hash_table, lookup_key, _stolen_key, _stolen_value); \
G_GNUC_END_IGNORE_DEPRECATIONS \
})
#endif
/*****************************************************************************/
__attribute__((
__deprecated__("Don't use g_cancellable_reset(). Create a new cancellable instead."))) void
_nm_g_cancellable_reset(GCancellable *cancellable);
#undef g_cancellable_reset
#define g_cancellable_reset(cancellable) _nm_g_cancellable_reset(cancellable)
/*****************************************************************************/
#endif /* __NM_GLIB_H__ */