Rearrange and fixup abstraction layer

- Replace os_result by dds_retcode_t and move DDS return code defines down.
  Eliminates the need to convert between different return code types.

- Move dds_time_t down and remove os_time.
  Eliminates the need to convert between different time representations and
  reduces code duplication.

- Remove use of Microsoft source-code annotation language (SAL).
  SAL annotations are Microsoft specific and not very well documented. This
  makes it very difficult for contributers to write.

- Rearrange the abstraction layer to be feature-based. The previous layout
  falsely assumed that the operating system dictates which implementation is
  best suited. For general purpose operating systems this is mostly true, but
  embedded targets require a slightly different approach and may not even offer
  all features. The new layout makes it possible to mix-and-match feature
  implementations and allows for features to not be implemented at all.

- Replace the os prefix by ddsrt to avoid name collisions.

- Remove various portions of unused and unwanted code.

- Export thread names on all supported platforms.

- Return native thread identifier on POSIX compatible platforms.

- Add timed wait for condition variables that takes an absolute time.

- Remove system abstraction for errno. The os_getErrno and os_setErrno were
  incorrect. Functions that might fail now simply return a DDS return code
  instead.

- Remove thread-specific memory abstraction. os_threadMemGet and accompanying
  functions were a mess and their use has been eliminated by other changes in
  this commit.

- Replace attribute (re)defines by ddsrt_ prefixed equivalents to avoid name
  collisions and problems with faulty __nonnull__ attributes.

Signed-off-by: Jeroen Koekkoek <jeroen@koekkoek.nl>
This commit is contained in:
Jeroen Koekkoek 2019-01-18 14:10:19 +01:00
parent 318968f40f
commit cd6742ee12
439 changed files with 22117 additions and 28782 deletions

View file

@ -0,0 +1,43 @@
#
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
# v. 1.0 which is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#
include(CUnit)
set(sources
"atomics.c"
"environ.c"
"heap.c"
"ifaddrs.c"
"sync.c"
"strtoll.c"
"thread.c"
"thread_cleanup.c"
"string.c"
"log.c"
"strlcpy.c"
"socket.c"
"select.c")
add_cunit_executable(cunit_ddsrt ${sources})
target_link_libraries(cunit_ddsrt PRIVATE ddsrt)
# Create a dummy export header. generate_export_header can only be used with
# library targets, but since the targets are linked statically,
# __declspec(dllimport) is not required anyway.
set(export_dir "${CMAKE_CURRENT_BINARY_DIR}/include/dds")
set(export_header "${export_dir}/export.h")
if(NOT EXISTS "${export_header}")
file(MAKE_DIRECTORY "${export_dir}")
file(WRITE "${export_header}" "#define DDS_EXPORT\n")
endif()
target_include_directories(
cunit_ddsrt PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")

393
src/ddsrt/tests/atomics.c Normal file
View file

@ -0,0 +1,393 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include "CUnit/Test.h"
#include "dds/ddsrt/atomics.h"
uint32_t _osuint32 = 0;
uint64_t _osuint64 = 0;
uintptr_t _osaddress = 0;
ptrdiff_t _ptrdiff = 0;
void * _osvoidp = (uintptr_t *)0;
CU_Test(ddsrt_atomics, load_store)
{
volatile ddsrt_atomic_uint32_t uint32 = DDSRT_ATOMIC_UINT32_INIT(5);
#if DDSRT_HAVE_ATOMIC64
volatile ddsrt_atomic_uint64_t uint64 = DDSRT_ATOMIC_UINT64_INIT(5);
#endif
volatile ddsrt_atomic_uintptr_t uintptr = DDSRT_ATOMIC_UINTPTR_INIT(5);
volatile ddsrt_atomic_voidp_t voidp = DDSRT_ATOMIC_VOIDP_INIT((uintptr_t)5);
/* Test uint32 LD-ST */
CU_ASSERT (ddsrt_atomic_ld32 (&uint32) == 5); /* Returns contents of uint32 */
ddsrt_atomic_st32 (&uint32, _osuint32); /* Writes os_uint32 into uint32 */
CU_ASSERT (ddsrt_atomic_ld32 (&uint32) == _osuint32);
/* Test uint64 LD-ST */
#if DDSRT_HAVE_ATOMIC64
CU_ASSERT (ddsrt_atomic_ld64 (&uint64) == 5);
ddsrt_atomic_st64 (&uint64, _osuint64);
CU_ASSERT (ddsrt_atomic_ld64 (&uint64) == _osuint64);
#endif
/* Test uintptr LD-ST */
CU_ASSERT (ddsrt_atomic_ldptr (&uintptr) == 5);
ddsrt_atomic_stptr (&uintptr, _osaddress);
CU_ASSERT (ddsrt_atomic_ldptr (&uintptr) == _osaddress);
/* Test uintvoidp LD-ST */
CU_ASSERT (ddsrt_atomic_ldvoidp (&voidp) == (uintptr_t*)5);
ddsrt_atomic_stvoidp (&voidp, _osvoidp);
CU_ASSERT (ddsrt_atomic_ldvoidp (&voidp) == (uintptr_t*)_osvoidp);
}
CU_Test(ddsrt_atomics, compare_and_swap)
{
/* Compare and Swap if (ptr == expected) { ptr = newval; } */
volatile ddsrt_atomic_uint32_t uint32 = DDSRT_ATOMIC_UINT32_INIT(0);
#if DDSRT_HAVE_ATOMIC64
volatile ddsrt_atomic_uint64_t uint64 = DDSRT_ATOMIC_UINT64_INIT(0);
#endif
volatile ddsrt_atomic_uintptr_t uintptr = DDSRT_ATOMIC_UINTPTR_INIT(0);
volatile ddsrt_atomic_voidp_t uintvoidp = DDSRT_ATOMIC_VOIDP_INIT((uintptr_t)0);
_osuint32 = 1;
_osuint64 = 1;
_osaddress = 1;
_osvoidp = (uintptr_t *)1;
uint32_t expected = 0, newval = 5;
uintptr_t addr_expected = 0, addr_newval = 5;
void *void_expected = (uintptr_t*)0;
void *void_newval = (uintptr_t*)5;
int ret = 0;
/* Test ddsrt_atomic_cas32 */
ret = ddsrt_atomic_cas32 (&uint32, expected, newval);
CU_ASSERT (ddsrt_atomic_ld32 (&uint32) == newval && ret == 1);
ddsrt_atomic_st32 (&uint32, _osuint32);
ret = ddsrt_atomic_cas32 (&uint32, expected, newval);
CU_ASSERT (ddsrt_atomic_ld32 (&uint32) != newval && ret == 0);
/* Test ddsrt_atomic_cas64 */
#if DDSRT_HAVE_ATOMIC64
ret = ddsrt_atomic_cas64 (&uint64, expected, newval);
CU_ASSERT (ddsrt_atomic_ld64 (&uint64) == newval && ret == 1);
ddsrt_atomic_st64 (&uint64, _osuint64);
ret = ddsrt_atomic_cas64 (&uint64, expected, newval);
CU_ASSERT (ddsrt_atomic_ld64 (&uint64) != newval && ret == 0);
#endif
/* Test ddsrt_atomic_casptr */
ret = ddsrt_atomic_casptr (&uintptr, addr_expected, addr_newval);
CU_ASSERT (ddsrt_atomic_ldptr (&uintptr) == addr_newval && ret == 1);
ddsrt_atomic_stptr (&uintptr, _osaddress);
ret = ddsrt_atomic_casptr (&uintptr, addr_expected, addr_newval);
CU_ASSERT (ddsrt_atomic_ldptr (&uintptr) != addr_newval && ret == 0);
/* Test ddsrt_atomic_casvoidp */
ret = ddsrt_atomic_casvoidp (&uintvoidp, void_expected, void_newval);
CU_ASSERT (ddsrt_atomic_ldvoidp (&uintvoidp) == (uintptr_t*)void_newval && ret == 1);
ddsrt_atomic_stvoidp (&uintvoidp, _osvoidp);
ret = ddsrt_atomic_casvoidp (&uintvoidp, void_expected, void_newval);
CU_ASSERT (ddsrt_atomic_ldvoidp (&uintvoidp) == (uintptr_t*)1 && ret == 0);
}
CU_Test(ddsrt_atomics, increment)
{
volatile ddsrt_atomic_uint32_t uint32 = DDSRT_ATOMIC_UINT32_INIT(0);
#if DDSRT_HAVE_ATOMIC64
volatile ddsrt_atomic_uint64_t uint64 = DDSRT_ATOMIC_UINT64_INIT(0);
#endif
volatile ddsrt_atomic_uintptr_t uintptr = DDSRT_ATOMIC_UINTPTR_INIT(0);
_osuint32 = 0;
_osuint64 = 0;
_osaddress = 0;
_osvoidp = (uintptr_t *)0;
/* Test os_inc32 */
ddsrt_atomic_inc32 (&uint32);
CU_ASSERT (ddsrt_atomic_ld32 (&uint32) == 1);
/* Test os_inc64 */
#if DDSRT_HAVE_ATOMIC64
ddsrt_atomic_inc64 (&uint64);
CU_ASSERT (ddsrt_atomic_ld64 (&uint64) == 1);
#endif
/* Test os_incptr */
ddsrt_atomic_incptr (&uintptr);
CU_ASSERT (ddsrt_atomic_ldptr (&uintptr) == 1);
/* Test ddsrt_atomic_inc32_nv */
ddsrt_atomic_st32 (&uint32, _osuint32);
CU_ASSERT (ddsrt_atomic_inc32_nv (&uint32) == 1);
/* Test ddsrt_atomic_inc64_nv */
#if DDSRT_HAVE_ATOMIC64
ddsrt_atomic_st64 (&uint64, _osuint64);
CU_ASSERT (ddsrt_atomic_inc64_nv (&uint64) == 1);
#endif
/* Test ddsrt_atomic_incptr_nv */
ddsrt_atomic_stptr (&uintptr, _osaddress);
CU_ASSERT (ddsrt_atomic_incptr_nv(&uintptr) == 1);
}
CU_Test(ddsrt_atomics, decrement)
{
volatile ddsrt_atomic_uint32_t uint32 = DDSRT_ATOMIC_UINT32_INIT(1);
#if DDSRT_HAVE_ATOMIC64
volatile ddsrt_atomic_uint64_t uint64 = DDSRT_ATOMIC_UINT64_INIT(1);
#endif
volatile ddsrt_atomic_uintptr_t uintptr = DDSRT_ATOMIC_UINTPTR_INIT(1);
_osuint32 = 1;
_osuint64 = 1;
_osaddress = 1;
_osvoidp = (uintptr_t *)1;
/* Test ddsrt_atomic_dec32 */
ddsrt_atomic_dec32 (&uint32);
CU_ASSERT (ddsrt_atomic_ld32 (&uint32) == 0);
/* Test ddsrt_atomic_dec64 */
#if DDSRT_HAVE_ATOMIC64
ddsrt_atomic_dec64 (&uint64);
CU_ASSERT (ddsrt_atomic_ld64 (&uint64) == 0);
#endif
/* Test ddsrt_atomic_decptr */
ddsrt_atomic_decptr (&uintptr);
CU_ASSERT (ddsrt_atomic_ldptr (&uintptr) == 0);
/* Test ddsrt_atomic_dec32_nv */
ddsrt_atomic_st32 (&uint32, _osuint32);
CU_ASSERT (ddsrt_atomic_dec32_nv (&uint32) == 0);
/* Test ddsrt_atomic_dec64_nv */
#if DDSRT_HAVE_ATOMIC64
ddsrt_atomic_st64 (&uint64, _osuint64);
CU_ASSERT (ddsrt_atomic_dec64_nv (&uint64) == 0);
#endif
/* Test ddsrt_atomic_decptr_nv */
ddsrt_atomic_stptr (&uintptr, _osaddress);
CU_ASSERT (ddsrt_atomic_decptr_nv(&uintptr) == 0);
}
CU_Test(ddsrt_atomics, add)
{
volatile ddsrt_atomic_uint32_t uint32 = DDSRT_ATOMIC_UINT32_INIT(1);
#if DDSRT_HAVE_ATOMIC64
volatile ddsrt_atomic_uint64_t uint64 = DDSRT_ATOMIC_UINT64_INIT(1);
#endif
volatile ddsrt_atomic_uintptr_t uintptr = DDSRT_ATOMIC_UINTPTR_INIT(1);
volatile ddsrt_atomic_voidp_t uintvoidp = DDSRT_ATOMIC_VOIDP_INIT((uintptr_t)1);
_osuint32 = 2;
_osuint64 = 2;
_osaddress = 2;
_ptrdiff = 2;
/* Test ddsrt_atomic_add32 */
ddsrt_atomic_add32 (&uint32, _osuint32);
CU_ASSERT (ddsrt_atomic_ld32 (&uint32) == 3);
/* Test ddsrt_atomic_add64 */
#if DDSRT_HAVE_ATOMIC64
ddsrt_atomic_add64 (&uint64, _osuint64);
CU_ASSERT (ddsrt_atomic_ld64 (&uint64) == 3);
#endif
/* Test ddsrt_atomic_addptr */
ddsrt_atomic_addptr (&uintptr, _osaddress);
CU_ASSERT (ddsrt_atomic_ldptr (&uintptr) == 3);
/* Test ddsrt_atomic_addvoidp */
ddsrt_atomic_addvoidp (&uintvoidp, _ptrdiff);
CU_ASSERT (ddsrt_atomic_ldvoidp (&uintvoidp) == (uintptr_t*)3);
/* Test ddsrt_atomic_add32_nv */
ddsrt_atomic_st32 (&uint32, 1);
CU_ASSERT (ddsrt_atomic_add32_nv (&uint32, _osuint32) == 3);
/* Test ddsrt_atomic_add64_nv */
#if DDSRT_HAVE_ATOMIC64
ddsrt_atomic_st64 (&uint64, 1);
CU_ASSERT (ddsrt_atomic_add64_nv (&uint64, _osuint64) == 3);
#endif
/* Test ddsrt_atomic_addptr_nv */
ddsrt_atomic_stptr (&uintptr, 1);
CU_ASSERT (ddsrt_atomic_addptr_nv (&uintptr, _osaddress) == 3);
/* Test ddsrt_atomic_addvoidp_nv */
ddsrt_atomic_stvoidp (&uintvoidp, (uintptr_t*)1);
CU_ASSERT (ddsrt_atomic_addvoidp_nv (&uintvoidp, _ptrdiff) == (uintptr_t*)3);
}
CU_Test(ddsrt_atomics, subtract)
{
volatile ddsrt_atomic_uint32_t uint32 = DDSRT_ATOMIC_UINT32_INIT(5);
#if DDSRT_HAVE_ATOMIC64
volatile ddsrt_atomic_uint64_t uint64 = DDSRT_ATOMIC_UINT64_INIT(5);
#endif
volatile ddsrt_atomic_uintptr_t uintptr = DDSRT_ATOMIC_UINTPTR_INIT(5);
volatile ddsrt_atomic_voidp_t uintvoidp = DDSRT_ATOMIC_VOIDP_INIT((uintptr_t)5);
_osuint32 = 2;
_osuint64 = 2;
_osaddress = 2;
_ptrdiff = 2;
/* Test ddsrt_atomic_sub32 */
ddsrt_atomic_sub32 (&uint32, _osuint32);
CU_ASSERT (ddsrt_atomic_ld32 (&uint32) == 3);
/* Test ddsrt_atomic_sub64 */
#if DDSRT_HAVE_ATOMIC64
ddsrt_atomic_sub64 (&uint64, _osuint64);
CU_ASSERT (ddsrt_atomic_ld64 (&uint64) == 3);
#endif
/* Test ddsrt_atomic_subptr */
ddsrt_atomic_subptr (&uintptr, _osaddress);
CU_ASSERT (ddsrt_atomic_ldptr (&uintptr) == 3);
/* Test ddsrt_atomic_subvoidp */
ddsrt_atomic_subvoidp (&uintvoidp, _ptrdiff);
CU_ASSERT (ddsrt_atomic_ldvoidp (&uintvoidp) == (uintptr_t*)3);
/* Test ddsrt_atomic_sub32_nv */
ddsrt_atomic_st32 (&uint32, 5);
CU_ASSERT (ddsrt_atomic_sub32_nv (&uint32, _osuint32) == 3);
/* Test ddsrt_atomic_sub64_nv */
#if DDSRT_HAVE_ATOMIC64
ddsrt_atomic_st64 (&uint64, 5);
CU_ASSERT (ddsrt_atomic_sub64_nv (&uint64, _osuint64) == 3);
#endif
/* Test ddsrt_atomic_subptr_nv */
ddsrt_atomic_stptr (&uintptr, 5);
CU_ASSERT (ddsrt_atomic_subptr_nv (&uintptr, _osaddress) == 3);
/* Test ddsrt_atomic_subvoidp_nv */
ddsrt_atomic_stvoidp (&uintvoidp, (uintptr_t*)5);
CU_ASSERT (ddsrt_atomic_subvoidp_nv (&uintvoidp, _ptrdiff) == (void *)3);
}
CU_Test(ddsrt_atomics, and)
{
/* AND Operation:
150 010010110
500 111110100
148 010010100 */
volatile ddsrt_atomic_uint32_t uint32 = DDSRT_ATOMIC_UINT32_INIT(150);
#if DDSRT_HAVE_ATOMIC64
volatile ddsrt_atomic_uint64_t uint64 = DDSRT_ATOMIC_UINT64_INIT(150);
#endif
volatile ddsrt_atomic_uintptr_t uintptr = DDSRT_ATOMIC_UINTPTR_INIT(150);
_osuint32 = 500;
_osuint64 = 500;
_osaddress = 500;
/* Test ddsrt_atomic_and32 */
ddsrt_atomic_and32 (&uint32, _osuint32);
CU_ASSERT (ddsrt_atomic_ld32 (&uint32) == 148);
/* Test ddsrt_atomic_and64 */
#if DDSRT_HAVE_ATOMIC64
ddsrt_atomic_and64 (&uint64, _osuint64);
CU_ASSERT (ddsrt_atomic_ld64 (&uint64) == 148);
#endif
/* Test ddsrt_atomic_andptr */
ddsrt_atomic_andptr (&uintptr, _osaddress);
CU_ASSERT (ddsrt_atomic_ldptr (&uintptr) == 148);
/* Test ddsrt_atomic_and32_ov */
CU_ASSERT (ddsrt_atomic_and32_ov (&uint32, _osuint32) == 148);
/* Test ddsrt_atomic_and64_ov */
#if DDSRT_HAVE_ATOMIC64
CU_ASSERT (ddsrt_atomic_and64_ov (&uint64, _osuint64) == 148);
#endif
/* Test ddsrt_atomic_andptr_ov */
CU_ASSERT (ddsrt_atomic_andptr_ov (&uintptr, _osaddress) == 148);
/* Test ddsrt_atomic_and32_nv */
CU_ASSERT (ddsrt_atomic_and32_nv (&uint32, _osuint32) == 148);
/* Test ddsrt_atomic_and64_nv */
#if DDSRT_HAVE_ATOMIC64
CU_ASSERT (ddsrt_atomic_and64_nv (&uint64, _osuint64) == 148);
#endif
/* Test ddsrt_atomic_andptr_nv */
CU_ASSERT (ddsrt_atomic_andptr_nv (&uintptr, _osaddress) == 148);
}
CU_Test(ddsrt_atomics, or)
{
/* OR Operation:
150 010010110
500 111110100
502 111110110 */
volatile ddsrt_atomic_uint32_t uint32 = DDSRT_ATOMIC_UINT32_INIT(150);
#if DDSRT_HAVE_ATOMIC64
volatile ddsrt_atomic_uint64_t uint64 = DDSRT_ATOMIC_UINT64_INIT(150);
#endif
volatile ddsrt_atomic_uintptr_t uintptr = DDSRT_ATOMIC_UINTPTR_INIT(150);
_osuint32 = 500;
_osuint64 = 500;
_osaddress = 500;
/* Test ddsrt_atomic_or32 */
ddsrt_atomic_or32 (&uint32, _osuint32);
CU_ASSERT (ddsrt_atomic_ld32 (&uint32) == 502);
/* Test ddsrt_atomic_or64 */
#if DDSRT_HAVE_ATOMIC64
ddsrt_atomic_or64 (&uint64, _osuint64);
CU_ASSERT (ddsrt_atomic_ld64 (&uint64) == 502);
#endif
/* Test ddsrt_atomic_orptr */
ddsrt_atomic_orptr (&uintptr, _osaddress);
CU_ASSERT (ddsrt_atomic_ldptr (&uintptr) == 502);
/* Test ddsrt_atomic_or32_ov */
CU_ASSERT (ddsrt_atomic_or32_ov (&uint32, _osuint32) == 502);
/* Test ddsrt_atomic_or64_ov */
#if DDSRT_HAVE_ATOMIC64
CU_ASSERT (ddsrt_atomic_or64_ov (&uint64, _osuint64) == 502);
#endif
/* Test ddsrt_atomic_orptr_ov */
CU_ASSERT (ddsrt_atomic_orptr_ov (&uintptr, _osaddress) == 502);
/* Test ddsrt_atomic_or32_nv */
CU_ASSERT (ddsrt_atomic_or32_nv (&uint32, _osuint32) == 502);
/* Test ddsrt_atomic_or64_nv */
#if DDSRT_HAVE_ATOMIC64
CU_ASSERT (ddsrt_atomic_or64_nv (&uint64, _osuint64) == 502);
#endif
/* Test ddsrt_atomic_orptr_nv */
CU_ASSERT (ddsrt_atomic_orptr_nv (&uintptr, _osaddress) == 502);
}

98
src/ddsrt/tests/environ.c Normal file
View file

@ -0,0 +1,98 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stdlib.h>
#include "CUnit/Theory.h"
#include "dds/ddsrt/environ.h"
#include "dds/ddsrt/misc.h"
CU_TheoryDataPoints(ddsrt_environ, bad_name) = {
CU_DataPoints(const char *, "", "foo=")
};
CU_Theory((const char *name), ddsrt_environ, bad_name)
{
dds_retcode_t rc;
static const char value[] = "bar";
static char dummy[] = "foobar";
char *ptr;
rc = ddsrt_setenv(name, value);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_BAD_PARAMETER);
rc = ddsrt_unsetenv(name);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_BAD_PARAMETER);
ptr = dummy;
rc = ddsrt_getenv(name, &ptr);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_BAD_PARAMETER);
CU_ASSERT_PTR_EQUAL(ptr, dummy);
}
DDSRT_WARNING_MSVC_OFF(4996)
CU_Test(ddsrt_environ, setenv)
{
dds_retcode_t rc;
static const char name[] = "foo";
static char value[] = "bar";
char *ptr;
rc = ddsrt_setenv(name, value);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
ptr = getenv(name);
CU_ASSERT_PTR_NOT_NULL(ptr);
CU_ASSERT_STRING_EQUAL(ptr, "bar");
/* Ensure value is copied into the environment. */
value[2] = 'z';
ptr = getenv(name);
CU_ASSERT_PTR_NOT_NULL(ptr);
CU_ASSERT_STRING_EQUAL(ptr, "bar");
rc = ddsrt_setenv(name, "");
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
ptr = getenv(name);
CU_ASSERT_PTR_NULL(ptr);
}
DDSRT_WARNING_MSVC_ON(4996)
CU_Test(ddsrt_environ, getenv)
{
dds_retcode_t rc;
static const char name[] = "foo";
static const char value[] = "bar";
static char dummy[] = "foobar";
char *ptr;
/* Ensure "not found" is returned. */
rc = ddsrt_unsetenv(name);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
ptr = dummy;
rc = ddsrt_getenv(name, &ptr);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_NOT_FOUND);
CU_ASSERT_PTR_EQUAL(ptr, dummy);
/* Ensure "ok" is returned and value is what it should be. */
rc = ddsrt_setenv(name, value);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
ptr = dummy;
rc = ddsrt_getenv(name, &ptr);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
CU_ASSERT_PTR_NOT_EQUAL(ptr, dummy);
CU_ASSERT_PTR_NOT_EQUAL(ptr, NULL);
if (ptr != NULL) {
CU_ASSERT_STRING_EQUAL(ptr, "bar");
}
/* Ensure environement is as it was. */
rc = ddsrt_unsetenv(name);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
}

156
src/ddsrt/tests/heap.c Normal file
View file

@ -0,0 +1,156 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stdio.h>
#include "CUnit/Test.h"
#include "dds/ddsrt/cdtors.h"
#include "dds/ddsrt/heap.h"
CU_Init(ddsrt_heap)
{
ddsrt_init();
return 0;
}
CU_Clean(ddsrt_heap)
{
ddsrt_fini();
return 0;
}
static const size_t allocsizes[] = {0, 1, 2, 3, 4, 5, 10, 20, 257, 1024};
static const size_t nof_allocsizes = sizeof allocsizes / sizeof *allocsizes;
CU_Test(ddsrt_heap, malloc)
{
for(size_t i = 0; i < nof_allocsizes; i++) {
for(size_t j = 0; j < nof_allocsizes; j++) {
size_t s = allocsizes[i] * allocsizes[j]; /* Allocates up to 1MB */
void *ptr = ddsrt_malloc(s);
CU_ASSERT_PTR_NOT_EQUAL(ptr, NULL); /* ddsrt_malloc is supposed to abort on failure */
memset(ptr, 0, s); /* This potentially segfaults if the actual allocated block is too small */
ddsrt_free(ptr);
}
}
CU_PASS("ddsrt_malloc");
}
CU_Test(ddsrt_heap, calloc)
{
for(size_t i = 0; i < nof_allocsizes; i++) {
for(size_t j = 0; j < nof_allocsizes; j++) {
char *ptr = ddsrt_calloc(allocsizes[i], allocsizes[j]);
CU_ASSERT_PTR_NOT_EQUAL(ptr, NULL); /* ddsrt_calloc is supposed to abort on failure */
if(allocsizes[i] * allocsizes[j] > 0) {
CU_ASSERT (ptr[0] == 0 && !memcmp(ptr, ptr + 1, (allocsizes[i] * allocsizes[j]) - 1)); /* ddsrt_calloc should memset properly */
}
ddsrt_free(ptr);
}
}
CU_PASS("ddsrt_calloc");
}
CU_Test(ddsrt_heap, realloc)
{
char *ptr = NULL;
size_t unchanged, s, prevs = 0;
for(size_t i = 0; i < nof_allocsizes; i++) {
for(size_t j = 0; j < nof_allocsizes; j++) {
s = allocsizes[i] * allocsizes[j]; /* Allocates up to 1MB */
printf("ddsrt_realloc(%p) %zu -> %zu\n", ptr, prevs, s);
ptr = ddsrt_realloc(ptr, s);
CU_ASSERT_PTR_NOT_EQUAL(ptr, NULL); /* ddsrt_realloc is supposed to abort on failure */
unchanged = (prevs < s) ? prevs : s;
if(unchanged) {
CU_ASSERT (ptr[0] == 1 && !memcmp(ptr, ptr + 1, unchanged - 1)); /* ddsrt_realloc shouldn't change memory */
}
memset(ptr, 1, s); /* This potentially segfaults if the actual allocated block is too small */
prevs = s;
}
}
ddsrt_free(ptr);
CU_PASS("ddsrt_realloc");
}
static const size_t allocsizes_s[] = {0, 1, 2, 3, 4, 5, 10, 20, 257, 1024, 8192};
static const size_t nof_allocsizes_s = sizeof allocsizes_s / sizeof *allocsizes_s;
CU_Test(ddsrt_heap, malloc_s)
{
for(size_t i = 0; i < nof_allocsizes_s; i++) {
for(size_t j = 0; j < nof_allocsizes_s; j++) {
size_t s = allocsizes_s[i] * allocsizes_s[j]; /* Allocates up to 8MB */
void *ptr = ddsrt_malloc_s(s); /* If s == 0, ddsrt_malloc_s should still return a pointer */
if(ptr) {
memset(ptr, 0, s); /* This potentially segfaults if the actual allocated block is too small */
} else if (s <= 16) {
/* Failure to allocate can't be considered a test fault really,
* except that a malloc(<=16) would fail is unlikely. */
CU_FAIL("ddsrt_malloc_s(<=16) returned NULL");
}
ddsrt_free(ptr);
}
}
CU_PASS("ddsrt_malloc_s");
}
CU_Test(ddsrt_heap, calloc_s)
{
for(size_t i = 0; i < nof_allocsizes_s; i++) {
for(size_t j = 0; j < nof_allocsizes_s; j++) {
size_t s = allocsizes_s[i] * allocsizes_s[j];
char *ptr = ddsrt_calloc_s(allocsizes_s[i], allocsizes_s[j]); /* If either one is 0, ddsrt_calloc_s should still return a pointer */
if(ptr) {
if(s) {
CU_ASSERT (ptr[0] == 0 && !memcmp(ptr, ptr + 1, s - 1)); /* malloc_0_s should memset properly */
}
} else if (s <= 16) {
/* Failure to allocate can't be considered a test fault really,
* except that a calloc(<=16) would fail is unlikely. */
CU_FAIL("ddsrt_calloc_s(<=16) returned NULL");
}
ddsrt_free(ptr);
}
}
CU_PASS("ddsrt_calloc_s");
}
CU_Test(ddsrt_heap, ddsrt_realloc_s)
{
char *newptr, *ptr = NULL;
size_t unchanged, s, prevs = 0;
for(size_t i = 0; i < nof_allocsizes_s; i++) {
for(size_t j = 0; j < nof_allocsizes_s; j++) {
s = allocsizes_s[i] * allocsizes_s[j]; /* Allocates up to 8MB */
newptr = ddsrt_realloc_s(ptr, s);
printf("%p = ddsrt_realloc_s(%p) %zu -> %zu\n", newptr, ptr, prevs, s);
if (s <= 16) {
/* Failure to allocate can't be considered a test fault really,
* except that a ddsrt_realloc_s(0 < s <=16) would fail is unlikely. */
CU_ASSERT_PTR_NOT_EQUAL(newptr, NULL);
}
if(newptr){
unchanged = (prevs < s) ? prevs : s;
if(unchanged) {
CU_ASSERT (newptr[0] == 1 && !memcmp(newptr, newptr + 1, unchanged - 1)); /* ddsrt_realloc_s shouldn't change memory */
}
memset(newptr, 1, s); /* This potentially segfaults if the actual allocated block is too small */
}
prevs = s;
ptr = newptr;
}
}
ddsrt_free(ptr);
CU_PASS("ddsrt_realloc_s");
}

187
src/ddsrt/tests/ifaddrs.c Normal file
View file

@ -0,0 +1,187 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include "CUnit/Test.h"
#include "dds/ddsrt/cdtors.h"
#include "dds/ddsrt/ifaddrs.h"
#include "dds/ddsrt/retcode.h"
/* FIXME: It's not possible to predict what network interfaces are available
on a given host. To properly test all combinations the abstracted
operating system functions must be mocked. */
/* FIXME: It's possible that IPv6 is available in the network stack, but
disabled in the kernel. Travis CI for example has build environments
that do not have IPv6 enabled. */
#ifdef DDSRT_HAVE_IPV6
static int ipv6_enabled = 1;
#endif
CU_Init(ddsrt_getifaddrs)
{
ddsrt_init();
#ifdef DDSRT_HAVE_IPV6
#ifdef __linux
FILE *fh;
const char *const *path;
static const char *const paths[] = {
"/proc/sys/net/ipv6/conf/all/disable_ipv6",
"/proc/sys/net/ipv6/conf/default/disable_ipv6",
NULL
};
for (path = paths; ipv6_enabled == 1 && *path != NULL; path++) {
if ((fh = fopen(*path, "r")) != NULL) {
ipv6_enabled = (fgetc(fh) == '0');
fclose(fh);
fh = NULL;
}
}
#endif /* __linux */
#endif /* DDSRT_HAVE_IPV6 */
return 0;
}
CU_Clean(ddsrt_getifaddrs)
{
ddsrt_fini();
return 0;
}
/* Assume every test machine has at least one IPv4 enabled interface. This
simple test verifies an interface can at least be found and that the
IFF_LOOPBACK flags are properly set. */
CU_Test(ddsrt_getifaddrs, ipv4)
{
dds_retcode_t ret;
int seen = 0;
ddsrt_ifaddrs_t *ifa_root, *ifa;
const int afs[] = { AF_INET, DDSRT_AF_TERM };
ret = ddsrt_getifaddrs(&ifa_root, afs);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
for (ifa = ifa_root; ifa; ifa = ifa->next) {
CU_ASSERT_PTR_NOT_EQUAL_FATAL(ifa->addr, NULL);
CU_ASSERT_EQUAL(ifa->addr->sa_family, AF_INET);
if (ifa->addr->sa_family == AF_INET) {
if (ifa->flags & IFF_LOOPBACK) {
CU_ASSERT(ddsrt_sockaddr_isloopback(ifa->addr));
} else {
CU_ASSERT(!ddsrt_sockaddr_isloopback(ifa->addr));
}
seen = 1;
}
}
CU_ASSERT_EQUAL(seen, 1);
ddsrt_freeifaddrs(ifa_root);
}
CU_Test(ddsrt_getifaddrs, null_filter)
{
dds_retcode_t ret;
int cnt = 0;
ddsrt_ifaddrs_t *ifa_root, *ifa;
ret = ddsrt_getifaddrs(&ifa_root, NULL);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
for (ifa = ifa_root; ifa; ifa = ifa->next) {
CU_ASSERT_PTR_NOT_EQUAL_FATAL(ifa->addr, NULL);
cnt++;
}
CU_ASSERT(cnt > 0);
ddsrt_freeifaddrs(ifa_root);
}
CU_Test(ddsrt_getifaddrs, empty_filter)
{
dds_retcode_t ret;
ddsrt_ifaddrs_t *ifa_root;
const int afs[] = { DDSRT_AF_TERM };
ret = ddsrt_getifaddrs(&ifa_root, afs);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
CU_ASSERT_PTR_EQUAL(ifa_root, NULL);
ddsrt_freeifaddrs(ifa_root);
}
#ifdef DDSRT_HAVE_IPV6
CU_Test(ddsrt_getifaddrs, ipv6)
{
if (ipv6_enabled == 1) {
dds_retcode_t ret;
int have_ipv6 = 0;
ddsrt_ifaddrs_t *ifa_root, *ifa;
const int afs[] = { AF_INET6, DDSRT_AF_TERM };
ret = ddsrt_getifaddrs(&ifa_root, afs);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
for (ifa = ifa_root; ifa; ifa = ifa->next) {
CU_ASSERT_PTR_NOT_EQUAL_FATAL(ifa->addr, NULL);
CU_ASSERT_EQUAL(ifa->addr->sa_family, AF_INET6);
if (ifa->addr->sa_family == AF_INET6) {
have_ipv6 = 1;
/* macOS assigns a link-local address to the loopback interface, so
the loopback address must be assigned to the loopback interface,
but the loopback interface can have addresses other than the
loopback address assigned. */
if (ddsrt_sockaddr_isloopback(ifa->addr)) {
CU_ASSERT(ifa->flags & IFF_LOOPBACK);
}
}
}
CU_ASSERT_EQUAL(have_ipv6, 1);
ddsrt_freeifaddrs(ifa_root);
CU_PASS("IPv6 enabled in test environment");
} else {
CU_PASS("IPv6 disabled in test environment");
}
}
/* Assume at least one IPv4 and one IPv6 interface are available when IPv6 is
available on the platform. */
CU_Test(ddsrt_getifaddrs, ipv4_n_ipv6)
{
if (ipv6_enabled == 1) {
dds_retcode_t ret;
int have_ipv4 = 0;
int have_ipv6 = 0;
ddsrt_ifaddrs_t *ifa_root, *ifa;
const int afs[] = { AF_INET, AF_INET6, DDSRT_AF_TERM };
ret = ddsrt_getifaddrs(&ifa_root, afs);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
for (ifa = ifa_root; ifa; ifa = ifa->next) {
CU_ASSERT_PTR_NOT_EQUAL_FATAL(ifa->addr, NULL);
CU_ASSERT(ifa->addr->sa_family == AF_INET ||
ifa->addr->sa_family == AF_INET6);
if (ifa->addr->sa_family == AF_INET) {
have_ipv4 = 1;
} else if (ifa->addr->sa_family == AF_INET6) {
have_ipv6 = 1;
}
}
CU_ASSERT_EQUAL(have_ipv4, 1);
CU_ASSERT_EQUAL(have_ipv6, 1);
ddsrt_freeifaddrs(ifa_root);
CU_PASS("IPv6 enabled in test environment");
} else {
CU_PASS("IPv6 disabled in test environment");
}
}
#endif /* DDSRT_HAVE_IPV6 */

366
src/ddsrt/tests/log.c Normal file
View file

@ -0,0 +1,366 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#ifdef __APPLE__
#include <pthread.h>
#endif /* __APPLE__ */
#include "CUnit/Test.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/log.h"
#include "dds/ddsrt/misc.h"
#include "dds/ddsrt/string.h"
#include "dds/ddsrt/sync.h"
#include "dds/ddsrt/threads.h"
#include "dds/ddsrt/time.h"
static FILE *fh = NULL;
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
/* Windows does not support opening a stream to a buffer like fmemopen on
* Linux does. A temporary file that will never be flushed to disk is created
* instead. See the link below for more detail.
*
* https://blogs.msdn.microsoft.com/larryosterman/2004/04/19/its-only-temporary/
*/
FILE *fmemopen(void *buf, size_t size, const char *mode)
{
int err = 0;
int fd = -1;
DWORD ret;
FILE *fh = NULL;
HANDLE hdl = INVALID_HANDLE_VALUE;
/* GetTempFileName will fail if the directory is be longer than MAX_PATH-14
characters */
char tmpdir[(MAX_PATH + 1) - 14];
char tmpfile[MAX_PATH + 1];
static const int max = 1000;
static const char pfx[] = "cyclone"; /* Up to first three are used. */
(void)buf;
(void)size;
ret = GetTempPath(sizeof(tmpdir), tmpdir);
if (ret == 0) {
err = GetLastError();
} else if (ret > sizeof(tmpdir)) {
err = ENOMEM;
}
if (GetTempFileName(tmpdir, pfx, 0, tmpfile) == 0) {
err = GetLastError();
assert(err != ERROR_BUFFER_OVERFLOW);
} else {
/* The combination of FILE_ATTRIBUTE_TEMPORARY and
FILE_FLAG_DELETE_ON_CLOSE hints to the filesystem that the file should
never be flushed to disk. */
hdl = CreateFile(
tmpfile,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY,
NULL);
if (hdl == INVALID_HANDLE_VALUE) {
err = GetLastError();
}
}
if (err) {
errno = err;
} else {
DDSRT_WARNING_MSVC_OFF(4996);
if ((fd = _open_osfhandle((intptr_t)hdl, _O_APPEND)) == -1) {
/* errno set by _open_osfhandle. */
CloseHandle(hdl);
} else if ((fh = fdopen(fd, mode)) == NULL) {
/* errno set by fdopen. */
_close(fd); /* Automatically closes underlying handle. */
} else {
return fh;
}
DDSRT_WARNING_MSVC_ON(4996);
}
return NULL;
}
#endif /* _WIN32 */
static void count(void *ptr, const dds_log_data_t *data)
{
(void)data;
*(int *)ptr += 1;
}
static void copy(void *ptr, const dds_log_data_t *data)
{
*(char **)ptr = ddsrt_strdup(data->message);
}
static void setup(void)
{
fh = fmemopen(NULL, 1024, "wb+");
CU_ASSERT_PTR_NOT_NULL_FATAL(fh);
}
static void teardown(void)
{
(void)fclose(fh);
}
/* By default only DDS_LC_FATAL and DDS_LC_ERROR are set. This means setting a
trace sink should not have any effect, because no trace categories are
enabled. The message should end up in the log file. */
CU_Test(dds_log, only_log_file, .init=setup, .fini=teardown)
{
char buf[1024], *ptr;
int cnt = 0;
size_t nbytes;
dds_set_log_file(fh);
dds_set_trace_sink(&count, &cnt);
DDS_ERROR("foo%s\n", "bar");
(void)fseek(fh, 0L, SEEK_SET);
nbytes = fread(buf, 1, sizeof(buf) - 1, fh);
/* At least foobar should have been printed to the log file. */
CU_ASSERT_FATAL(nbytes > 6);
buf[nbytes] = '\0';
ptr = strstr(buf, "foobar\n");
CU_ASSERT_PTR_NOT_NULL(ptr);
/* No trace categories are enabled by default, verify trace callback was
not invoked. */
CU_ASSERT_EQUAL(cnt, 0);
}
/* Messages must be printed to the trace file if at least one trace category
is enabled. Messages must not be written twice if the trace file is the
same as the log file. */
CU_Test(dds_log, same_file, .init=setup, .fini=teardown)
{
char buf[1024], *ptr;
size_t nbytes;
dds_set_log_mask(DDS_LC_ALL);
dds_set_log_file(fh);
dds_set_trace_file(fh);
DDS_ERROR("foo%s\n", "bar");
(void)fseek(fh, 0L, SEEK_SET);
nbytes = fread(buf, 1, sizeof(buf) - 1, fh);
/* At least foobar should have been written to the trace file. */
CU_ASSERT_FATAL(nbytes > 6);
buf[nbytes] = '\0';
ptr = strstr(buf, "foobar\n");
CU_ASSERT_PTR_NOT_NULL_FATAL(ptr);
/* The message should only have been printed once, verify foobar does not
occur again. */
ptr = strstr(ptr + 1, "foobar\n");
CU_ASSERT_PTR_NULL(ptr);
}
/* The sinks are considered to be the same only if the callback and userdata
both are an exact match. If the userdata is different, the function should
be called twice for log messages. */
CU_Test(dds_log, same_sink_function)
{
int log_cnt = 0, trace_cnt = 0;
dds_set_log_mask(DDS_LC_ALL);
dds_set_log_sink(&count, &log_cnt);
dds_set_trace_sink(&count, &trace_cnt);
DDS_ERROR("foo%s\n", "bar");
CU_ASSERT_EQUAL(log_cnt, 1);
CU_ASSERT_EQUAL(trace_cnt, 1);
}
CU_Test(dds_log, exact_same_sink)
{
int cnt = 0;
dds_set_log_mask(DDS_LC_ALL);
dds_set_log_sink(&count, &cnt);
dds_set_trace_sink(&count, &cnt);
DDS_ERROR("foo%s\n", "bar");
CU_ASSERT_EQUAL(cnt, 1);
}
/* The log file must be restored if the sink is unregistered, verify the log
file is not used while the sink is registered. Verify use of the log file is
restored again when the sink is unregistered. */
CU_Test(dds_log, no_sink, .init=setup, .fini=teardown)
{
int ret;
char buf[1024], *ptr = NULL;
size_t cnt[2] = {0, 0};
/* Set the destination log file and verify the message is written. */
dds_set_log_file(fh);
DDS_ERROR("foobar\n");
ret = fseek(fh, 0L, SEEK_SET);
CU_ASSERT_EQUAL_FATAL(ret, 0);
buf[0] = '\0';
cnt[0] = fread(buf, 1, sizeof(buf) - 1, fh);
buf[cnt[0]] = '\0';
ptr = strstr(buf, "foobar\n");
CU_ASSERT_PTR_NOT_NULL_FATAL(ptr);
/* Register a custom sink and verify it receives the message. */
ptr = NULL;
dds_set_log_sink(&copy, &ptr);
DDS_ERROR("foobaz\n");
CU_ASSERT_PTR_NOT_NULL_FATAL(ptr);
CU_ASSERT(strcmp(ptr, "foobaz\n") == 0);
ddsrt_free(ptr);
ptr = NULL;
/* Verify it has not been written to the stream. */
ret = fseek(fh, 0L, SEEK_SET);
CU_ASSERT_EQUAL_FATAL(ret, 0);
buf[0] = '\0';
cnt[1] = fread(buf, 1, sizeof(buf) - 1, fh);
buf[cnt[1]] = '\0';
ptr = strstr(buf, "foobaz\n");
CU_ASSERT_PTR_NULL_FATAL(ptr);
/* Unregister the custom sink and verify the default is restored. */
dds_set_log_sink(0, NULL);
ret = fseek(fh, 0, SEEK_SET);
CU_ASSERT_EQUAL_FATAL(ret, 0);
ptr = NULL;
DDS_ERROR("foobaz\n");
ret = fseek(fh, 0, SEEK_SET);
CU_ASSERT_PTR_NULL(ptr);
if (ptr != NULL) {
ddsrt_free(ptr);
ptr = NULL;
}
buf[0]= '\0';
cnt[1] = fread(buf, 1, sizeof(buf) - 1, fh);
#ifdef _WIN32
/* Write on Windows appends. */
CU_ASSERT_EQUAL(cnt[1], cnt[0] * 2);
#else
CU_ASSERT_EQUAL(cnt[1], cnt[0]);
#endif
buf[cnt[1]] = '\0';
ptr = strstr(buf, "foobaz\n");
CU_ASSERT_PTR_NOT_NULL_FATAL(ptr);
}
/* A newline terminates the message. Until that a newline is encountered, the
messages must be concatenated in the buffer. The newline is replaced by a
NULL byte if it is flushed to a sink. */
CU_Test(dds_log, newline_terminates)
{
char *msg = NULL;
dds_set_log_sink(&copy, &msg);
DDS_ERROR("foo");
CU_ASSERT_PTR_NULL_FATAL(msg);
DDS_ERROR("bar");
CU_ASSERT_PTR_NULL_FATAL(msg);
DDS_ERROR("baz\n");
CU_ASSERT_PTR_NOT_NULL_FATAL(msg);
CU_ASSERT(strcmp(msg, "foobarbaz\n") == 0);
ddsrt_free(msg);
}
/* Nothing must be written unless a category is enabled. */
CU_Test(dds_log, disabled_categories_discarded)
{
char *msg = NULL;
dds_set_log_sink(&copy, &msg);
DDS_INFO("foobar\n");
CU_ASSERT_PTR_NULL_FATAL(msg);
dds_set_log_mask(DDS_LC_FATAL | DDS_LC_ERROR | DDS_LC_INFO);
DDS_INFO("foobar\n");
CU_ASSERT_PTR_NOT_NULL_FATAL(msg);
CU_ASSERT(strcmp(msg, "foobar\n") == 0);
ddsrt_free(msg);
}
static ddsrt_cond_t cond;
static ddsrt_mutex_t mutex;
struct arg {
ddsrt_cond_t *cond;
ddsrt_mutex_t *mutex;
dds_time_t stamp;
dds_duration_t pause;
};
static void dummy(void *ptr, const dds_log_data_t *data)
{
(void)ptr;
(void)data;
}
static void block(void *ptr, const dds_log_data_t *data)
{
(void)data;
struct arg *arg = (struct arg *)ptr;
ddsrt_mutex_lock(arg->mutex);
arg->stamp = dds_time();
ddsrt_cond_broadcast(arg->cond);
ddsrt_mutex_unlock(arg->mutex);
dds_sleepfor(arg->pause);
}
static uint32_t run(void *ptr)
{
(void)ptr;
DDS_ERROR("foobar\n");
return 0;
}
/* Log and trace sinks can be changed at runtime. However, the operation must
be synchronous! Verify the dds_set_log_sink blocks while other threads
reside in the log or trace sinks. */
CU_Test(dds_log, synchronous_sink_changes)
{
struct arg arg;
dds_time_t diff, stamp;
ddsrt_thread_t tid;
ddsrt_threadattr_t tattr;
dds_retcode_t ret;
ddsrt_mutex_init(&mutex);
ddsrt_cond_init(&cond);
(void)memset(&arg, 0, sizeof(arg));
arg.mutex = &mutex;
arg.cond = &cond;
arg.pause = 1000000;
ddsrt_mutex_lock(&mutex);
dds_set_log_sink(&block, &arg);
ddsrt_threadattr_init(&tattr);
ret = ddsrt_thread_create(&tid, "foobar", &tattr, &run, &arg);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ddsrt_cond_wait(&cond, &mutex);
dds_set_log_sink(dummy, NULL);
stamp = dds_time();
CU_ASSERT(arg.stamp < stamp);
diff = stamp - arg.stamp;
CU_ASSERT(arg.pause < diff);
}

325
src/ddsrt/tests/select.c Normal file
View file

@ -0,0 +1,325 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include "CUnit/Theory.h"
#include "dds/ddsrt/cdtors.h"
#include "dds/ddsrt/sockets_priv.h"
#include "dds/ddsrt/threads.h"
CU_Init(ddsrt_select)
{
ddsrt_init();
return 0;
}
CU_Clean(ddsrt_select)
{
ddsrt_fini();
return 0;
}
static struct timeval tv_init = { .tv_sec = -2, .tv_usec = -2 };
#define CU_ASSERT_TIMEVAL_EQUAL(tv, secs, usecs) \
CU_ASSERT((tv.tv_sec == secs) && (tv.tv_usec == usecs))
/* Simple test to validate that duration to timeval conversion is correct. */
CU_Test(ddsrt_select, duration_to_timeval)
{
struct timeval tv, *tvptr;
dds_duration_t nsecs_max;
dds_duration_t secs_max = DDSRT_MAX_INTEGER(ddsrt_tv_sec_t);
dds_duration_t usecs_max = 999999;
if (DDS_INFINITY > secs_max) {
CU_ASSERT_EQUAL_FATAL(secs_max, INT32_MAX);
nsecs_max = DDS_INFINITY * secs_max;
} else {
CU_ASSERT_EQUAL_FATAL(secs_max, DDS_INFINITY);
nsecs_max = DDS_INFINITY / DDS_NSECS_IN_SEC;
}
tv = tv_init;
tvptr = ddsrt_duration_to_timeval_ceil(INT64_MIN, &tv);
CU_ASSERT_PTR_EQUAL(tvptr, &tv);
CU_ASSERT_TIMEVAL_EQUAL(tv, 0, 0);
tv = tv_init;
tvptr = ddsrt_duration_to_timeval_ceil(INT64_MIN + 1, &tv);
CU_ASSERT_PTR_EQUAL(tvptr, &tv);
CU_ASSERT_TIMEVAL_EQUAL(tv, 0, 0);
tv = tv_init;
tvptr = ddsrt_duration_to_timeval_ceil(-2, &tv);
CU_ASSERT_PTR_EQUAL(tvptr, &tv);
CU_ASSERT_TIMEVAL_EQUAL(tv, 0, 0);
tv = tv_init;
tvptr = ddsrt_duration_to_timeval_ceil(-1, &tv);
CU_ASSERT_PTR_EQUAL(tvptr, &tv);
CU_ASSERT_TIMEVAL_EQUAL(tv, 0, 0);
tv = tv_init;
tvptr = ddsrt_duration_to_timeval_ceil(0, &tv);
CU_ASSERT_PTR_EQUAL(tvptr, &tv);
CU_ASSERT_TIMEVAL_EQUAL(tv, 0, 0);
tv = tv_init;
tvptr = ddsrt_duration_to_timeval_ceil(nsecs_max - 1, &tv);
CU_ASSERT_PTR_EQUAL(tvptr, &tv);
CU_ASSERT_TIMEVAL_EQUAL(tv, secs_max, usecs_max);
tv = tv_init;
tvptr = ddsrt_duration_to_timeval_ceil(nsecs_max, &tv);
CU_ASSERT_PTR_EQUAL(tvptr, &tv);
CU_ASSERT_TIMEVAL_EQUAL(tv, secs_max, usecs_max);
tv = tv_init;
tvptr = ddsrt_duration_to_timeval_ceil(nsecs_max + 1, &tv);
CU_ASSERT_PTR_EQUAL(tvptr, &tv);
CU_ASSERT_TIMEVAL_EQUAL(tv, secs_max, usecs_max);
tv = tv_init;
tvptr = ddsrt_duration_to_timeval_ceil(DDS_INFINITY - 1, &tv);
CU_ASSERT_PTR_EQUAL(tvptr, &tv);
CU_ASSERT_TIMEVAL_EQUAL(tv, secs_max, usecs_max);
tv = tv_init;
tvptr = ddsrt_duration_to_timeval_ceil(DDS_INFINITY, &tv);
CU_ASSERT_PTR_EQUAL(tvptr, NULL);
CU_ASSERT_TIMEVAL_EQUAL(tv, 0, 0);
}
typedef struct {
dds_duration_t delay;
dds_duration_t skew;
ddsrt_socket_t sock;
} thread_arg_t;
static void
sockets_pipe(ddsrt_socket_t socks[2])
{
dds_retcode_t rc;
ddsrt_socket_t sock;
int reuseaddr = 1;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = htons(54321);
CU_ASSERT_PTR_NOT_NULL_FATAL(socks);
rc = ddsrt_socket(&sock, AF_INET, SOCK_STREAM, 0);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = ddsrt_setsockopt(
sock, SOL_SOCKET, SO_REUSEADDR, (void*)&reuseaddr, sizeof(reuseaddr));
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = ddsrt_socket(&socks[1], AF_INET, SOCK_STREAM, 0);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = ddsrt_bind(sock, (struct sockaddr *)&addr, sizeof(addr));
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = ddsrt_listen(sock, 1);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = ddsrt_connect(socks[1], (struct sockaddr *)&addr, sizeof(addr));
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = ddsrt_accept(sock, NULL, NULL, &socks[0]);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
ddsrt_close(sock);
}
static const char mesg[] = "foobar";
static uint32_t select_timeout_routine(void *ptr)
{
int cnt = -1;
dds_retcode_t rc;
dds_time_t before, after;
dds_duration_t delay;
fd_set rdset;
thread_arg_t *arg = (thread_arg_t *)ptr;
uint32_t res = 0;
FD_ZERO(&rdset);
FD_SET(arg->sock, &rdset);
before = dds_time();
rc = ddsrt_select(arg->sock + 1, &rdset, NULL, NULL, arg->delay, &cnt);
after = dds_time();
delay = after - before;
fprintf(stderr, "Waited for %"PRId64" (nanoseconds)\n", delay);
fprintf(stderr, "Expected to wait %"PRId64" (nanoseconds)\n", arg->delay);
fprintf(stderr, "ddsrt_select returned %d\n", rc);
fprintf(stderr, "ddsrt_select reported %d ready\n", cnt);
if (rc == DDS_RETCODE_TIMEOUT) {
res = (((after - delay) >= (arg->delay - arg->skew)) && (cnt == 0));
}
return res;
}
CU_Test(ddsrt_select, timeout)
{
dds_retcode_t rc;
ddsrt_socket_t socks[2];
ddsrt_thread_t thr;
ddsrt_threadattr_t attr;
thread_arg_t arg;
uint32_t res = 0;
sockets_pipe(socks);
arg.delay = DDS_MSECS(100);
/* Allow the delay to be off by x microseconds (arbitrarily chosen) for
systems with a really poor clock. This test is just to get some
confidence that time calculation is not completely broken, it is by
no means proof that time calculation is entirely correct! */
arg.skew = DDS_MSECS(20);
arg.sock = socks[0];
ddsrt_threadattr_init(&attr);
rc = ddsrt_thread_create(&thr, "select_timeout", &attr, &select_timeout_routine, &arg);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
/* Allow the thread some time to get ready. */
dds_sleepfor(arg.delay * 2);
/* Send data to the read socket to avoid blocking indefinitely. */
ssize_t sent = 0;
rc = ddsrt_send(socks[1], mesg, sizeof(mesg), 0, &sent);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = ddsrt_thread_join(thr, &res);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
CU_ASSERT_EQUAL(res, 1);
(void)ddsrt_close(socks[0]);
(void)ddsrt_close(socks[1]);
}
static uint32_t recv_routine(void *ptr)
{
thread_arg_t *arg = (thread_arg_t*)ptr;
int nfds = 0;
fd_set rdset;
ssize_t rcvd = -1;
char buf[sizeof(mesg)];
FD_ZERO(&rdset);
FD_SET(arg->sock, &rdset);
(void)ddsrt_select(arg->sock + 1, &rdset, NULL, NULL, arg->delay, &nfds);
if (ddsrt_recv(arg->sock, buf, sizeof(buf), 0, &rcvd) == DDS_RETCODE_OK) {
return (rcvd == sizeof(mesg) && strcmp(buf, mesg) == 0);
}
return 0;
}
CU_Test(ddsrt_select, send_recv)
{
dds_retcode_t rc;
ddsrt_socket_t socks[2];
ddsrt_thread_t thr;
ddsrt_threadattr_t attr;
thread_arg_t arg;
uint32_t res = 0;
sockets_pipe(socks);
arg.delay = DDS_SECS(1);
arg.skew = 0;
arg.sock = socks[0];
ddsrt_threadattr_init(&attr);
rc = ddsrt_thread_create(&thr, "recv", &attr, &recv_routine, &arg);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
ssize_t sent = 0;
rc = ddsrt_send(socks[1], mesg, sizeof(mesg), 0, &sent);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
CU_ASSERT_EQUAL(sent, sizeof(mesg));
rc = ddsrt_thread_join(thr, &res);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
CU_ASSERT_EQUAL(res, 1);
(void)ddsrt_close(socks[0]);
(void)ddsrt_close(socks[1]);
}
static uint32_t recvmsg_routine(void *ptr)
{
thread_arg_t *arg = (thread_arg_t*)ptr;
int nfds = 0;
fd_set rdset;
ssize_t rcvd = -1;
char buf[sizeof(mesg)];
ddsrt_msghdr_t msg;
ddsrt_iovec_t iov;
memset(&msg, 0, sizeof(msg));
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
FD_ZERO(&rdset);
FD_SET(arg->sock, &rdset);
(void)ddsrt_select(arg->sock + 1, &rdset, NULL, NULL, arg->delay, &nfds);
if (ddsrt_recvmsg(arg->sock, &msg, 0, &rcvd) == DDS_RETCODE_OK) {
return (rcvd == sizeof(mesg) && strcmp(buf, mesg) == 0);
}
return 0;
}
CU_Test(ddsrt_select, sendmsg_recvmsg)
{
dds_retcode_t rc;
ddsrt_socket_t socks[2];
ddsrt_thread_t thr;
ddsrt_threadattr_t attr;
thread_arg_t arg;
uint32_t res = 0;
sockets_pipe(socks);
memset(&arg, 0, sizeof(arg));
arg.sock = socks[0];
ddsrt_threadattr_init(&attr);
rc = ddsrt_thread_create(&thr, "recvmsg", &attr, &recvmsg_routine, &arg);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
ssize_t sent = 0;
ddsrt_msghdr_t msg;
ddsrt_iovec_t iov;
memset(&msg, 0, sizeof(msg));
iov.iov_base = (void*)mesg;
iov.iov_len = (ddsrt_iov_len_t)sizeof(mesg);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
rc = ddsrt_sendmsg(socks[1], &msg, 0, &sent);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
CU_ASSERT_EQUAL(sent, sizeof(mesg));
rc = ddsrt_thread_join(thr, &res);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
CU_ASSERT_EQUAL(res, 1);
(void)ddsrt_close(socks[0]);
(void)ddsrt_close(socks[1]);
}

198
src/ddsrt/tests/socket.c Normal file
View file

@ -0,0 +1,198 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "CUnit/Theory.h"
#include "dds/ddsrt/cdtors.h"
#include "dds/ddsrt/endian.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/misc.h"
#include "dds/ddsrt/sockets.h"
DDSRT_WARNING_MSVC_OFF(4305)
#if DDSRT_ENDIAN == DDSRT_BIG_ENDIAN
static const struct sockaddr_in ipv4_loopback =
{ .sin_family = AF_INET, .sin_addr = { .s_addr = 0x7f000001 } };
#else
static const struct sockaddr_in ipv4_loopback =
{ .sin_family = AF_INET, .sin_addr = { .s_addr = 0x0100007f } };
#endif /* DDSRT_ENDIAN */
DDSRT_WARNING_MSVC_ON(4305)
#if DDSRT_HAVE_IPV6
static const struct sockaddr_in6 ipv6_loopback =
{ .sin6_family = AF_INET6, .sin6_addr = IN6ADDR_LOOPBACK_INIT };
#endif
static void setup(void)
{
ddsrt_init();
}
static void teardown(void)
{
ddsrt_fini();
}
CU_Test(ddsrt_sockaddrfromstr, bad_family)
{
dds_retcode_t rc;
struct sockaddr_storage sa;
rc = ddsrt_sockaddrfromstr(AF_UNSPEC, "127.0.0.1", &sa);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_BAD_PARAMETER);
}
static void sockaddrfromstr_test(char *str, int af, dds_retcode_t exp)
{
dds_retcode_t rc;
struct sockaddr_storage ss;
rc = ddsrt_sockaddrfromstr(af, str, &ss);
CU_ASSERT_EQUAL(rc, exp);
if (rc == DDS_RETCODE_OK) {
CU_ASSERT_EQUAL(ss.ss_family, af);
}
}
CU_TheoryDataPoints(ddsrt_sockaddrfromstr, ipv4) = {
CU_DataPoints(char *, "127.0.0.1", "0.0.0.0",
"nip"),
CU_DataPoints(int, AF_INET, AF_INET,
AF_INET),
CU_DataPoints(dds_retcode_t, DDS_RETCODE_OK, DDS_RETCODE_OK,
DDS_RETCODE_BAD_PARAMETER)
};
CU_Theory((char *str, int af, dds_retcode_t exp), ddsrt_sockaddrfromstr, ipv4, .init=setup, .fini=teardown)
{
sockaddrfromstr_test(str, af, exp);
}
#if DDSRT_HAVE_IPV6
CU_TheoryDataPoints(ddsrt_sockaddrfromstr, ipv6) = {
CU_DataPoints(char *, "127.0.0.1", "::1",
"::1", "::",
"nip"),
CU_DataPoints(int, AF_INET6, AF_INET6,
AF_INET, AF_INET6,
AF_INET6),
CU_DataPoints(dds_retcode_t, DDS_RETCODE_BAD_PARAMETER, DDS_RETCODE_OK,
DDS_RETCODE_BAD_PARAMETER, DDS_RETCODE_OK,
DDS_RETCODE_BAD_PARAMETER)
};
CU_Theory((char *str, int af, dds_retcode_t exp), ddsrt_sockaddrfromstr, ipv6, .init=setup, .fini=teardown)
{
sockaddrfromstr_test(str, af, exp);
}
#endif /* DDSRT_HAVE_IPV6 */
CU_Test(ddsrt_sockaddrtostr, bad_sockaddr, .init=setup, .fini=teardown)
{
dds_retcode_t rc;
char buf[128] = { 0 };
struct sockaddr_in sa;
memcpy(&sa, &ipv4_loopback, sizeof(ipv4_loopback));
sa.sin_family = AF_UNSPEC;
rc = ddsrt_sockaddrtostr(&sa, buf, sizeof(buf));
CU_ASSERT_EQUAL(rc, DDS_RETCODE_BAD_PARAMETER);
}
CU_Test(ddsrt_sockaddrtostr, no_space, .init=setup, .fini=teardown)
{
dds_retcode_t rc;
char buf[1] = { 0 };
rc = ddsrt_sockaddrtostr(&ipv4_loopback, buf, sizeof(buf));
CU_ASSERT_EQUAL(rc, DDS_RETCODE_NOT_ENOUGH_SPACE);
}
CU_Test(ddsrt_sockaddrtostr, ipv4)
{
dds_retcode_t rc;
char buf[128] = { 0 };
rc = ddsrt_sockaddrtostr(&ipv4_loopback, buf, sizeof(buf));
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
CU_ASSERT_STRING_EQUAL(buf, "127.0.0.1");
}
CU_Test(ddsrt_sockaddrtostr, ipv6)
{
dds_retcode_t rc;
char buf[128] = { 0 };
rc = ddsrt_sockaddrtostr(&ipv6_loopback, buf, sizeof(buf));
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
CU_ASSERT_STRING_EQUAL(buf, "::1");
}
CU_Test(ddsrt_sockets, gethostname)
{
int ret;
dds_retcode_t rc;
char sysbuf[200], buf[200];
buf[0] = '\0';
rc = ddsrt_gethostname(buf, sizeof(buf));
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
sysbuf[0] = '\0';
ret = gethostname(sysbuf, sizeof(sysbuf));
CU_ASSERT_EQUAL(ret, 0);
CU_ASSERT(strcmp(buf, sysbuf) == 0);
rc = ddsrt_gethostname(buf, strlen(buf) - 1);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_NOT_ENOUGH_SPACE);
}
#if DDSRT_HAVE_DNS
static void gethostbyname_test(char *name, int af, dds_retcode_t exp)
{
dds_retcode_t rc;
ddsrt_hostent_t *hent = NULL;
rc = ddsrt_gethostbyname(name, af, &hent);
CU_ASSERT_EQUAL(rc, exp);
if (rc == DDS_RETCODE_OK) {
CU_ASSERT_FATAL(hent->naddrs > 0);
if (af != AF_UNSPEC) {
CU_ASSERT_EQUAL(hent->addrs[0].ss_family, af);
}
}
ddsrt_free(hent);
}
CU_TheoryDataPoints(ddsrt_gethostbyname, ipv4) = {
CU_DataPoints(char *, "", "127.0.0.1", "127.0.0.1"),
CU_DataPoints(int, AF_UNSPEC, AF_INET, AF_UNSPEC),
CU_DataPoints(dds_retcode_t, DDS_RETCODE_HOST_NOT_FOUND, DDS_RETCODE_OK, DDS_RETCODE_OK)
};
CU_Theory((char *name, int af, dds_retcode_t exp), ddsrt_gethostbyname, ipv4, .init=setup, .fini=teardown)
{
gethostbyname_test(name, af, exp);
}
#if DDSRT_HAVE_IPV6
/* Lookup of IPv4 address and specifying AF_INET6 is not invalid as it may
return an IPV4-mapped IPv6 address. */
CU_TheoryDataPoints(ddsrt_gethostbyname, ipv6) = {
CU_DataPoints(char *, "::1", "::1", "::1"),
CU_DataPoints(int, AF_INET, AF_INET6, AF_UNSPEC),
CU_DataPoints(dds_retcode_t, DDS_RETCODE_HOST_NOT_FOUND, DDS_RETCODE_OK, DDS_RETCODE_OK)
};
CU_Theory((char *name, int af, dds_retcode_t exp), ddsrt_gethostbyname, ipv6, .init=setup, .fini=teardown)
{
gethostbyname_test(name, af, exp);
}
#endif /* DDSRT_HAVE_IPV6 */
#endif /* DDSRT_HAVE_DNS */

101
src/ddsrt/tests/string.c Normal file
View file

@ -0,0 +1,101 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <string.h>
#include "CUnit/Theory.h"
#include "dds/ddsrt/string.h"
typedef enum { eq, lt, gt } eq_t;
CU_TheoryDataPoints(ddsrt_strcasecmp, basic) = {
CU_DataPoints(const char *, "a", "aa", "a", "a", "A", "a", "b", "a", "B", "A", "", "a"),
CU_DataPoints(const char *, "a", "a", "aa", "A", "a", "b", "a", "b", "A", "B", "a", ""),
CU_DataPoints(eq_t, eq, gt, lt, eq, eq, lt, gt, lt, gt, lt, lt, gt)
};
CU_Theory((const char *s1, const char *s2, eq_t e), ddsrt_strcasecmp, basic)
{
int r = ddsrt_strcasecmp(s1, s2);
CU_ASSERT((e == eq && r == 0) || (e == lt && r < 0) || (e == gt && r > 0));
}
CU_TheoryDataPoints(ddsrt_strncasecmp, basic) = {
CU_DataPoints(const char *, "a", "aa", "a", "A", "a", "b", "a", "B", "A", "", "a"),
CU_DataPoints(const char *, "a", "a", "aa", "a", "A", "a", "b", "A", "B", "a", ""),
CU_DataPoints(size_t, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1),
CU_DataPoints(eq_t, eq, gt, lt, eq, eq, gt, lt, gt, lt, lt, gt)
};
CU_Theory((const char *s1, const char *s2, size_t n, eq_t e), ddsrt_strncasecmp, basic)
{
int r = ddsrt_strncasecmp(s1, s2, n);
CU_ASSERT((e == eq && r == 0) || (e == lt && r < 0) || (e == gt && r > 0));
}
CU_TheoryDataPoints(ddsrt_strncasecmp, empty) = {
CU_DataPoints(const char *, "a", "", "a", "", "a", ""),
CU_DataPoints(const char *, "", "a", "", "a", "", "a"),
CU_DataPoints(size_t, 1, 1, 0, 0, 2, 2),
CU_DataPoints(eq_t, gt, lt, eq, eq, gt, lt)
};
CU_Theory((const char *s1, const char *s2, size_t n, eq_t e), ddsrt_strncasecmp, empty)
{
int r = ddsrt_strncasecmp(s1, s2, n);
CU_ASSERT((e == eq && r == 0) || (e == lt && r < 0) || (e == gt && r > 0));
}
CU_TheoryDataPoints(ddsrt_strncasecmp, length) = {
CU_DataPoints(const char *, "aBcD", "AbCX", "aBcD", "AbCX", "aBcD"),
CU_DataPoints(const char *, "AbCX", "aBcD", "AbCX", "aBcD", "AbCd"),
CU_DataPoints(size_t, 3, 3, 4, 4, 5, 5),
CU_DataPoints(eq_t, eq, eq, lt, gt, eq, eq)
};
CU_Theory((const char *s1, const char *s2, size_t n, eq_t e), ddsrt_strncasecmp, length)
{
int r = ddsrt_strncasecmp(s1, s2, n);
CU_ASSERT((e == eq && r == 0) || (e == lt && r < 0) || (e == gt && r > 0));
}
CU_Test(ddsrt_string, strtok_r)
{
char *res;
char *saveptr;
char ts1[] = "123,234";
char ts2[] = ",;,123abc,,456,:,";
char ts3[] = ",,,123,,456,789,,,";
res = ddsrt_strtok_r(ts1, ",", &saveptr);
CU_ASSERT(strcmp(res, "123") == 0);
res = ddsrt_strtok_r( NULL, ",", &saveptr);
CU_ASSERT(strcmp(res, "234") == 0);
res = ddsrt_strtok_r( NULL, ",", &saveptr);
CU_ASSERT(res == NULL);
res = ddsrt_strtok_r(ts2, ",;", &saveptr);
CU_ASSERT(strcmp(res, "123abc") == 0);
res = ddsrt_strtok_r( NULL, ",", &saveptr);
CU_ASSERT(strcmp(res, "456") == 0);
res = ddsrt_strtok_r( NULL, ",:", &saveptr);
CU_ASSERT(res == NULL);
res = ddsrt_strtok_r(ts3, ",", &saveptr);
CU_ASSERT(strcmp(res, "123") == 0);
res = ddsrt_strtok_r( NULL, ",", &saveptr);
CU_ASSERT(strcmp(res, "456") == 0);
res = ddsrt_strtok_r( NULL, ",", &saveptr);
CU_ASSERT(strcmp(res, "789") == 0);
res = ddsrt_strtok_r( NULL, ",:", &saveptr);
CU_ASSERT(res == NULL);
}

80
src/ddsrt/tests/strlcpy.c Normal file
View file

@ -0,0 +1,80 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stdio.h>
#include <string.h>
#include "CUnit/Theory.h"
#include "dds/ddsrt/string.h"
CU_TheoryDataPoints(ddsrt_strlcpy, dest_size) = {
CU_DataPoints(char *, "foo", "foo", "foo", "foo", "foo", "", "", ""),
CU_DataPoints(size_t, 0, 1, 3, 4, 5, 0, 1, 2)
};
CU_Theory((char *src, size_t size), ddsrt_strlcpy, dest_size)
{
char dest[] = "................";
size_t len, srclen;
srclen = strlen(src);
len = ddsrt_strlcpy(dest, src, size);
CU_ASSERT_EQUAL(len, srclen);
if (size > 0) {
if ((size - 1) < len) {
len = size - 1;
}
CU_ASSERT_EQUAL(dest[len], '\0');
CU_ASSERT_EQUAL(dest[len+1], '.');
CU_ASSERT((strncmp(dest, src, len) == 0));
} else {
CU_ASSERT_EQUAL(dest[0], '.');
}
}
CU_TheoryDataPoints(ddsrt_strlcat, dest_size) = {
CU_DataPoints(char *, "", "", "", "", "foo", "foo", "foo", "foo", "foo", "foo", "foo", "", "", "foo", "foo", "foo"),
CU_DataPoints(char *, "bar", "bar", "bar", "bar", "bar", "bar", "bar", "bar", "bar", "bar", "bar", "", "", "", "", ""),
CU_DataPoints(size_t, 0, 1, 3, 4, 0, 1, 3, 4, 5, 6, 7, 0, 1, 3, 4, 5)
};
CU_Theory((char *seed, char *src, size_t size), ddsrt_strlcat, dest_size)
{
char dest[] = "................";
size_t len, seedlen, srclen;
seedlen = strlen(seed);
srclen = strlen(src);
memcpy(dest, seed, seedlen);
dest[seedlen] = '\0';
len = ddsrt_strlcat(dest, src, size);
CU_ASSERT_EQUAL(len, (seedlen + srclen));
if (size > 0) {
char foobar[sizeof(dest)];
if ((size - 1) <= seedlen) {
len = seedlen;
} else if ((size - 1) <= len) {
len = size - 1;
}
CU_ASSERT_EQUAL(dest[len], '\0');
if (seedlen < (size - 1)) {
CU_ASSERT_EQUAL(dest[len+1], '.');
}
(void)snprintf(foobar, len+1, "%s%s", seed, src);
CU_ASSERT((strncmp(dest, foobar, len) == 0));
} else {
CU_ASSERT((strcmp(dest, seed) == 0));
}
}

343
src/ddsrt/tests/strtoll.c Normal file
View file

@ -0,0 +1,343 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include "CUnit/Test.h"
#include "dds/ddsrt/cdtors.h"
#include "dds/ddsrt/misc.h"
#include "dds/ddsrt/strtol.h"
const char *str;
char *ptr;
char buf[100];
char str_llmin[100];
char str_llmax[100];
char str_ullmax[100];
char str_llrange[100];
char str_ullrange[100];
char str_xllmin[99], str_xllmax[99];
/* Really test with the maximum values supported on a platform, not some
made up number. */
long long llmin = DDSRT_MIN_INTEGER(long long);
long long llmax = DDSRT_MAX_INTEGER(long long);
unsigned long long ullmax = DDSRT_MAX_INTEGER(unsigned long long);
CU_Init(ddsrt_strtoll)
{
ddsrt_init();
(void)snprintf (str_llmin, sizeof(str_llmin), "%lld", llmin);
(void)snprintf (str_llmax, sizeof(str_llmax), "%lld", llmax);
(void)snprintf (str_llrange, sizeof(str_llrange), "%lld1", llmax);
(void)snprintf (str_ullmax, sizeof(str_ullmax), "%llu", ullmax);
(void)snprintf (str_ullrange, sizeof(str_ullrange), "%llu1", ullmax);
(void)snprintf (str_xllmin, sizeof(str_xllmin), "-%llx", llmin);
(void)snprintf (str_xllmax, sizeof(str_xllmax), "+%llx", llmax);
return 0;
}
CU_Clean(ddstr_strtoll)
{
ddsrt_fini();
return 0;
}
CU_Test(ddsrt_strtoll, strtoll)
{
dds_retcode_t rc;
long long ll;
static char dummy[] = "dummy";
str = "gibberish";
ll = -1;
rc = ddsrt_strtoll(str, &ptr, 0, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 0 && ptr == str);
str = "+gibberish";
ll = -2;
rc = ddsrt_strtoll(str, &ptr, 0, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 0 && ptr == str);
str = "-gibberish";
ll = -3;
rc = ddsrt_strtoll(str, &ptr, 0, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 0 && ptr == str);
str = "gibberish";
ptr = NULL;
ll = -4;
rc = ddsrt_strtoll(str, &ptr, 36, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 46572948005345 && ptr && *ptr == '\0');
str = "1050505055";
ptr = dummy;
ll = -5;
rc = ddsrt_strtoll(str, &ptr, 37, &ll);
CU_ASSERT (rc == DDS_RETCODE_BAD_PARAMETER);
CU_ASSERT (ll == -5 && ptr == dummy);
str = " \t \n 1050505055";
ll = -6;
rc = ddsrt_strtoll(str, NULL, 10, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 1050505055LL);
str = " \t \n -1050505055";
ptr = NULL;
ll = -7;
rc = ddsrt_strtoll(str, &ptr, 10, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == -1050505055LL);
str = " \t \n - \t \n 1050505055";
ptr = NULL;
ll = -8;
rc = ddsrt_strtoll(str, &ptr, 10, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 0LL && ptr == str);
str = "10x";
ptr = NULL;
ll = -9;
rc = ddsrt_strtoll(str, &ptr, 10, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 10LL && ptr && *ptr == 'x');
str = "+10x";
ll = -10;
rc = ddsrt_strtoll(str, &ptr, 10, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 10LL && ptr && *ptr == 'x');
str = "-10x";
ll = -11;
rc = ddsrt_strtoll(str, &ptr, 10, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == -10LL && ptr && *ptr == 'x');
str = (const char *)str_llmax;
ll = -12;
rc = ddsrt_strtoll(str, NULL, 10, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == llmax);
str = (const char *)str_llmin;
ll = -13;
rc = ddsrt_strtoll(str, NULL, 10, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == llmin);
str = (const char *)str_llrange;
ll = -14;
rc = ddsrt_strtoll(str, &ptr, 10, &ll);
CU_ASSERT (rc == DDS_RETCODE_OUT_OF_RANGE);
CU_ASSERT (ll == llmax && *ptr == '1');
str = "0x100";
ll = -15;
rc = ddsrt_strtoll(str, NULL, 16, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 0x100LL);
str = "0X100";
ll = -16;
rc = ddsrt_strtoll(str, NULL, 16, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 0x100LL);
str = "0x1DEFCAB";
ll = -17;
rc = ddsrt_strtoll(str, NULL, 16, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 0x1DEFCABLL);
str = "0x1defcab";
ll = -18;
rc = ddsrt_strtoll(str, NULL, 16, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 0x1DEFCABLL);
str = (char *)str_xllmin;
ll = -19;
rc = ddsrt_strtoll(str, NULL, 16, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == llmin);
str = (char *)str_xllmax;
ll = -20;
rc = ddsrt_strtoll(str, NULL, 16, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == llmax);
str = "0x100";
ll = -21;
rc = ddsrt_strtoll(str, NULL, 0, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 0x100LL);
str = "100";
ll = -22;
rc = ddsrt_strtoll(str, NULL, 16, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 0x100LL);
/* calling os_strtoll with \"%s\" and base 10, expected result 0 */
str = "0x100";
ll = -23;
rc = ddsrt_strtoll(str, &ptr, 10, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 0 && ptr && *ptr == 'x');
/* calling os_strtoll with \"%s\" and base 0, expected result 256 */
str = "0x100g";
ll = -24;
rc = ddsrt_strtoll(str, &ptr, 0, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 256 && ptr && *ptr == 'g');
str = "0100";
ll = -25;
rc = ddsrt_strtoll(str, NULL, 0, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 64LL);
str = "0100";
ll = -26;
rc = ddsrt_strtoll(str, NULL, 8, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 64LL);
str = "100";
ll = -27;
rc = ddsrt_strtoll(str, NULL, 8, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 64LL);
/* calling os_strtoll with \"%s\" and base 10, expected result 100 */
str = "0100";
ll = -28;
rc = ddsrt_strtoll(str, &ptr, 10, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 100);
/* calling os_strtoll with \"%s\" and base 0, expected result 64 */
str = "01008";
ll = -29;
rc = ddsrt_strtoll(str, &ptr, 8, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 64LL && ptr && *ptr == '8');
str = "00001010";
ll = -30;
rc = ddsrt_strtoll(str, NULL, 2, &ll);
CU_ASSERT (rc == DDS_RETCODE_OK);
CU_ASSERT (ll == 10LL);
}
CU_Test(ddsrt_strtoll, strtoull)
{
dds_retcode_t rc;
unsigned long long ull;
str = "0xffffffffffffffff";
ull = 1;
rc = ddsrt_strtoull(str, NULL, 0, &ull);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
CU_ASSERT(ull == ullmax);
str = "-1";
ull = 2;
rc = ddsrt_strtoull(str, NULL, 0, &ull);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
CU_ASSERT(ull == ullmax);
str = "-2";
ull = 3;
rc = ddsrt_strtoull(str, NULL, 0, &ull);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
CU_ASSERT(ull == (ullmax - 1));
}
CU_Test(ddsrt_strtoll, atoll)
{
dds_retcode_t rc;
long long ll;
str = "10";
ll = -1;
rc = ddsrt_atoll(str, &ll);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
CU_ASSERT(ll == 10);
}
CU_Test(ddsrt_strtoll, atoull)
{
dds_retcode_t rc;
unsigned long long ull;
str = "10";
ull = 1;
rc = ddsrt_atoull(str, &ull);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
CU_ASSERT(ull == 10);
}
CU_Test(ddsrt_strtoll, lltostr)
{
long long ll;
ll = llmax;
ptr = ddsrt_lltostr(ll, buf, 0, NULL);
CU_ASSERT(ptr == NULL);
/* calling os_lltostr with %lld with buffer size of 5, expected result \"5432\" */
ll = 54321;
ptr = ddsrt_lltostr(ll, buf, 5, NULL);
CU_ASSERT(strcmp(ptr, "5432") == 0);
ll = llmax;
ptr = ddsrt_lltostr(ll, buf, sizeof(buf), NULL);
CU_ASSERT(strcmp(ptr, str_llmax) == 0);
ll = llmin;
ptr = ddsrt_lltostr(ll, buf, sizeof(buf), NULL);
CU_ASSERT(strcmp(ptr, str_llmin) == 0);
ll = 1;
ptr = ddsrt_lltostr(ll, buf, sizeof(buf), NULL);
CU_ASSERT(strcmp(ptr, "1") == 0);
ll = 0;
ptr = ddsrt_lltostr(ll, buf, sizeof(buf), NULL);
CU_ASSERT(strcmp(ptr, "0") == 0);
ll = -1;
ptr = ddsrt_lltostr(ll, buf, sizeof(buf), NULL);
CU_ASSERT(strcmp(ptr, "-1") == 0);
}
CU_Test(ddsrt_strtoll, ulltostr)
{
unsigned long long ull;
ull = ullmax;
ptr = ddsrt_ulltostr(ull, buf, sizeof(buf), NULL);
CU_ASSERT(strcmp(ptr, str_ullmax) == 0);
ull = 0ULL;
ptr = ddsrt_ulltostr(ull, buf, sizeof(buf), NULL);
CU_ASSERT(strcmp(ptr, "0") == 0);
}

357
src/ddsrt/tests/sync.c Normal file
View file

@ -0,0 +1,357 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stdint.h>
#include "CUnit/Theory.h"
#include "dds/ddsrt/atomics.h"
#include "dds/ddsrt/cdtors.h"
#include "dds/ddsrt/sync.h"
#include "dds/ddsrt/threads.h"
#include "dds/ddsrt/time.h"
CU_Init(ddsrt_sync)
{
ddsrt_init();
return 0;
}
CU_Clean(ddsrt_sync)
{
ddsrt_fini();
return 0;
}
typedef struct {
ddsrt_atomic_uint32_t cnt;
ddsrt_mutex_t lock;
ddsrt_rwlock_t rwlock;
ddsrt_cond_t cond;
dds_time_t abstime;
dds_time_t reltime;
} thread_arg_t;
static uint32_t mutex_lock_routine(void *ptr)
{
int res;
thread_arg_t *arg = (thread_arg_t *)ptr;
ddsrt_atomic_inc32(&arg->cnt);
ddsrt_mutex_lock(&arg->lock);
res = ddsrt_atomic_cas32(&arg->cnt, 2UL, 4UL);
ddsrt_mutex_unlock(&arg->lock);
return (uint32_t)res;
}
/* This test is merely best-effort, the scheduler might schedule the main
main thread before a lock operation is attempted by the second thread. */
CU_Test(ddsrt_sync, mutex_lock_conc)
{
dds_retcode_t ret;
ddsrt_thread_t thr;
ddsrt_threadattr_t attr;
thread_arg_t arg = { .cnt = DDSRT_ATOMIC_UINT32_INIT(0) };
uint32_t res = 0;
ddsrt_mutex_init(&arg.lock);
ddsrt_mutex_lock(&arg.lock);
ddsrt_threadattr_init(&attr);
ret = ddsrt_thread_create(&thr, "mutex_lock_conc", &attr, &mutex_lock_routine, &arg);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
while (ddsrt_atomic_ld32(&arg.cnt) == 0)
/* Wait for thread to be scheduled. */ ;
ddsrt_atomic_inc32(&arg.cnt);
ddsrt_mutex_unlock(&arg.lock);
ret = ddsrt_thread_join(thr, &res);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
CU_ASSERT_EQUAL(res, 1);
CU_ASSERT_EQUAL(ddsrt_atomic_ld32(&arg.cnt), 4UL);
ddsrt_mutex_destroy(&arg.lock);
}
static uint32_t mutex_trylock_routine(void *ptr)
{
thread_arg_t *arg = (thread_arg_t *)ptr;
if (ddsrt_mutex_trylock(&arg->lock)) {
ddsrt_atomic_inc32(&arg->cnt);
}
return ddsrt_atomic_ld32(&arg->cnt);
}
CU_Test(ddsrt_sync, mutex_trylock)
{
bool locked;
ddsrt_mutex_t lock;
ddsrt_mutex_init(&lock);
locked = ddsrt_mutex_trylock(&lock);
CU_ASSERT(locked == true);
locked = ddsrt_mutex_trylock (&lock);
/* NOTE: On VxWorks RTP mutexes seemingly can be locked recursively. Still,
behavior should be consistent across targets. If this fails, fix
the implementation instead. */
CU_ASSERT(locked == false);
ddsrt_mutex_unlock(&lock);
ddsrt_mutex_destroy(&lock);
}
static uint32_t rwlock_tryread_routine(void *ptr)
{
thread_arg_t *arg = (thread_arg_t *)ptr;
if (ddsrt_rwlock_tryread(&arg->rwlock)) {
ddsrt_atomic_inc32(&arg->cnt);
ddsrt_rwlock_unlock(&arg->rwlock);
}
return ddsrt_atomic_ld32(&arg->cnt);
}
static uint32_t rwlock_trywrite_routine(void *ptr)
{
thread_arg_t *arg = (thread_arg_t *)ptr;
/* This operation should never succeed in the test, but if it does the
result must reflect it. */
if (ddsrt_rwlock_trywrite(&arg->rwlock)) {
ddsrt_atomic_inc32(&arg->cnt);
ddsrt_rwlock_unlock(&arg->rwlock);
}
return ddsrt_atomic_ld32(&arg->cnt);
}
CU_Test(ddsrt_sync, mutex_trylock_conc)
{
dds_retcode_t ret;
ddsrt_thread_t thr;
ddsrt_threadattr_t attr;
thread_arg_t arg = { .cnt = DDSRT_ATOMIC_UINT32_INIT(1) };
uint32_t res = 0;
ddsrt_mutex_init(&arg.lock);
ddsrt_mutex_lock(&arg.lock);
ddsrt_threadattr_init(&attr);
ret = ddsrt_thread_create(&thr, "mutex_trylock_conc", &attr, &mutex_trylock_routine, &arg);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ret = ddsrt_thread_join(thr, &res);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
CU_ASSERT_EQUAL(res, 1);
ddsrt_mutex_unlock(&arg.lock);
ddsrt_mutex_destroy(&arg.lock);
}
#define READ (1)
#define TRYREAD (2)
#define WRITE (3)
#define TRYWRITE (4)
CU_TheoryDataPoints(ddsrt_sync, rwlock_trylock_conc) = {
CU_DataPoints(uint32_t, READ, READ, WRITE, WRITE),
CU_DataPoints(uint32_t, TRYREAD, TRYWRITE, TRYREAD, TRYWRITE),
CU_DataPoints(uint32_t, 2, 1, 1, 1)
};
CU_Theory((uint32_t lock, uint32_t trylock, uint32_t exp), ddsrt_sync, rwlock_trylock_conc)
{
dds_retcode_t ret;
ddsrt_thread_t thr;
ddsrt_threadattr_t attr;
ddsrt_thread_routine_t func;
thread_arg_t arg = { .cnt = DDSRT_ATOMIC_UINT32_INIT(1) };
uint32_t res = 0;
ddsrt_rwlock_init(&arg.rwlock);
if (lock == READ) {
ddsrt_rwlock_read(&arg.rwlock);
} else {
ddsrt_rwlock_write(&arg.rwlock);
}
if (trylock == TRYREAD) {
func = &rwlock_tryread_routine;
} else {
func = &rwlock_trywrite_routine;
}
ddsrt_threadattr_init(&attr);
ret = ddsrt_thread_create(&thr, "rwlock_trylock_conc", &attr, func, &arg);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ret = ddsrt_thread_join(thr, &res);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ddsrt_rwlock_unlock(&arg.rwlock);
CU_ASSERT_EQUAL(res, exp);
ddsrt_rwlock_destroy(&arg.rwlock);
}
/* An atomic read is used for synchronization because it is important that the
threads try to access the once control concurrently as much as possible. Of
course, this is only best-effort as there is no guarantee that
initialization is actually tried concurrently. */
static ddsrt_atomic_uint32_t once_count = DDSRT_ATOMIC_UINT32_INIT(0);
static ddsrt_once_t once_control = DDSRT_ONCE_INIT;
#define ONCE_THREADS (8)
static void do_once(void)
{
ddsrt_atomic_inc32(&once_count);
}
static uint32_t once_routine(void *ptr)
{
(void)ptr;
while (ddsrt_atomic_ld32(&once_count) == 0)
/* Wait for the go-ahead. */ ;
ddsrt_once(&once_control, &do_once);
return ddsrt_atomic_ld32(&once_count);
}
CU_Test(ddsrt_sync, once_conc)
{
dds_retcode_t ret;
ddsrt_thread_t thrs[ONCE_THREADS];
ddsrt_threadattr_t attr;
uint32_t res;
char buf[32];
ddsrt_threadattr_init(&attr);
for (int i = 0; i < ONCE_THREADS; i++) {
(void)snprintf(buf, sizeof(buf), "once_conc%d", i + 1);
ret = ddsrt_thread_create(&thrs[i], buf, &attr, &once_routine, NULL);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
}
ddsrt_atomic_st32(&once_count, 1);
for (int i = 0; i < ONCE_THREADS; i++) {
res = 0;
ret = ddsrt_thread_join(thrs[i], &res);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
CU_ASSERT_EQUAL(res, 2);
}
ddsrt_once(&once_control, &do_once);
CU_ASSERT_EQUAL(ddsrt_atomic_ld32(&once_count), 2);
}
static uint32_t waitfor_routine(void *ptr)
{
dds_time_t before, after;
dds_duration_t reltime;
thread_arg_t *arg = (thread_arg_t *)ptr;
uint32_t cnt = 0, res = 0;
while (ddsrt_atomic_ld32(&arg->cnt) == 0)
/* Wait for go-ahead. */ ;
ddsrt_mutex_lock(&arg->lock);
before = dds_time();
reltime = arg->reltime;
while (ddsrt_cond_waitfor(&arg->cond, &arg->lock, reltime)) {
after = dds_time();
if ((after - before) < arg->reltime && (after - before) > 0) {
reltime = arg->reltime - (after - before);
} else {
reltime = 0;
}
cnt++;
}
after = dds_time();
reltime = after - before;
fprintf(stderr, "waited for %"PRId64" (nanoseconds)\n", reltime);
fprintf(stderr, "expected to wait %"PRId64" (nanoseconds)\n", arg->reltime);
fprintf(stderr, "woke up %u times\n", cnt);
ddsrt_mutex_unlock(&arg->lock);
if (reltime >= arg->reltime) {
/* Ensure that the condition variable at least waited for the amount of
time so that time calculation is not (too) broken.*/
res = cnt < 3; /* An arbitrary number to ensure the implementation
did not just spin, aka is completely broken. */
}
return res;
}
CU_Test(ddsrt_sync, cond_waitfor)
{
dds_retcode_t rc;
ddsrt_thread_t thr;
ddsrt_threadattr_t attr;
thread_arg_t arg = { .cnt = DDSRT_ATOMIC_UINT32_INIT(0), .reltime = DDS_MSECS(100) };
uint32_t res = 0;
ddsrt_mutex_init(&arg.lock);
ddsrt_cond_init(&arg.cond);
ddsrt_mutex_lock(&arg.lock);
ddsrt_threadattr_init(&attr);
rc = ddsrt_thread_create(&thr, "cond_waitfor", &attr, &waitfor_routine, &arg);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
ddsrt_mutex_unlock(&arg.lock);
/* Give go-ahead. */
ddsrt_atomic_inc32(&arg.cnt);
/* Wait a little longer than the waiting thread. */
dds_sleepfor(arg.reltime * 2);
/* Send a signal too avoid blocking indefinitely. */
ddsrt_cond_signal(&arg.cond);
rc = ddsrt_thread_join(thr, &res);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
CU_ASSERT_EQUAL(res, 1);
}
static uint32_t waituntil_routine(void *ptr)
{
dds_time_t after;
thread_arg_t *arg = (thread_arg_t *)ptr;
uint32_t cnt = 0, res = 0;
ddsrt_mutex_lock(&arg->lock);
while(ddsrt_cond_waituntil(&arg->cond, &arg->lock, arg->abstime)) {
cnt++;
}
after = dds_time();
ddsrt_mutex_unlock(&arg->lock);
fprintf(stderr, "waited until %"PRId64" (nanoseconds)\n", after);
fprintf(stderr, "expected to wait until %"PRId64" (nanoseconds)\n", arg->abstime);
fprintf(stderr, "woke up %u times\n", cnt);
if (after > arg->abstime) {
res = cnt < 3; /* An arbitrary number to ensure the implementation
did not just spin, aka is completely broken. */
}
return res;
}
CU_Test(ddsrt_sync, cond_waituntil)
{
dds_retcode_t rc;
dds_duration_t delay = DDS_MSECS(100);
ddsrt_thread_t thr;
ddsrt_threadattr_t attr;
thread_arg_t arg = { .cnt = DDSRT_ATOMIC_UINT32_INIT(0) };
uint32_t res = 0;
arg.abstime = dds_time() + delay;
ddsrt_mutex_init(&arg.lock);
ddsrt_cond_init(&arg.cond);
ddsrt_mutex_lock(&arg.lock);
ddsrt_threadattr_init(&attr);
rc = ddsrt_thread_create(&thr, "cond_waituntil", &attr, &waituntil_routine, &arg);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
ddsrt_mutex_unlock(&arg.lock);
dds_sleepfor(delay * 2);
/* Send a signal too avoid blocking indefinitely. */
ddsrt_cond_signal(&arg.cond);
rc = ddsrt_thread_join(thr, &res);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
CU_ASSERT_EQUAL(res, 1);
}

233
src/ddsrt/tests/thread.c Normal file
View file

@ -0,0 +1,233 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <assert.h>
#include <stdlib.h>
#if !defined(_WIN32)
#include <sched.h>
#include <unistd.h>
#endif
#include "CUnit/Theory.h"
#include "dds/ddsrt/cdtors.h"
#include "dds/ddsrt/retcode.h"
#include "dds/ddsrt/sync.h"
#include "dds/ddsrt/threads.h"
static int32_t min_fifo_prio = 250;
static int32_t max_fifo_prio = 250;
static int32_t max_other_prio = 60;
static int32_t min_other_prio = 250;
CU_Init(ddsrt_thread)
{
ddsrt_init();
#if defined(WIN32)
max_fifo_prio = THREAD_PRIORITY_HIGHEST;
min_fifo_prio = THREAD_PRIORITY_LOWEST;
max_other_prio = THREAD_PRIORITY_HIGHEST;
min_other_prio = THREAD_PRIORITY_LOWEST;
#else
min_fifo_prio = sched_get_priority_min(SCHED_FIFO);
max_fifo_prio = sched_get_priority_max(SCHED_FIFO);
# if !defined(_WRS_KERNEL)
max_other_prio = sched_get_priority_max(SCHED_OTHER);
min_other_prio = sched_get_priority_min(SCHED_OTHER);
# endif
#endif
return 0;
}
CU_Clean(ddsrt_thread)
{
ddsrt_fini();
return 0;
}
typedef struct {
int res;
int ret;
ddsrt_threadattr_t *attr;
} thread_arg_t;
uint32_t thread_main(void *ptr)
{
thread_arg_t *arg = (thread_arg_t *)ptr;
ddsrt_threadattr_t *attr;
assert(arg != NULL);
attr = arg->attr;
#if _WIN32
int prio = GetThreadPriority(GetCurrentThread());
if (prio == THREAD_PRIORITY_ERROR_RETURN)
abort();
if (prio == attr->schedPriority) {
arg->res = 1;
}
#else
int err;
int policy;
struct sched_param sched;
err = pthread_getschedparam(pthread_self(), &policy, &sched);
if (err != 0) {
abort();
}
if (((policy == SCHED_OTHER && attr->schedClass == DDSRT_SCHED_TIMESHARE) ||
(policy == SCHED_FIFO && attr->schedClass == DDSRT_SCHED_REALTIME))
&& (sched.sched_priority == attr->schedPriority))
{
arg->res = 1;
}
#endif
return (uint32_t)arg->ret;
}
CU_TheoryDataPoints(ddsrt_thread, create_and_join) = {
CU_DataPoints(ddsrt_sched_t, DDSRT_SCHED_TIMESHARE, DDSRT_SCHED_TIMESHARE,
DDSRT_SCHED_REALTIME, DDSRT_SCHED_REALTIME),
CU_DataPoints(int32_t *, &min_other_prio, &max_other_prio,
&min_fifo_prio, &max_fifo_prio),
CU_DataPoints(uint32_t, 10101, 20202,
30303, 40404)
};
CU_Theory((ddsrt_sched_t sched, int32_t *prio, uint32_t exp), ddsrt_thread, create_and_join)
{
int skip = 0;
uint32_t res = 50505;
dds_retcode_t ret;
ddsrt_thread_t thr;
ddsrt_threadattr_t attr;
thread_arg_t arg;
#if defined(__VXWORKS__)
# if defined(_WRS_KERNEL)
if (sched == DDSRT_SCHED_TIMESHARE) {
skip = 1;
CU_PASS("VxWorks DKM only supports SCHED_FIFO");
}
# endif
#elif !defined(_WIN32)
if (sched == DDSRT_SCHED_REALTIME && (getuid() != 0 && geteuid() != 0)) {
skip = 1;
CU_PASS("SCHED_FIFO requires root privileges");
}
#endif
if (!skip) {
ddsrt_threadattr_init(&attr);
attr.schedClass = sched;
attr.schedPriority = *prio;
memset(&arg, 0, sizeof(arg));
arg.ret = (int32_t)exp;
arg.attr = &attr;
ret = ddsrt_thread_create(&thr, "thread", &attr, &thread_main, &arg);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
if (ret == DDS_RETCODE_OK) {
ret = ddsrt_thread_join (thr, &res);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
CU_ASSERT_EQUAL(res, exp);
if (ret == DDS_RETCODE_OK) {
CU_ASSERT_EQUAL(arg.res, 1);
}
}
}
}
CU_Test(ddsrt_thread, thread_id)
{
int eq = 0;
ddsrt_thread_t thr;
#if defined(_WIN32)
DWORD _tid;
#else
pthread_t _thr;
#endif
thr = ddsrt_thread_self();
#if defined(_WIN32)
_tid = GetCurrentThreadId();
eq = (thr.tid == _tid);
#else
_thr = pthread_self();
eq = pthread_equal(thr.v, _thr);
#endif
CU_ASSERT_NOT_EQUAL(eq, 0);
}
static ddsrt_mutex_t locks[2];
uint32_t thread_main_waitforme(void *ptr)
{
uint32_t ret = 0;
(void)ptr;
ddsrt_mutex_lock(&locks[0]);
ret = 10101;
ddsrt_mutex_unlock(&locks[0]);
return ret;
}
uint32_t thread_main_waitforit(void *ptr)
{
uint32_t res = 0;
ddsrt_thread_t *thr = (ddsrt_thread_t *)ptr;
ddsrt_mutex_lock(&locks[1]);
(void)ddsrt_thread_join(*thr, &res);
ddsrt_mutex_unlock(&locks[1]);
return res + 20202;
}
CU_Test(ddsrt_thread, stacked_join)
{
dds_retcode_t ret;
ddsrt_thread_t thrs[2];
ddsrt_threadattr_t attr;
uint32_t res = 0;
ddsrt_mutex_init(&locks[0]);
ddsrt_mutex_init(&locks[1]);
ddsrt_mutex_lock(&locks[0]);
ddsrt_mutex_lock(&locks[1]);
ddsrt_threadattr_init(&attr);
ret = ddsrt_thread_create(&thrs[0], "", &attr, &thread_main_waitforme, NULL);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ret = ddsrt_thread_create(&thrs[1], "", &attr, &thread_main_waitforit, &thrs[0]);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ddsrt_mutex_unlock(&locks[1]);
dds_sleepfor(DDS_MSECS(100)); /* 100ms */
ddsrt_mutex_unlock(&locks[0]);
ddsrt_thread_join(thrs[1], &res);
CU_ASSERT_EQUAL(res, 30303);
ddsrt_mutex_destroy(&locks[0]);
ddsrt_mutex_destroy(&locks[1]);
}
CU_Test(ddsrt_thread, attribute)
{
ddsrt_threadattr_t attr;
ddsrt_threadattr_init(&attr);
CU_ASSERT_EQUAL(attr.schedClass, DDSRT_SCHED_DEFAULT);
CU_ASSERT_EQUAL(attr.schedPriority, 0);
CU_ASSERT_EQUAL(attr.stackSize, 0);
}

View file

@ -0,0 +1,286 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <assert.h>
#include <stdint.h>
#include "CUnit/Test.h"
#include "dds/ddsrt/cdtors.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/sync.h"
#include "dds/ddsrt/threads.h"
CU_Init(ddsrt_thread_cleanup)
{
ddsrt_init();
return 0;
}
CU_Clean(ddsrt_thread_cleanup)
{
ddsrt_fini();
return 0;
}
#define THREAD_RESET_1 (1<<0)
#define THREAD_RESET_2 (1<<1)
#define THREAD_RUN_OFFSET (4)
#define THREAD_RUN_1 (1<<(THREAD_RUN_OFFSET))
#define THREAD_RUN_2 (1<<(THREAD_RUN_OFFSET + 1))
struct thread_argument {
int flags;
int pop;
int one;
int two;
int executed;
int cancelled;
int block;
ddsrt_mutex_t *mutex;
ddsrt_thread_t thread;
};
static struct thread_argument *
make_thread_argument(
int flags, int pop, int one, int two)
{
struct thread_argument *targ = ddsrt_malloc(sizeof(*targ));
memset(targ, 0, sizeof(*targ));
targ->flags = flags;
targ->pop = pop;
targ->one = one;
targ->two = two;
return targ;
}
static void
reset_one(
void *arg)
{
struct thread_argument *targ = (struct thread_argument *)arg;
targ->one = 0;
targ->executed++;
}
static void
reset_two(
void *arg)
{
struct thread_argument *targ = (struct thread_argument *)arg;
targ->two = 0;
targ->executed++;
}
static uint32_t
thread_main(
void *arg)
{
int pushed = 0;
int popped = 0;
int execute = 0;
struct thread_argument *targ = (struct thread_argument *)arg;
if (targ->flags & THREAD_RESET_1) {
ddsrt_thread_cleanup_push(&reset_one, arg);
pushed++;
}
if (targ->flags & THREAD_RESET_2) {
ddsrt_thread_cleanup_push(&reset_two, arg);
pushed++;
}
assert(targ->pop <= pushed);
if (targ->block) {
ddsrt_mutex_lock(targ->mutex);
}
while (popped < targ->pop) {
execute = 1 << (THREAD_RUN_OFFSET + (targ->pop - (popped + 1)));
ddsrt_thread_cleanup_pop(targ->flags & execute);
targ->cancelled++;
popped++;
}
if (targ->block) {
ddsrt_mutex_unlock(targ->mutex);
}
return 0;
}
static void
setup(
struct thread_argument *arg)
{
dds_retcode_t rc;
ddsrt_thread_t thr;
ddsrt_threadattr_t attr;
uint32_t tres = 0;
ddsrt_threadattr_init(&attr);
rc = ddsrt_thread_create(&thr, "", &attr, &thread_main, (void *)arg);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
arg->thread = thr;
if (!arg->block) {
rc = ddsrt_thread_join(thr, &tres);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
}
}
/* verify the cleanup routine is called */
CU_Test(ddsrt_thread_cleanup, push_one)
{
int flags = THREAD_RESET_1;
struct thread_argument *targ = make_thread_argument(flags, 0, 1, 2);
setup(targ);
CU_ASSERT_EQUAL(targ->one, 0);
CU_ASSERT_EQUAL(targ->two, 2);
CU_ASSERT_EQUAL(targ->executed, 1);
CU_ASSERT_EQUAL(targ->cancelled, 0);
ddsrt_free(targ);
}
/* verify all cleanup routines are called if multiple are registered */
CU_Test(ddsrt_thread_cleanup, push_two)
{
int flags = THREAD_RESET_1 | THREAD_RESET_2;
struct thread_argument *targ = make_thread_argument(flags, 0, 1, 2);
setup(targ);
CU_ASSERT_EQUAL(targ->one, 0);
CU_ASSERT_EQUAL(targ->two, 0);
CU_ASSERT_EQUAL(targ->executed, 2);
CU_ASSERT_EQUAL(targ->cancelled, 0);
ddsrt_free(targ);
}
/* verify the first cleanup routine is still called if second got popped */
CU_Test(ddsrt_thread_cleanup, push_two_pop_one_no_exec)
{
int flags = THREAD_RESET_1 | THREAD_RESET_2;
struct thread_argument *targ = make_thread_argument(flags, 1, 1, 2);
setup(targ);
CU_ASSERT_EQUAL(targ->one, 0);
CU_ASSERT_EQUAL(targ->two, 2);
CU_ASSERT_EQUAL(targ->executed, 1);
CU_ASSERT_EQUAL(targ->cancelled, 1);
ddsrt_free(targ);
}
CU_Test(ddsrt_thread_cleanup, push_two_pop_one_exec)
{
int flags = THREAD_RESET_1 | THREAD_RESET_2 | THREAD_RUN_1;
struct thread_argument *targ = make_thread_argument(flags, 1, 1, 2);
setup(targ);
CU_ASSERT_EQUAL(targ->one, 0);
CU_ASSERT_EQUAL(targ->two, 0);
CU_ASSERT_EQUAL(targ->executed, 2);
CU_ASSERT_EQUAL(targ->cancelled, 1);
ddsrt_free(targ);
}
/* verify no cleanup routines are called if all got popped */
CU_Test(ddsrt_thread_cleanup, push_two_pop_two_no_exec)
{
int flags = THREAD_RESET_1 | THREAD_RESET_2;
struct thread_argument *targ = make_thread_argument(flags, 2, 1, 2);
setup(targ);
CU_ASSERT_EQUAL(targ->one, 1);
CU_ASSERT_EQUAL(targ->two, 2);
CU_ASSERT_EQUAL(targ->executed, 0);
CU_ASSERT_EQUAL(targ->cancelled, 2);
ddsrt_free(targ);
}
CU_Test(ddsrt_thread_cleanup, push_two_pop_two_exec_one)
{
int flags = THREAD_RESET_1 | THREAD_RESET_2 | THREAD_RUN_1;
struct thread_argument *targ = make_thread_argument(flags, 2, 1, 2);
setup(targ);
CU_ASSERT_EQUAL(targ->one, 0);
CU_ASSERT_EQUAL(targ->two, 2);
CU_ASSERT_EQUAL(targ->executed, 1);
CU_ASSERT_EQUAL(targ->cancelled, 2);
ddsrt_free(targ);
}
CU_Test(ddsrt_thread_cleanup, push_two_pop_two_exec_both)
{
int flags = THREAD_RESET_1 | THREAD_RESET_2 | THREAD_RUN_1 | THREAD_RUN_2;
struct thread_argument *targ = make_thread_argument(flags, 2, 1, 2);
setup(targ);
CU_ASSERT_EQUAL(targ->one, 0);
CU_ASSERT_EQUAL(targ->two, 0);
CU_ASSERT_EQUAL(targ->executed, 2);
CU_ASSERT_EQUAL(targ->cancelled, 2);
ddsrt_free(targ);
}
CU_Test(ddsrt_thread_cleanup, no_interference)
{
int flags = THREAD_RESET_1 | THREAD_RESET_2;
struct thread_argument *targ1 = make_thread_argument(flags, 0, 1, 2);
struct thread_argument *targ2 = make_thread_argument(flags, 2, 1, 2);
ddsrt_mutex_t mutex1, mutex2;
ddsrt_mutex_init(&mutex1);
ddsrt_mutex_init(&mutex2);
ddsrt_mutex_lock(&mutex1);
ddsrt_mutex_lock(&mutex2);
targ1->mutex = &mutex1;
targ1->block = 1;
targ2->mutex = &mutex2;
targ2->block = 1;
setup(targ1);
setup(targ2);
/* ensure thread 2 pops it's cleanup routines while thread 1 blocks */
ddsrt_mutex_unlock(&mutex2);
ddsrt_thread_join(targ2->thread, NULL);
CU_ASSERT_EQUAL(targ2->one, 1);
CU_ASSERT_EQUAL(targ2->two, 2);
CU_ASSERT_EQUAL(targ2->executed, 0);
CU_ASSERT_EQUAL(targ2->cancelled, 2);
/* instruct thread 1 to continue */
ddsrt_mutex_unlock(&mutex1);
ddsrt_thread_join(targ1->thread, NULL);
CU_ASSERT_EQUAL(targ1->one, 0);
CU_ASSERT_EQUAL(targ1->two, 0);
CU_ASSERT_EQUAL(targ1->executed, 2);
CU_ASSERT_EQUAL(targ1->cancelled, 0);
ddsrt_mutex_destroy(&mutex1);
ddsrt_mutex_destroy(&mutex2);
ddsrt_free(targ1);
ddsrt_free(targ2);
}