Add C++ build tests
authorSimon Marchi <simon.marchi@polymtl.ca>
Thu, 29 Jul 2021 02:48:21 +0000 (22:48 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Wed, 8 Sep 2021 19:28:38 +0000 (15:28 -0400)
Some urcu header files cause build failures when included in C++
programs.  Add a test file that includes all exported headers (except
those that are marked as deprecated) and build that test file as a C and
C++ program, with and without _LGPL_SOURCE defined (to test both the
static and non-static implementations).  This helps ensure that the code
in these headers works as both languages.  This alone does not ensure
full coverage, there may be code in unused macros that would need
fixing.  But by including the "static" headers, this already finds a few
issues.

The test doesn't run anything, its purpose is only to verify that things
build.

This catches the following build failures:

 - clang++ doesn't know the __transparent_union__ attribute, place some
   pragmas to ignore this warning around where __transparent_union__ is
   used.

 - CDS_WFS_WOULDBLOCK and CDS_WFS_END are cast to a void*.  This doesn't
   work in C++ when assigning to a field them to typed pointer.  Fix
   them by casting to the appropriate type.

 - The transparent union trick doesn't work in C++:

      CXX      test_build_cxx.o
    In file included from /home/simark/src/urcu/include/urcu/wfstack.h:116,
                     from /home/simark/src/urcu/include/urcu/cds.h:35,
                     from /home/simark/src/urcu/tests/unit/test_build_cxx.cpp:34:
    /home/simark/src/urcu/include/urcu/static/wfstack.h: In function ‘cds_wfs_node* _cds_wfs_pop_with_state_blocking(cds_wfs_stack*, int*)’:
    /home/simark/src/urcu/include/urcu/static/wfstack.h:350:54: error: could not convert ‘s’ from ‘cds_wfs_stack*’ to ‘cds_wfs_stack_ptr_t’
      350 |         retnode = ___cds_wfs_pop_with_state_blocking(s, state);
          |                                                      ^
          |                                                      |
          |                                                      cds_wfs_stack*

   A C++ user can fall back to instantiating a cds_wfs_stack_ptr_t
   explicitly, assigning the right field, and passing the
   cds_wfs_stack_ptr_t to the function.  Fix a few instances in the
   static headers.

   A follow up commit will introduce C++ API wrappers based on
   function overloading to provide a C++ API similar to the C API.

Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Change-Id: I30adc8df69414a0a019c5ec081f64cfac64843f5

include/urcu/compiler.h
include/urcu/lfstack.h
include/urcu/static/lfstack.h
include/urcu/static/rculfqueue.h
include/urcu/static/wfstack.h
include/urcu/wfstack.h
tests/unit/Makefile.am
tests/unit/test_build.c [new file with mode: 0644]
tests/unit/test_build_cxx.cpp [new file with mode: 0644]

index 34eb564bb77795e9e77d21983240c3327b3401d1..157346763078a3ec1d73b7c65618ed1bd98b325f 100644 (file)
                                + __GNUC_PATCHLEVEL__)
 #endif
 
+#ifndef __cplusplus
+#define caa_c_transparent_union        __attribute__((__transparent_union__))
+#else
+#define caa_c_transparent_union
+#endif
+
 #endif /* _URCU_COMPILER_H */
index 5a9bca368775db300cbd1bac44e5fbcd51c6ce47..43ef2a5ccad5b0b90ef38c818d691ccfee0e3476 100644 (file)
@@ -29,6 +29,7 @@ extern "C" {
 
 #include <stdbool.h>
 #include <pthread.h>
+#include <urcu/compiler.h>
 
 /*
  * Lock-free stack.
@@ -83,11 +84,13 @@ struct cds_lfs_stack {
  * The transparent union allows calling functions that work on both
  * struct cds_lfs_stack and struct __cds_lfs_stack on any of those two
  * types.
+ *
+ * Avoid complaints from clang++ not knowing this attribute.
  */
 typedef union {
        struct __cds_lfs_stack *_s;
        struct cds_lfs_stack *s;
-} __attribute__((__transparent_union__)) cds_lfs_stack_ptr_t;
+} caa_c_transparent_union cds_lfs_stack_ptr_t;
 
 #ifdef _LGPL_SOURCE
 
index 6c82b42c0ef9922b2c3c5ee9cdc1cca3feb69b14..a05acb4904d56308caadf3b7cc030ee81ea3ce94 100644 (file)
@@ -289,9 +289,11 @@ struct cds_lfs_node *
 _cds_lfs_pop_blocking(struct cds_lfs_stack *s)
 {
        struct cds_lfs_node *retnode;
+       cds_lfs_stack_ptr_t stack;
 
        _cds_lfs_pop_lock(s);
-       retnode = ___cds_lfs_pop(s);
+       stack.s = s;
+       retnode = ___cds_lfs_pop(stack);
        _cds_lfs_pop_unlock(s);
        return retnode;
 }
@@ -304,9 +306,11 @@ struct cds_lfs_head *
 _cds_lfs_pop_all_blocking(struct cds_lfs_stack *s)
 {
        struct cds_lfs_head *rethead;
+       cds_lfs_stack_ptr_t stack;
 
        _cds_lfs_pop_lock(s);
-       rethead = ___cds_lfs_pop_all(s);
+       stack.s = s;
+       rethead = ___cds_lfs_pop_all(stack);
        _cds_lfs_pop_unlock(s);
        return rethead;
 }
index a8e109138e10d4e06945e941328454aed8627f43..ad73454ed57e5e9f3a4a4c10fb26a69af3bc978f 100644 (file)
@@ -66,7 +66,8 @@ struct cds_lfq_node_rcu *make_dummy(struct cds_lfq_queue_rcu *q,
 {
        struct cds_lfq_node_rcu_dummy *dummy;
 
-       dummy = malloc(sizeof(struct cds_lfq_node_rcu_dummy));
+       dummy = (struct cds_lfq_node_rcu_dummy *)
+               malloc(sizeof(struct cds_lfq_node_rcu_dummy));
        urcu_posix_assert(dummy);
        dummy->parent.next = next;
        dummy->parent.dummy = 1;
index bd7ba688e31c4964d854c9f612b4b0f70377081b..088e6e3acbbe4631a6c7c16136a687c3d21811db 100644 (file)
@@ -37,7 +37,7 @@
 extern "C" {
 #endif
 
-#define CDS_WFS_END                    ((void *) 0x1UL)
+#define CDS_WFS_END                    ((struct cds_wfs_head *) 0x1UL)
 #define CDS_WFS_ADAPT_ATTEMPTS         10      /* Retry if being set */
 #define CDS_WFS_WAIT                   10      /* Wait 10 ms if being set */
 
@@ -345,9 +345,11 @@ struct cds_wfs_node *
 _cds_wfs_pop_with_state_blocking(struct cds_wfs_stack *s, int *state)
 {
        struct cds_wfs_node *retnode;
+       cds_wfs_stack_ptr_t stack;
 
        _cds_wfs_pop_lock(s);
-       retnode = ___cds_wfs_pop_with_state_blocking(s, state);
+       stack.s = s;
+       retnode = ___cds_wfs_pop_with_state_blocking(stack, state);
        _cds_wfs_pop_unlock(s);
        return retnode;
 }
@@ -370,9 +372,11 @@ struct cds_wfs_head *
 _cds_wfs_pop_all_blocking(struct cds_wfs_stack *s)
 {
        struct cds_wfs_head *rethead;
+       cds_wfs_stack_ptr_t stack;
 
        _cds_wfs_pop_lock(s);
-       rethead = ___cds_wfs_pop_all(s);
+       stack.s = s;
+       rethead = ___cds_wfs_pop_all(stack);
        _cds_wfs_pop_unlock(s);
        return rethead;
 }
index 1058fba9baa4dc0fa89b1250e68f5c2814e486e2..016d9f2207df89a37cca8d7ca49847b829c8bf23 100644 (file)
@@ -57,7 +57,7 @@ extern "C" {
  * synchronization.
  */
 
-#define CDS_WFS_WOULDBLOCK     ((void *) -1UL)
+#define CDS_WFS_WOULDBLOCK     ((struct cds_wfs_node *) -1UL)
 
 enum cds_wfs_state {
        CDS_WFS_STATE_LAST =            (1U << 0),
@@ -95,11 +95,13 @@ struct cds_wfs_stack {
  * The transparent union allows calling functions that work on both
  * struct cds_wfs_stack and struct __cds_wfs_stack on any of those two
  * types.
+ *
+ * Avoid complaints from clang++ not knowing this attribute.
  */
 typedef union {
        struct __cds_wfs_stack *_s;
        struct cds_wfs_stack *s;
-} __attribute__((__transparent_union__)) cds_wfs_stack_ptr_t;
+} caa_c_transparent_union cds_wfs_stack_ptr_t;
 
 #ifdef _LGPL_SOURCE
 
index 119dc73fb3a5529ec253b10ee8d2a61143f3bcd4..8e2aa8af84eed6ea858839405238b448a5610c4f 100644 (file)
@@ -16,7 +16,11 @@ noinst_PROGRAMS = \
        test_urcu_multiflavor_single_unit \
        test_urcu_multiflavor_single_unit_cxx \
        test_urcu_multiflavor_single_unit_dynlink \
-       test_urcu_multiflavor_single_unit_dynlink_cxx
+       test_urcu_multiflavor_single_unit_dynlink_cxx \
+       test_build \
+       test_build_cxx \
+       test_build_dynlink \
+       test_build_dynlink_cxx
 
 TESTS = $(noinst_PROGRAMS)
 
@@ -99,6 +103,24 @@ test_urcu_multiflavor_single_unit_dynlink_cxx_CXXFLAGS = -DDYNAMIC_LINK_TEST $(A
 test_urcu_multiflavor_single_unit_dynlink_cxx_LDADD = $(URCU_LIB) $(URCU_MB_LIB) \
        $(URCU_SIGNAL_LIB) $(URCU_QSBR_LIB) $(URCU_BP_LIB) $(TAP_LIB)
 
+test_build_SOURCES = \
+       test_build.c
+test_build_LDADD = $(URCU_COMMON_LIB) $(TAP_LIB)
+
+test_build_cxx_SOURCES = \
+       test_build_cxx.cpp
+test_build_cxx_LDADD = $(URCU_COMMON_LIB) $(TAP_LIB)
+
+test_build_dynlink_SOURCES = \
+       test_build.c
+test_build_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS)
+test_build_dynlink_LDADD = $(URCU_COMMON_LIB) $(TAP_LIB)
+
+test_build_dynlink_cxx_SOURCES = \
+       test_build_cxx.cpp
+test_build_dynlink_cxx_CXXFLAGS = -DDYNAMIC_LINK_TEST $(AM_CXXFLAGS)
+test_build_dynlink_cxx_LDADD = $(URCU_COMMON_LIB) $(TAP_LIB)
+
 all-local:
        @if [ x"$(srcdir)" != x"$(builddir)" ]; then \
                for script in $(SCRIPT_LIST); do \
diff --git a/tests/unit/test_build.c b/tests/unit/test_build.c
new file mode 100644 (file)
index 0000000..deb0a73
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2021 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * This file is meant to verify that headers are compatible with both C and
+ * C++.  It includes all exported headers and is compiled as C and C++ source.
+ */
+
+#ifndef DYNAMIC_LINK_TEST
+# define _LGPL_SOURCE
+#endif
+
+#include <urcu/arch.h>
+#include <urcu/call-rcu.h>
+#include <urcu/cds.h>
+#include <urcu/compiler.h>
+#include <urcu/debug.h>
+#include <urcu/defer.h>
+#include <urcu/flavor.h>
+#include <urcu/futex.h>
+#include <urcu/hlist.h>
+#include <urcu/lfstack.h>
+#include <urcu/list.h>
+#include <urcu/pointer.h>
+#include <urcu/rcuhlist.h>
+#include <urcu/rculfhash.h>
+#include <urcu/rculfqueue.h>
+#include <urcu/rculfstack.h>
+#include <urcu/rculist.h>
+#include <urcu/ref.h>
+#include <urcu/syscall-compat.h>
+#include <urcu/system.h>
+#include <urcu/tls-compat.h>
+#include <urcu/uatomic.h>
+#include <urcu/urcu-bp.h>
+#include <urcu/urcu.h>
+#include <urcu/urcu-mb.h>
+#include <urcu/urcu-memb.h>
+#include <urcu/urcu-qsbr.h>
+#include <urcu/urcu-signal.h>
+#include <urcu/wfcqueue.h>
+#include <urcu/wfqueue.h>
+#include <urcu/wfstack.h>
+
+#include "tap.h"
+
+int main(void)
+{
+       /* Need at least 1 test to make a valid TAP test plan. */
+       plan_tests(1);
+       ok(1, "dummy");
+
+       return exit_status();
+}
diff --git a/tests/unit/test_build_cxx.cpp b/tests/unit/test_build_cxx.cpp
new file mode 100644 (file)
index 0000000..5a45b6a
--- /dev/null
@@ -0,0 +1,3 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "test_build.c"
This page took 0.031573 seconds and 4 git commands to generate.