Merge branch 'atomic-older-cplusplus' into 'main'

gatomic: fix the atomic compare_and_exchange macros on older C++ standard versions

See merge request GNOME/glib!2864
This commit is contained in:
Philip Withnall 2022-10-12 09:56:19 +00:00
commit e90733a457
8 changed files with 110 additions and 36 deletions

View File

@ -27,6 +27,8 @@ if cc.has_header('pty.h')
endif
endif
test_cpp_args = test_c_args
if host_machine.system() == 'windows'
common_gio_tests_deps += [iphlpapi_dep, winsock2, cc.find_library ('secur32')]
endif
@ -130,8 +132,19 @@ if have_cxx
gio_tests += {
'cxx' : {
'source' : ['cxx.cpp'],
'suite': ['C++'],
},
}
foreach std, arg: cxx_standards
gio_tests += {
'cxx-@0@'.format(std) : {
'source' : ['cxx.cpp'],
'suite' : ['cpp'],
'cpp_args' : [arg],
},
}
endforeach
endif
test_extra_programs = {
@ -882,6 +895,7 @@ foreach test_name, extra_args : gio_tests
exe = executable(test_name, [source, extra_sources],
c_args : test_c_args + extra_args.get('c_args', []),
cpp_args : test_cpp_args + extra_args.get('cpp_args', []),
dependencies : common_gio_tests_deps + extra_args.get('dependencies', []),
install_rpath : extra_args.get('install_rpath', ''),
install_dir: installed_tests_execdir,

View File

@ -170,7 +170,7 @@ G_END_DECLS
(void) (0 ? *(atomic) ^ *(atomic) : 1); \
__atomic_fetch_sub ((atomic), 1, __ATOMIC_SEQ_CST) == 1; \
}))
#if defined(glib_typeof) && defined(__cplusplus) && __cplusplus >= 201103L
#if defined(glib_typeof) && defined(__cplusplus)
/* See comments below about equivalent g_atomic_pointer_compare_and_exchange()
* shenanigans for type-safety when compiling in C++ mode. */
#define g_atomic_int_compare_and_exchange(atomic, oldval, newval) \
@ -180,7 +180,7 @@ G_END_DECLS
(void) (0 ? *(atomic) ^ (newval) ^ (oldval) : 1); \
__atomic_compare_exchange_n ((atomic), &gaicae_oldval, (newval), FALSE, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? TRUE : FALSE; \
}))
#else /* if !(defined(glib_typeof) && defined(__cplusplus) && __cplusplus >= 201103L) */
#else /* if !(defined(glib_typeof) && defined(__cplusplus)) */
#define g_atomic_int_compare_and_exchange(atomic, oldval, newval) \
(G_GNUC_EXTENSION ({ \
gint gaicae_oldval = (oldval); \
@ -230,7 +230,7 @@ G_END_DECLS
(guint) __atomic_fetch_xor ((atomic), (val), __ATOMIC_SEQ_CST); \
}))
#if defined(glib_typeof) && defined(__cplusplus) && __cplusplus >= 201103L
#if defined(glib_typeof) && defined(__cplusplus)
/* This is typesafe because we check we can assign oldval to the type of
* (*atomic). Unfortunately it can only be done in C++ because gcc/clang warn
* when atomic is volatile and not oldval, or when atomic is gsize* and oldval
@ -247,7 +247,7 @@ G_END_DECLS
(void) (0 ? (gpointer) *(atomic) : NULL); \
__atomic_compare_exchange_n ((atomic), &gapcae_oldval, (newval), FALSE, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? TRUE : FALSE; \
}))
#else /* if !(defined(glib_typeof) && defined(__cplusplus) && __cplusplus >= 201103L) */
#else /* if !(defined(glib_typeof) && defined(__cplusplus) */
#define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \
(G_GNUC_EXTENSION ({ \
G_STATIC_ASSERT (sizeof (oldval) == sizeof (gpointer)); \

View File

@ -34,9 +34,12 @@
* This symbol is private.
*/
#undef glib_typeof
#if !defined(__cplusplus) && (G_GNUC_CHECK_VERSION(4, 8) || defined(__clang__))
#if (!defined(__cplusplus) || (!defined (_MSVC_LANG) && __cplusplus < 201103L)) && \
(G_GNUC_CHECK_VERSION(4, 8) || defined(__clang__))
#define glib_typeof(t) __typeof__ (t)
#elif defined(__cplusplus) && __cplusplus >= 201103L && GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_68
#elif defined(__cplusplus) && \
(__cplusplus >= 201103L || (defined (_MSVC_LANG) && _MSVC_LANG > 201103L)) && \
GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_68
/* C++11 decltype() is close enough for our usage */
#include <type_traits>
#define glib_typeof(t) typename std::remove_reference<decltype (t)>::type

View File

@ -27,9 +27,6 @@ typedef struct
static void
test_typeof (void)
{
#if __cplusplus >= 201103L
// Test that with C++11 we don't get those kind of errors:
// error: invalid conversion from gpointer {aka void*} to MyObject* [-fpermissive]
MyObject *obj = g_rc_box_new0 (MyObject);
MyObject *obj2 = g_rc_box_acquire (obj);
g_assert_true (obj2 == obj);
@ -37,12 +34,21 @@ test_typeof (void)
MyObject *obj3 = g_atomic_pointer_get (&obj2);
g_assert_true (obj3 == obj);
#if __cplusplus >= 201103L
MyObject *obj4 = nullptr;
#else
MyObject *obj4 = NULL;
#endif
g_atomic_pointer_set (&obj4, obj3);
g_assert_true (obj4 == obj);
#if __cplusplus >= 201103L
MyObject *obj5 = nullptr;
g_atomic_pointer_compare_and_exchange (&obj5, nullptr, obj4);
#else
MyObject *obj5 = NULL;
g_atomic_pointer_compare_and_exchange (&obj5, NULL, obj4);
#endif
g_assert_true (obj5 == obj);
MyObject *obj6 = g_steal_pointer (&obj5);
@ -50,15 +56,11 @@ test_typeof (void)
g_clear_pointer (&obj6, g_rc_box_release);
g_rc_box_release (obj);
#else
g_test_skip ("This test requires a C++11 compiler");
#endif
}
static void
test_atomic_pointer_compare_and_exchange (void)
{
#if __cplusplus >= 201103L
const gchar *str1 = "str1";
const gchar *str2 = "str2";
const gchar *atomic_string = str1;
@ -68,15 +70,11 @@ test_atomic_pointer_compare_and_exchange (void)
g_assert_true (g_atomic_pointer_compare_and_exchange (&atomic_string, str1, str2));
g_assert_true (atomic_string == str2);
#else
g_test_skip ("This test requires a C++11 compiler");
#endif
}
static void
test_atomic_pointer_compare_and_exchange_full (void)
{
#if __cplusplus >= 201103L
const gchar *str1 = "str1";
const gchar *str2 = "str2";
const gchar *atomic_string = str1;
@ -88,15 +86,11 @@ test_atomic_pointer_compare_and_exchange_full (void)
g_assert_true (g_atomic_pointer_compare_and_exchange_full (&atomic_string, str1, str2, &old));
g_assert_true (atomic_string == str2);
g_assert_true (old == str1);
#else
g_test_skip ("This test requires a C++11 compiler");
#endif
}
static void
test_atomic_int_compare_and_exchange (void)
{
#if __cplusplus >= 201103L
gint atomic_int = 5;
g_test_message ("Test that g_atomic_int_compare_and_exchange() doesnt have "
@ -104,15 +98,11 @@ test_atomic_int_compare_and_exchange (void)
g_assert_true (g_atomic_int_compare_and_exchange (&atomic_int, 5, 50));
g_assert_cmpint (atomic_int, ==, 50);
#else
g_test_skip ("This test requires a C++11 compiler");
#endif
}
static void
test_atomic_int_compare_and_exchange_full (void)
{
#if __cplusplus >= 201103L
gint atomic_int = 5;
gint old_value;
@ -122,15 +112,11 @@ test_atomic_int_compare_and_exchange_full (void)
g_assert_true (g_atomic_int_compare_and_exchange_full (&atomic_int, 5, 50, &old_value));
g_assert_cmpint (atomic_int, ==, 50);
g_assert_cmpint (old_value, ==, 5);
#else
g_test_skip ("This test requires a C++11 compiler");
#endif
}
static void
test_atomic_pointer_exchange (void)
{
#if __cplusplus >= 201103L
const gchar *str1 = "str1";
const gchar *str2 = "str2";
const gchar *atomic_string = str1;
@ -140,24 +126,17 @@ test_atomic_pointer_exchange (void)
g_assert_true (g_atomic_pointer_exchange (&atomic_string, str2) == str1);
g_assert_true (atomic_string == str2);
#else
g_test_skip ("This test requires a C++11 compiler");
#endif
}
static void
test_atomic_int_exchange (void)
{
#if __cplusplus >= 201103L
gint atomic_int = 5;
g_test_message ("Test that g_atomic_int_compare_and_exchange() doesnt have "
"any compiler warnings in C++ mode");
g_assert_cmpint (g_atomic_int_exchange (&atomic_int, 50), ==, 5);
#else
g_test_skip ("This test requires a C++11 compiler");
#endif
}
G_NO_INLINE
@ -184,6 +163,35 @@ test_inline_no_inline_macros (void)
g_assert_true (do_inline_this ());
}
static void
clear_boolean_ptr (gboolean *val)
{
*val = TRUE;
}
static void
test_clear_pointer (void)
{
gboolean value = FALSE;
gboolean *ptr = &value;
g_assert_true (ptr == &value);
g_assert_false (value);
g_clear_pointer (&ptr, clear_boolean_ptr);
g_assert_null (ptr);
g_assert_true (value);
}
static void
test_steal_pointer (void)
{
gpointer ptr = &ptr;
g_assert_true (ptr == &ptr);
g_assert_true (g_steal_pointer (&ptr) == &ptr);
g_assert_null (ptr);
}
int
main (int argc, char *argv[])
{
@ -201,6 +209,8 @@ main (int argc, char *argv[])
g_test_add_func ("/C++/atomic-pointer-exchange", test_atomic_pointer_exchange);
g_test_add_func ("/C++/atomic-int-exchange", test_atomic_int_exchange);
g_test_add_func ("/C++/inlined-not-inlined-functions", test_inline_no_inline_macros);
g_test_add_func ("/C++/clear-pointer", test_clear_pointer);
g_test_add_func ("/C++/steal-pointer", test_steal_pointer);
return g_test_run ();
}

View File

@ -159,8 +159,19 @@ if have_cxx
glib_tests += {
'cxx' : {
'source' : ['cxx.cpp'],
'suite': ['cpp'],
}
}
foreach std, arg: cxx_standards
glib_tests += {
'cxx-@0@'.format(std) : {
'source' : ['cxx.cpp'],
'suite' : ['cpp'],
'cpp_args' : [arg],
},
}
endforeach
endif
if cc.get_id() != 'msvc'
@ -255,6 +266,7 @@ test_env.set('MALLOC_CHECK_', '2')
test_deps = [libm, thread_dep, libglib_dep]
test_cargs = ['-DG_LOG_DOMAIN="GLib"', '-UG_DISABLE_ASSERT']
test_cpp_args = test_cargs
foreach test_name, extra_args : glib_tests
source = extra_args.get('source', test_name + '.c')
@ -276,6 +288,7 @@ foreach test_name, extra_args : glib_tests
exe = executable(test_name, source,
c_args : test_cargs + extra_args.get('c_args', []),
cpp_args : test_cpp_args + extra_args.get('cpp_args', []),
link_args : extra_args.get('link_args', []),
dependencies : test_deps + extra_args.get('dependencies', []),
install_dir: installed_tests_execdir,

View File

@ -15,8 +15,19 @@ if have_cxx
gmodule_tests += {
'cxx' : {
'source' : ['cxx.cpp'],
'suite' : ['cpp'],
}
}
foreach std, arg: cxx_standards
gmodule_tests += {
'cxx-@0@'.format(std) : {
'source' : ['cxx.cpp'],
'suite' : ['cpp'],
'cpp_args' : [arg],
},
}
endforeach
endif
module_suffix = []
@ -51,6 +62,7 @@ test_env.set('MALLOC_CHECK_', '2')
test_deps = [libm, thread_dep, libglib_dep, libgmodule_dep]
test_cargs = ['-DG_LOG_DOMAIN="GModule"', '-UG_DISABLE_ASSERT']
test_cpp_args = test_cargs
foreach test_name, extra_args : gmodule_tests
source = extra_args.get('source', test_name + '.c')
@ -72,6 +84,7 @@ foreach test_name, extra_args : gmodule_tests
exe = executable(test_name, source,
c_args : test_cargs + extra_args.get('c_args', []),
cpp_args : test_cpp_args + extra_args.get('cpp_args', []),
link_args : extra_args.get('link_args', []),
dependencies : test_deps + extra_args.get('dependencies', []),
export_dynamic : extra_args.get('export_dynamic', false),

View File

@ -102,8 +102,19 @@ if have_cxx
gobject_tests += {
'cxx' : {
'source' : ['cxx.cpp'],
'suite' : ['cpp'],
},
}
foreach std, arg: cxx_standards
gobject_tests += {
'cxx-@0@'.format(std) : {
'source' : ['cxx.cpp'],
'suite' : ['cpp'],
'cpp_args' : [arg],
},
}
endforeach
endif
if cc.get_id() != 'msvc'
@ -125,6 +136,7 @@ test_env.set('MALLOC_CHECK_', '2')
test_deps = [libm, thread_dep, libglib_dep, libgobject_dep]
test_cargs = ['-DG_LOG_DOMAIN="GLib-GObject"', '-UG_DISABLE_ASSERT']
test_cpp_args = test_cargs
foreach test_name, extra_args : gobject_tests
source = extra_args.get('source', test_name + '.c')
@ -146,6 +158,7 @@ foreach test_name, extra_args : gobject_tests
exe = executable(test_name, source,
c_args : test_cargs + extra_args.get('c_args', []),
cpp_args : test_cpp_args + extra_args.get('cpp_args', []),
dependencies : test_deps + extra_args.get('dependencies', []),
install_dir: installed_tests_execdir,
install_tag: 'tests',

View File

@ -13,6 +13,14 @@ cc = meson.get_compiler('c')
have_cxx = add_languages('cpp', native: false, required: get_option('oss_fuzz').enabled())
if have_cxx
cxx = meson.get_compiler('cpp')
cxx_standards = {}
foreach std : ['98', '03', '11', '14', '17', '20', '2b', 'latest']
arg = (cxx.get_id() == 'msvc' ? '/std:' : '-std=') + 'c++' + std
if cxx.has_argument(arg)
cxx_standards += { std: arg }
endif
endforeach
endif
cc_can_run = meson.can_run_host_binaries()