From aa2715f4fe2e6bebfab9936ec5d369a4becd74d4 Mon Sep 17 00:00:00 2001 From: Jeroen Koekkoek Date: Thu, 23 May 2019 14:27:56 +0200 Subject: [PATCH] Add support for FreeRTOS and lwIP (#166) Add support for FreeRTOS and lwIP Signed-off-by: Jeroen Koekkoek --- docs/dev/freertos.md | 103 ++++ ports/freertos-posix/CMakeLists.txt | 126 ++++ ports/freertos-posix/README.md | 83 +++ ports/freertos-posix/freertos-sim.cmake.in | 33 ++ ports/freertos-posix/include/FreeRTOSConfig.h | 74 +++ ports/freertos-posix/src/loader.c | 165 ++++++ src/CMakeLists.txt | 4 +- src/cmake/modules/CUnit/src/main.c.in | 4 - src/cmake/modules/FindCUnit.cmake | 13 +- .../modules/GenerateDummyExportHeader.cmake | 51 ++ src/cmake/modules/Packaging.cmake | 2 +- src/core/CMakeLists.txt | 12 +- src/core/ddsc/tests/participant.c | 1 - src/core/ddsi/include/dds/ddsi/q_log.h | 4 + src/core/ddsi/src/ddsi_raweth.c | 2 +- src/core/ddsi/src/ddsi_tcp.c | 6 + src/core/ddsi/src/ddsi_threadmon.c | 4 +- src/core/ddsi/src/ddsi_udp.c | 3 +- src/core/ddsi/src/q_nwif.c | 33 +- src/core/ddsi/src/q_sockwaitset.c | 57 +- src/core/ddsi/src/sysdeps.c | 2 +- src/ddsrt/CMakeLists.txt | 70 ++- src/ddsrt/cmake/ifaddrs.c | 23 + src/ddsrt/cmake/process.c | 18 + src/ddsrt/cmake/rusage.c | 19 + src/ddsrt/include/dds/ddsrt/endian.h | 10 +- src/ddsrt/include/dds/ddsrt/process.h | 19 +- src/ddsrt/include/dds/ddsrt/rusage.h | 12 + src/ddsrt/include/dds/ddsrt/sockets.h | 4 + src/ddsrt/include/dds/ddsrt/sockets/posix.h | 32 +- src/ddsrt/include/dds/ddsrt/sockets/windows.h | 2 +- src/ddsrt/include/dds/ddsrt/sync.h | 4 +- src/ddsrt/include/dds/ddsrt/sync/freertos.h | 93 +++ src/ddsrt/include/dds/ddsrt/threads.h | 6 +- .../include/dds/ddsrt/threads/freertos.h | 35 ++ src/ddsrt/include/dds/ddsrt/threads/posix.h | 6 + src/ddsrt/include/dds/ddsrt/threads/windows.h | 2 + src/ddsrt/include/dds/ddsrt/time.h | 4 + src/ddsrt/include/dds/ddsrt/time/freertos.h | 53 ++ src/ddsrt/include/dds/ddsrt/types/posix.h | 4 + src/ddsrt/src/heap/freertos/heap.c | 136 +++++ src/ddsrt/src/ifaddrs/lwip/ifaddrs.c | 207 +++++++ src/ddsrt/src/process/freertos/process.c | 22 + src/ddsrt/src/rusage/freertos/rusage.c | 96 +++ src/ddsrt/src/sockets.c | 34 +- .../sockets/include/dds/ddsrt/sockets_priv.h | 4 +- src/ddsrt/src/sockets/posix/gethostname.c | 31 +- src/ddsrt/src/sockets/posix/socket.c | 48 +- src/ddsrt/src/sync/freertos/sync.c | 469 +++++++++++++++ src/ddsrt/src/sync/freertos/tasklist.c | 396 +++++++++++++ src/ddsrt/src/threads/freertos/threads.c | 545 ++++++++++++++++++ .../threads/include/dds/ddsrt/threads_priv.h | 6 - src/ddsrt/src/threads/posix/threads.c | 6 + src/ddsrt/src/threads/windows/threads.c | 6 + src/ddsrt/src/time.c | 2 +- src/ddsrt/src/time/freertos/time.c | 53 ++ src/ddsrt/tests/CMakeLists.txt | 100 ++-- src/ddsrt/tests/ifaddrs.c | 12 +- src/ddsrt/tests/log.c | 16 +- src/ddsrt/tests/select.c | 36 +- src/ddsrt/tests/socket.c | 56 +- src/ddsrt/tests/sync.c | 4 +- src/ddsrt/tests/tasklist.c | 341 +++++++++++ src/ddsrt/tests/thread.c | 38 +- src/examples/roundtrip/ping.c | 6 +- src/examples/roundtrip/pong.c | 6 +- src/tools/ddsperf/ddsperf.c | 17 +- 67 files changed, 3691 insertions(+), 200 deletions(-) create mode 100644 docs/dev/freertos.md create mode 100644 ports/freertos-posix/CMakeLists.txt create mode 100644 ports/freertos-posix/README.md create mode 100644 ports/freertos-posix/freertos-sim.cmake.in create mode 100644 ports/freertos-posix/include/FreeRTOSConfig.h create mode 100644 ports/freertos-posix/src/loader.c create mode 100644 src/cmake/modules/GenerateDummyExportHeader.cmake create mode 100644 src/ddsrt/cmake/ifaddrs.c create mode 100644 src/ddsrt/cmake/process.c create mode 100644 src/ddsrt/cmake/rusage.c create mode 100644 src/ddsrt/include/dds/ddsrt/sync/freertos.h create mode 100644 src/ddsrt/include/dds/ddsrt/threads/freertos.h create mode 100644 src/ddsrt/include/dds/ddsrt/time/freertos.h create mode 100644 src/ddsrt/src/heap/freertos/heap.c create mode 100644 src/ddsrt/src/ifaddrs/lwip/ifaddrs.c create mode 100644 src/ddsrt/src/process/freertos/process.c create mode 100644 src/ddsrt/src/rusage/freertos/rusage.c create mode 100644 src/ddsrt/src/sync/freertos/sync.c create mode 100644 src/ddsrt/src/sync/freertos/tasklist.c create mode 100644 src/ddsrt/src/threads/freertos/threads.c create mode 100644 src/ddsrt/src/time/freertos/time.c create mode 100644 src/ddsrt/tests/tasklist.c diff --git a/docs/dev/freertos.md b/docs/dev/freertos.md new file mode 100644 index 0000000..e877f4b --- /dev/null +++ b/docs/dev/freertos.md @@ -0,0 +1,103 @@ +# FreeRTOS + +[FreeRTOS][1] is real-time operating system kernel for embedded devices. Think +of it as a thread library rather than a general purpose operating system like +Linux or Microsoft Windows. Out-of-the-box, FreeRTOS provides support for +tasks (threads), mutexes, semaphores and software times. Third-party modules +are available to add features. e.g. [lwIP][2] can be used to add networking. + +> FreeRTOS+lwIP is currently supported by Eclipse Cyclone DDS. Support for other +> network stacks, e.g. [FreeRTOS+TCP][3], may be added in the future. + +> Eclipse Cyclone DDS does not make use of [FreeRTOS+POSIX][4] because it was +> not available at the time. Future versions of Eclipse Cyclone DDS may or may +> not require FreeRTOS+POSIX for compatibility when it becomes stable. + +[1]: https://www.freertos.org/ +[2]: https://savannah.nongnu.org/projects/lwip/ +[3]: https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/index.html +[4]: https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_POSIX/index.html + + +## Target + +FreeRTOS provides an operating system kernel. Batteries are not included. i.e. +no C library or device drivers. Third-party distributions, known as board +support packages (BSP), for various (hardware) platforms are available though. + +Board support packages, apart from FreeRTOS, contain: + +* C library. Often ships with the compiler toolchain, e.g. + [IAR Embedded Workbench][5] includes the DLIB runtime, but open source + libraries can also be used. e.g. The [Xilinx Software Development Kit][6] + includes newlib. +* Device drivers. Generally available from the hardware vendor, e.g. NXP or + Xilinx. Device drivers for extra components, like a real-time clock, must + also be included in the board support package. + +[5]: https://www.iar.com/iar-embedded-workbench/ +[6]: https://www.xilinx.com/products/design-tools/embedded-software/sdk.html + +A board support package is linked with the application by the toolchain to +generate a binary that can be flashed to the target. + + +### Requirements + +Eclipse Cyclone DDS requires certain compile-time options to be enabled in +FreeRTOS (`FreeRTOSConfig.h`) and lwIP (`lwipopts.h`) for correct operation. +The compiler will croak when a required compile-time option is not enabled. + +Apart from the aforementioned compile-time options, the target and toolchain +must provide the following. +* Support for thread-local storage (TLS) from the compiler and linker. +* Berkeley socket API compatible socket interface. +* Real-time clock (RTC). A high-precision real-time clock is preferred, but + the monotonic clock can be combined with an offset obtained from e.g. the + network if the target lacks an actual real-time clock. A proper + `clock_gettime` implementation is required to retrieve the wall clock time. + + +### Thread-local storage + +FreeRTOS tasks are not threads and compiler supported thread-local storage +(tls) might not work as desired/expected under FreeRTOS on embedded targets. +i.e. the address of a given variable defined with *__thread* may be the same +for different tasks. + +The compiler generates code to retrieve a unique address per thread when it +encounters a variable defined with *__thread*. What code it generates depends +on the compiler and the target it generates the code for. e.g. `iccarm.exe` +that comes with IAR Embedded Workbench requires `__aeabi_read_tp` to be +implemented and `mb-gcc` that comes with the Xilinx SDK requires +`__tls_get_addr` to be implemented. + +The implementation for each of these functions is more-or-less the same. +Generally speaking they require the number of bytes to allocate, call +`pvTaskGetThreadLocalStoragePointer` and return the address of the memory +block to the caller. + + +## Simulator + +FreeRTOS ports for Windows and POSIX exist to test compatibility. How to +cross-compile Eclipse Cyclone DDS for the [FreeRTOS Windows Port][7] or +the unofficial [FreeRTOS POSIX Port][8] can be found in the msvc and +[posix](/ports/freertos-posix) port directories. + +[7]: https://www.freertos.org/FreeRTOS-Windows-Simulator-Emulator-for-Visual-Studio-and-Eclipse-MingW.html +[8]: https://github.com/shlinym/FreeRTOS-Sim.git + + +## Known Limitations + +Triggering the socket waitset is not (yet) implemented for FreeRTOS+lwIP. This +introduces issues in scenarios where it is required. + + * Receive threads require a trigger to shutdown or a thread may block + indefinitely if no packet arrives from the network. + * Sockets are created dynamically if ManySocketsMode is used and a participant + is created, or TCP is used. A trigger is issued after the sockets are added + to the set if I/O multiplexing logic does not automatically wait for data + on the newly created sockets as well. + diff --git a/ports/freertos-posix/CMakeLists.txt b/ports/freertos-posix/CMakeLists.txt new file mode 100644 index 0000000..005e13b --- /dev/null +++ b/ports/freertos-posix/CMakeLists.txt @@ -0,0 +1,126 @@ +# +# 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 +# +cmake_minimum_required(VERSION 3.5) +project(FreeRTOS-Sim VERSION 10.0.2.0) + +include(GNUInstallDirs) + +# Some distributions place libraries in lib64 when the architecture is x86_64, +# but since the simulator is compiled with -m32, lib is a better name. +if(UNIX AND CMAKE_INSTALL_LIBDIR STREQUAL "lib64") + set(CMAKE_INSTALL_LIBDIR "lib") +endif() + +# Conflicts may be introduced when placing the libraries or headers in the +# default system locations, i.e. /usr/lib and /usr/include on *NIX platforms. +# The install prefix must therefore be postfixed with the project name. +if(UNIX) + set(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/${CMAKE_PROJECT_NAME}") +endif() + +set(ENTRYPOINT "real_main" + CACHE STRING "Alternate name of original entrypoint") +set(FREERTOS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/FreeRTOS-Sim" + CACHE STRING "Location of FreeRTOS POSIX Port sources") + +set(source_path "${FREERTOS_SOURCE_DIR}/Source") + +list(APPEND sources + "${source_path}/croutine.c" + "${source_path}/event_groups.c" + "${source_path}/list.c" + "${source_path}/queue.c" + "${source_path}/tasks.c" + "${source_path}/timers.c" + "${source_path}/portable/MemMang/heap_3.c" + "${source_path}/portable/GCC/POSIX/port.c") + +list(APPEND headers + "${source_path}/include/croutine.h" + "${source_path}/include/deprecated_definitions.h" + "${source_path}/include/event_groups.h" + "${source_path}/include/FreeRTOS.h" + "${source_path}/include/list.h" + "${source_path}/include/mpu_prototypes.h" + "${source_path}/include/mpu_wrappers.h" + "${source_path}/include/portable.h" + "${source_path}/include/projdefs.h" + "${source_path}/include/queue.h" + "${source_path}/include/semphr.h" + "${source_path}/include/StackMacros.h" + "${source_path}/include/task.h" + "${source_path}/include/timers.h" + "${source_path}/portable/GCC/POSIX/portmacro.h") + +list(APPEND headers + "${CMAKE_CURRENT_SOURCE_DIR}/include/FreeRTOSConfig.h") + +add_library(freertos-sim ${sources}) +target_compile_definitions( + freertos-sim PUBLIC __GCC_POSIX=1 MAX_NUMBER_OF_TASKS=300) +target_compile_options( + freertos-sim + PUBLIC + -m32 + PRIVATE + -W -Wall -Werror -Wmissing-braces -Wno-cast-align -Wparentheses -Wshadow + -Wno-sign-compare -Wswitch -Wuninitialized -Wunknown-pragmas + -Wunused-function -Wunused-label -Wunused-parameter -Wunused-value + -Wunused-variable -Wmissing-prototypes) +target_include_directories( + freertos-sim + PUBLIC + "$" + "$" + "$" + "$") +target_link_libraries( + freertos-sim PUBLIC -m32 -pthread) + +if(CMAKE_BUILD_TYPE STREQUAL "DEBUG" OR + CMAKE_BUILD_TYPE STREQUAL "RELWITHDEBINFO") + target_compile_options(freertos-sim PUBLIC -ggdb) + target_link_libraries(freertos-sim PUBLIC -ggdb) +endif() + +# The FreeRTOS POSIX Port does not require hardware to be initialized (unless +# lwIP is used), but the scheduler must be started before it is safe to execute +# application code. A "loader" is built to avoid modifications to existing +# code. The generated toolchain file will automatically redefine "main" to +# "real_main". The "real_main" function is executed once the scheduler is +# started. +# +# The loader is not part of the freertos-sim target as it has no place in the +# board support package. +add_library(freertos-sim-loader + "${CMAKE_CURRENT_SOURCE_DIR}/src/loader.c") +set_source_files_properties( + "${CMAKE_CURRENT_SOURCE_DIR}/src/loader.c" + PROPERTIES COMPILE_DEFINITIONS real_main=${ENTRYPOINT}) +target_link_libraries(freertos-sim-loader freertos-sim) + +install( + FILES ${headers} + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + +configure_file("freertos-sim.cmake.in" "freertos-sim.cmake" @ONLY) +install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/freertos-sim.cmake" + DESTINATION "${CMAKE_INSTALL_DATADIR}") + +install( + TARGETS freertos-sim freertos-sim-loader + EXPORT "${CMAKE_PROJECT_NAME}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") + diff --git a/ports/freertos-posix/README.md b/ports/freertos-posix/README.md new file mode 100644 index 0000000..f812374 --- /dev/null +++ b/ports/freertos-posix/README.md @@ -0,0 +1,83 @@ +# FreeRTOS POSIX Port + +FreeRTOS is a supported platform for Eclipse Cyclone DDS. This document +explains how to build and run Eclipse Cyclone DDS on the FreeRTOS POSIX Port. +For basic information, see: [freertos.md](/docs/dev/freertos.md). + +As the steps for building and running on the FreeRTOS POSIX Port are largely +the same as building and running on an actual embedded target, this document +should provide an excellent starting point for users. Apart from that, the +simulator can be used to verify commits do not break FreeRTOS compatibility. + +> lwIP can also be used in combination with both UNIX and Windows targets, but +> the simulator does not yet have integration. Once integration between both +> is figured out, this document should be updated accordingly. + + +## Build and install the simulator + +The FreeRTOS POSIX Port is not maintained by the FreeRTOS project. Various +projects are maintained across the internet. At the time of writing, the +version maintained by [Shilin][1] seemed the best as the version maintained by +[megakilo][2] was archived. + +[1]: https://github.com/shlinym/FreeRTOS-Sim.git +[2]: https://github.com/megakilo/FreeRTOS-Sim + +> A [FreeRTOS Linux Port][3] is in the works. Once it becomes stable, please +> update this document accordingly. + +[3]: https://sourceforge.net/p/freertos/discussion/382005/thread/f28af711/ + + +1. Clone the repository. The `CMakeLists.txt` in this directory assumes the + sources are available `./FreeRTOS-Sim` by default, but a different location + can be specified using the CMake option `FREERTOS_SOURCE_DIR`. + ``` +git clone https://github.com/shlinym/FreeRTOS-Sim.git +``` + +2. Specify an installation prefix and build the simulator like any other + CMake project. + ``` +mkdir build +cd build +cmake -DCMAKE_INSTALL_PREFIX=$(pwd)/install .. +cmake --build . --target install +``` + +> A CMake toolchain file is generated and installed into a `share` directory +> located under CMAKE\_INSTALL\_PREFIX/FreeRTOS-Sim. The compiler that CMake +> discovers and uses to build the simulator is exported in the toolchain file +> and will also be used to build Eclipse Cyclone DDS in the following steps. + + +## Build Eclipse Cyclone DDS for the simulator + +1. Change to the root of the repository and install the dependencies. + ``` +mkdir build +cd build +conan install -s arch=x86 .. +``` + +> For actual cross-compilation environments the instructions above will not +> install the correct packages. Even when e.g. Clang instead of GCC was used +> to build the simulator, the mismatch between Conan and CMake will break the +> build. To install the correct packages for the target, specify the required +> settings e.g. when the simulator was built using Clang 7.0, use +> `conan install -s arch=x86 -s compiler=clang -s compiler.version=7.0 ..`. +> If packages are not yet available for the target, as is usually the case +> with actual embedded targets, export the path to the toolchain file in the +> `CONAN_CMAKE_TOOLCHAIN_FILE` environment variable and add the `-b` flag to +> build the packages. + +2. Build Eclipse Cyclone DDS. + ``` +$ cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/toolchain/file -DWITH_FREERTOS=on ../src +``` + +> Examples (and tests) can be executed like usual. The simulator provides a +> *loader* that initializes the hardware (not used on non-embedded targets), +> starts the scheduler and loads the application. + diff --git a/ports/freertos-posix/freertos-sim.cmake.in b/ports/freertos-posix/freertos-sim.cmake.in new file mode 100644 index 0000000..73b85f2 --- /dev/null +++ b/ports/freertos-posix/freertos-sim.cmake.in @@ -0,0 +1,33 @@ +# +# 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 +# + +# +# CMake toolchain file generated by @CMAKE_PROJECT_NAME@ +# + +set(CMAKE_C_COMPILER "@CMAKE_C_COMPILER@") +set(CMAKE_CXX_COMPILER "@CMAKE_CXX_COMPILER@") + +set(include_path "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@") +set(library_path "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@") + +set(CMAKE_C_FLAGS "-I${include_path} -Dmain=@ENTRYPOINT@ -m32") +set(CMAKE_EXE_LINKER_FLAGS "-L${library_path}") +set(CMAKE_SHARED_LINKER_FLAGS "-L${library_path}") +set(CMAKE_C_STANDARD_LIBRARIES "-Wl,--start-group,-lfreertos-sim,-lfreertos-sim-loader,--end-group -m32 -lpthread") + +set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + diff --git a/ports/freertos-posix/include/FreeRTOSConfig.h b/ports/freertos-posix/include/FreeRTOSConfig.h new file mode 100644 index 0000000..a149f88 --- /dev/null +++ b/ports/freertos-posix/include/FreeRTOSConfig.h @@ -0,0 +1,74 @@ +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html. + *----------------------------------------------------------*/ + +#define configUSE_PREEMPTION 1 +#define configUSE_IDLE_HOOK 1 +#define configUSE_TICK_HOOK 1 +#define configTICK_RATE_HZ ( ( portTickType ) 1000 ) +#define configMINIMAL_STACK_SIZE ( ( unsigned portSHORT ) 64 ) /* This can be made smaller if required. */ +#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 64 * 1024 ) ) +#define configMAX_TASK_NAME_LEN ( 16 ) +#define configUSE_TRACE_FACILITY 1 +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configCHECK_FOR_STACK_OVERFLOW 0 /* Do not use this option on the PC port. */ +#define configUSE_RECURSIVE_MUTEXES 1 +#define configQUEUE_REGISTRY_SIZE 20 +#define configUSE_MALLOC_FAILED_HOOK 1 +#define configUSE_APPLICATION_TASK_TAG 1 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_ALTERNATIVE_API 0 +//#define configMAX_SYSCALL_INTERRUPT_PRIORITY 1 + +#define configUSE_QUEUE_SETS 1 +#define configUSE_TASK_NOTIFICATIONS 1 + +/* Software timer related configuration options. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 20 +#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) + +#define configMAX_PRIORITIES ( 10 ) + +#define configGENERATE_RUN_TIME_STATS 1 + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. In most cases the linker will remove unused +functions anyway. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 0 /* Do not use this option on the PC port. */ +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTimerGetTimerDaemonTaskHandle 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_pcTaskGetTaskName 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xSemaphoreGetMutexHolder 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xTaskGetHandle 1 + +/* It is a good idea to define configASSERT() while developing. configASSERT() +uses the same semantics as the standard C assert() macro. */ +extern void vAssertCalled( unsigned long ulLine, const char * const pcFileName ); +#define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( __LINE__, __FILE__ ) + +#endif /* FREERTOS_CONFIG_H */ diff --git a/ports/freertos-posix/src/loader.c b/ports/freertos-posix/src/loader.c new file mode 100644 index 0000000..baec424 --- /dev/null +++ b/ports/freertos-posix/src/loader.c @@ -0,0 +1,165 @@ +/* + * 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 + */ + +/* + * Launcher to run existing programs in the FreeRTOS+lwIP Simulator. + * + * Verification of FreeRTOS+lwIP compatibility in Continuous Integration (CI) + * setups is another intended purpose. + */ + +#include +#include +#include +#include +#include +#include +#if _WIN32 +# define EX_OK (0) +# define EX_USAGE (64) +# define LF "\r\n" +#else +# include +# define LF "\n" +#endif + +#include +#include + +/* Setup system hardware. */ +void prvSetupHardware(void) +{ + /* No hardware to setup when running in the simulator. */ + return; +} + +void vAssertCalled(unsigned long ulLine, const char * const pcFileName) +{ + taskENTER_CRITICAL(); + { + fprintf(stderr, "[ASSERT] %s:%lu"LF, pcFileName, ulLine); + } + taskEXIT_CRITICAL(); + abort(); +} + +void vApplicationMallocFailedHook(void) +{ + vAssertCalled(__LINE__, __FILE__); +} + +void vApplicationIdleHook(void) { return; } + +void vApplicationTickHook( void ) { return; } + +static void usage(const char *name) +{ + static const char fmt[] = + "Usage: %s LAUNCHER_OPTIONS -- PROGRAM_OPTIONS"LF + "Try '%s -h' for more information"LF; + + fprintf(stderr, fmt, name, name); +} + +static void help(const char *name) +{ + static const char fmt[] = + "Usage: %s LAUNCHER_OPTIONS -- PROGRAM_OPTIONS"LF + ""LF + "Launcher options:"LF + " -h Show this help message and exit"LF; + + fprintf(stdout, fmt, name); +} + +typedef struct { + int argc; + char **argv; +} args_t; + +extern int real_main(int argc, char *argv[]); + +static void vMainTask(void *ptr) +{ + args_t *args = (args_t *)ptr; + /* Reset getopt global variables. */ + opterr = 1; + optind = 1; + (void)real_main(args->argc, args->argv); + vPortFree(args->argv); + vPortFree(args); + vTaskDelete(NULL); + _Exit(0); +} + +int +main(int argc, char *argv[]) +{ + int opt; + char *name; + args_t *args = NULL; + + /* Determine program name. */ + assert(argc >= 0 && argv[0] != NULL); + name = strrchr(argv[0], '/'); + if (name != NULL) { + name++; + } else { + name = argv[0]; + } + + if ((args = pvPortMalloc(sizeof(*args))) == NULL) { + return EX_OSERR; + } + + memset(args, 0, sizeof(*args)); + + /* Parse command line options. */ + while ((opt = getopt(argc, argv, ":a:dg:hn:")) != -1) { + switch (opt) { + case 'h': + help(name); + exit(EX_OK); + case '?': + fprintf(stderr, "Unknown option '%c'"LF, opt); + usage(name); + exit(EX_USAGE); + case ':': + /* fall through */ + default: + fprintf(stderr, "Option '%c' requires an argument"LF, opt); + usage(name); + exit(EX_USAGE); + } + } + + /* Copy leftover arguments into a new array. */ + args->argc = (argc - optind) + 1; + args->argv = pvPortMalloc((args->argc + 1) * sizeof(*args->argv)); + if (args->argv == NULL) { + return EX_OSERR; + } + args->argv[0] = argv[0]; + for (int i = optind, j = 1; i < argc; i++, j++) { + args->argv[j] = argv[i]; + } + + prvSetupHardware(); + + xTaskCreate(vMainTask, name, + configMINIMAL_STACK_SIZE, args, (tskIDLE_PRIORITY + 1UL), + (xTaskHandle *) NULL); + + vTaskStartScheduler(); + + return EX_SOFTWARE; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d1539dd..ba9fb57 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -191,11 +191,9 @@ endif() add_subdirectory(examples) -if (BUILD_TESTING) - # Multi Process Tests +if(BUILD_TESTING AND HAVE_MULTI_PROCESS) add_subdirectory(mpt) endif() - # Pull-in CPack and support for generating Config.cmake and packages. include(Packaging) diff --git a/src/cmake/modules/CUnit/src/main.c.in b/src/cmake/modules/CUnit/src/main.c.in index 2be3165..a61ac85 100644 --- a/src/cmake/modules/CUnit/src/main.c.in +++ b/src/cmake/modules/CUnit/src/main.c.in @@ -13,12 +13,8 @@ #include #include -#ifndef _WIN32 -#include -#else #define EX_USAGE (64) #define EX_SOFTWARE (70) -#endif /* _WIN32 */ #include #include diff --git a/src/cmake/modules/FindCUnit.cmake b/src/cmake/modules/FindCUnit.cmake index 5b19165..518ed8a 100644 --- a/src/cmake/modules/FindCUnit.cmake +++ b/src/cmake/modules/FindCUnit.cmake @@ -11,7 +11,12 @@ # set(CUNIT_HEADER "CUnit/CUnit.h") -find_path(CUNIT_INCLUDE_DIR ${CUNIT_HEADER}) +if(CONAN_INCLUDE_DIRS) + find_path(CUNIT_INCLUDE_DIR ${CUNIT_HEADER} HINTS ${CONAN_INCLUDE_DIRS}) +else() + find_path(CUNIT_INCLUDE_DIR ${CUNIT_HEADER}) +endif() + mark_as_advanced(CUNIT_INCLUDE_DIR) if(CUNIT_INCLUDE_DIR AND EXISTS "${CUNIT_INCLUDE_DIR}/${CUNIT_HEADER}") @@ -25,7 +30,11 @@ if(CUNIT_INCLUDE_DIR AND EXISTS "${CUNIT_INCLUDE_DIR}/${CUNIT_HEADER}") set(CUNIT_VERSION "${CUNIT_VERSION_MAJOR}.${CUNIT_VERSION_MINOR}-${CUNIT_VERSION_PATCH}") endif() -find_library(CUNIT_LIBRARY cunit) +if(CONAN_LIB_DIRS) + find_library(CUNIT_LIBRARY cunit HINTS ${CONAN_LIB_DIRS}) +else() + find_library(CUNIT_LIBRARY cunit) +endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args( diff --git a/src/cmake/modules/GenerateDummyExportHeader.cmake b/src/cmake/modules/GenerateDummyExportHeader.cmake new file mode 100644 index 0000000..8a26e15 --- /dev/null +++ b/src/cmake/modules/GenerateDummyExportHeader.cmake @@ -0,0 +1,51 @@ +# 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 + +function(GENERATE_DUMMY_EXPORT_HEADER _target) + set(_opts) + set(_single_opts BASE_NAME EXPORT_FILE_NAME) + set(_multi_opts) + cmake_parse_arguments(_args "${_opts}" "${_single_opts}" "${_multi_opts}" ${ARGN}) + + if(NOT _target) + message(FATAL_ERROR "Target not specified") + elseif(NOT TARGET ${_target}) + message(FATAL_ERROR "Target ${_target} does not exist") + endif() + + string(TOUPPER _target_uc "${_target}") + string(TOLOWER _target_lc "${_target}") + + if(_args_EXPORT_FILE_NAME) + set(_path "${_args_EXPORT_FILE_NAME}") + else() + set(_path "${CMAKE_CURRENT_BINARY_DIR}/${_target_lc}_export.h") + endif() + + if(_args_BASE_NAME) + string(TOUPPER "${_args_BASE_NAME}" _base_name) + else() + set(_base_name "${_target_uc}") + endif() + + get_filename_component(_dir "${_path}" DIRECTORY) + get_filename_component(_file "${_path}" NAME) + + if(NOT IS_DIRECTORY "${_dir}") + file(MAKE_DIRECTORY "${_dir}") + endif() + + set(_content +"/* Dummy export header generated by CMake. */ +#define ${_base_name}_EXPORT\n") + + file(WRITE "${_path}" "${_content}") +endfunction() + diff --git a/src/cmake/modules/Packaging.cmake b/src/cmake/modules/Packaging.cmake index 9f0e53d..9b76487 100644 --- a/src/cmake/modules/Packaging.cmake +++ b/src/cmake/modules/Packaging.cmake @@ -37,7 +37,7 @@ install( "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}Version.cmake" DESTINATION "${CMAKE_INSTALL_CMAKEDIR}" COMPONENT dev) -if(DDSC_SHARED AND ((NOT DEFINED BUILD_SHARED_LIBS) OR BUILD_SHARED_LIBS)) +if((NOT DEFINED BUILD_SHARED_LIBS) OR BUILD_SHARED_LIBS) # Generates Targets.cmake file included by Config.cmake. # The files are placed in CMakeFiles/Export in the build tree. install( diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8f36723..f08808e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -19,16 +19,10 @@ FUNCTION(PREPEND var prefix) SET(${var} "${listVar}" PARENT_SCOPE) ENDFUNCTION(PREPEND) -option(DDSC_SHARED "Build DDSC as a shared library" ON) - -if(DDSC_SHARED AND ((NOT DEFINED BUILD_SHARED_LIBS) OR BUILD_SHARED_LIBS)) - # BUILD_SHARED_LIBS is set to off by for example VxWorks DKM environment - add_library(ddsc SHARED "") +if (BUILD_SHARED_LIBS OR NOT DEFINED BUILD_SHARED_LIBS) + add_library(ddsc SHARED) else() - if(DDSC_SHARED) - message(STATUS "Option DDSC_SHARED ignored. Only static libraries supported on this platform.") - endif() - add_library(ddsc "") + add_library(ddsc) endif() add_definitions(-DDDSI_INCLUDE_NETWORK_PARTITIONS -DDDSI_INCLUDE_SSM) diff --git a/src/core/ddsc/tests/participant.c b/src/core/ddsc/tests/participant.c index 14a8171..6b7155d 100644 --- a/src/core/ddsc/tests/participant.c +++ b/src/core/ddsc/tests/participant.c @@ -92,7 +92,6 @@ CU_Test(ddsc_participant, create_with_conf_no_env) { //invalid domain participant = dds_create_participant (1, NULL, NULL); - printf("\n participant is %d\n", participant); CU_ASSERT_FATAL(participant < 0); //valid specific domain value diff --git a/src/core/ddsi/include/dds/ddsi/q_log.h b/src/core/ddsi/include/dds/ddsi/q_log.h index 7a0bfda..2c9dc29 100644 --- a/src/core/ddsi/include/dds/ddsi/q_log.h +++ b/src/core/ddsi/include/dds/ddsi/q_log.h @@ -23,6 +23,7 @@ extern "C" { #endif /* LOG_THREAD_CPUTIME must be considered private. */ +#if DDSRT_HAVE_RUSAGE #define LOG_THREAD_CPUTIME(guard) \ do { \ if (dds_get_log_mask() & DDS_LC_TIMING) { \ @@ -40,6 +41,9 @@ extern "C" { } \ } \ } while (0) +#else +#define LOG_THREAD_CPUTIME(guard) (void)(guard) +#endif /* DDSRT_HAVE_RUSAGE */ #if defined (__cplusplus) } diff --git a/src/core/ddsi/src/ddsi_raweth.c b/src/core/ddsi/src/ddsi_raweth.c index 98cb1d5..dc409c7 100644 --- a/src/core/ddsi/src/ddsi_raweth.c +++ b/src/core/ddsi/src/ddsi_raweth.c @@ -24,7 +24,7 @@ #include "dds/ddsrt/log.h" #include "dds/ddsrt/sockets.h" -#ifdef __linux +#if defined(__linux) && !LWIP_SOCKET #include #include #include diff --git a/src/core/ddsi/src/ddsi_tcp.c b/src/core/ddsi/src/ddsi_tcp.c index 8d7ec2b..676099c 100644 --- a/src/core/ddsi/src/ddsi_tcp.c +++ b/src/core/ddsi/src/ddsi_tcp.c @@ -368,7 +368,13 @@ static bool ddsi_tcp_select (ddsrt_socket_t sock, bool read, size_t pos) int32_t ready = 0; FD_ZERO (&fds); +#if LWIP_SOCKET == 1 + DDSRT_WARNING_GNUC_OFF(sign-conversion) +#endif FD_SET (sock, &fds); +#if LWIP_SOCKET == 1 + DDSRT_WARNING_GNUC_ON(sign-conversion) +#endif DDS_LOG(DDS_LC_TCP, "%s blocked %s: sock %d\n", ddsi_name, read ? "read" : "write", (int) sock); do { diff --git a/src/core/ddsi/src/ddsi_threadmon.c b/src/core/ddsi/src/ddsi_threadmon.c index 6098d71..1f9d4af 100644 --- a/src/core/ddsi/src/ddsi_threadmon.c +++ b/src/core/ddsi/src/ddsi_threadmon.c @@ -118,13 +118,14 @@ static uint32_t threadmon_thread (struct ddsi_threadmon *sl) was_alive = false; } +#if DDSRT_HAVE_RUSAGE if (dds_get_log_mask() & DDS_LC_TIMING) { ddsrt_rusage_t u; if (ddsrt_getrusage (DDSRT_RUSAGE_SELF, &u) == DDS_RETCODE_OK) { DDS_LOG(DDS_LC_TIMING, - "rusage: utime %d.%09d stime %d.%09d maxrss %ld data %ld vcsw %ld ivcsw %ld\n", + "rusage: utime %d.%09d stime %d.%09d maxrss %zu data %zu vcsw %zu ivcsw %zu\n", (int) (u.utime / DDS_NSECS_IN_SEC), (int) (u.utime % DDS_NSECS_IN_SEC), (int) (u.stime / DDS_NSECS_IN_SEC), @@ -132,6 +133,7 @@ static uint32_t threadmon_thread (struct ddsi_threadmon *sl) u.maxrss, u.idrss, u.nvcsw, u.nivcsw); } } +#endif /* DDSRT_HAVE_RUSAGE */ /* While deaf, we need to make sure the receive thread wakes up every now and then to try recreating sockets & rejoining multicast diff --git a/src/core/ddsi/src/ddsi_udp.c b/src/core/ddsi/src/ddsi_udp.c index 53135f1..e2e1576 100644 --- a/src/core/ddsi/src/ddsi_udp.c +++ b/src/core/ddsi/src/ddsi_udp.c @@ -138,9 +138,10 @@ static ssize_t ddsi_udp_conn_write (ddsi_tran_conn_t conn, const nn_locator_t *d #if DDSRT_MSGHDR_FLAGS msg.msg_flags = (int) flags; #else + msg.msg_flags = 0; DDSRT_UNUSED_ARG(flags); #endif -#ifdef MSG_NOSIGNAL +#if MSG_NOSIGNAL && !LWIP_SOCKET sendflags |= MSG_NOSIGNAL; #endif do { diff --git a/src/core/ddsi/src/q_nwif.c b/src/core/ddsi/src/q_nwif.c index 3f5ac27..f15f9dd 100644 --- a/src/core/ddsi/src/q_nwif.c +++ b/src/core/ddsi/src/q_nwif.c @@ -96,12 +96,18 @@ static int set_rcvbuf (ddsrt_socket_t socket) uint32_t ReceiveBufferSize; socklen_t optlen = (socklen_t) sizeof (ReceiveBufferSize); uint32_t socket_min_rcvbuf_size; + dds_retcode_t rc; if (config.socket_min_rcvbuf_size.isdefault) socket_min_rcvbuf_size = 1048576; else socket_min_rcvbuf_size = config.socket_min_rcvbuf_size.value; - if (ddsrt_getsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) &ReceiveBufferSize, &optlen) != DDS_RETCODE_OK) - { + rc = ddsrt_getsockopt( + socket, SOL_SOCKET, SO_RCVBUF, (char *) &ReceiveBufferSize, &optlen); + /* TCP/IP stack may not support SO_RCVBUF. */ + if (rc == DDS_RETCODE_BAD_PARAMETER) { + DDS_LOG(DDS_LC_CONFIG, "cannot retrieve socket receive buffer size\n"); + return 0; + } else if (rc != DDS_RETCODE_OK) { print_sockerror ("get SO_RCVBUF"); return -2; } @@ -139,8 +145,13 @@ static int set_sndbuf (ddsrt_socket_t socket) { unsigned SendBufferSize; socklen_t optlen = (socklen_t) sizeof(SendBufferSize); - if (ddsrt_getsockopt(socket, SOL_SOCKET, SO_SNDBUF,(char *)&SendBufferSize, &optlen) != DDS_RETCODE_OK) - { + dds_retcode_t rc; + rc = ddsrt_getsockopt( + socket, SOL_SOCKET, SO_SNDBUF,(char *)&SendBufferSize, &optlen); + if (rc == DDS_RETCODE_BAD_PARAMETER) { + DDS_LOG(DDS_LC_CONFIG, "cannot retrieve socket send buffer size\n"); + return 0; + } else if (rc != DDS_RETCODE_OK) { print_sockerror ("get SO_SNDBUF"); return -2; } @@ -191,12 +202,16 @@ static int set_reuse_options (ddsrt_socket_t socket) /* Set REUSEADDR (if available on platform) for multicast sockets, leave unicast sockets alone. */ int one = 1; - - if (ddsrt_setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof (one)) != DDS_RETCODE_OK) - { + dds_retcode_t rc = ddsrt_setsockopt ( + socket, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof (one)); + if (rc == DDS_RETCODE_BAD_PARAMETER) { + DDS_LOG(DDS_LC_CONFIG, "cannot enable address reuse on socket\n"); + return 0; + } else if (rc != DDS_RETCODE_OK) { print_sockerror ("SO_REUSEADDR"); return -2; } + return 0; } @@ -266,7 +281,7 @@ static int set_mc_options_transmit_ipv4 (ddsrt_socket_t socket) unsigned char loop; dds_retcode_t ret; -#if defined __linux || defined __APPLE__ +#if (defined(__linux) || defined(__APPLE__)) && !LWIP_SOCKET if (config.use_multicast_if_mreqn) { struct ip_mreqn mreqn; @@ -470,7 +485,7 @@ int find_own_ip (const char *requested_address) continue; } -#ifdef __linux +#if defined(__linux) && !LWIP_SOCKET if (ifa->addr->sa_family == AF_PACKET) { /* FIXME: weirdo warning warranted */ diff --git a/src/core/ddsi/src/q_sockwaitset.c b/src/core/ddsi/src/q_sockwaitset.c index f8a761f..833312d 100644 --- a/src/core/ddsi/src/q_sockwaitset.c +++ b/src/core/ddsi/src/q_sockwaitset.c @@ -509,7 +509,7 @@ int os_sockWaitsetNextEvent (os_sockWaitsetCtx ctx, ddsi_tran_conn_t * conn) #define OSPL_PIPENAMESIZE 26 #endif -#ifndef _WIN32 +#if !_WIN32 && !LWIP_SOCKET #ifndef __VXWORKS__ #include @@ -524,7 +524,7 @@ int os_sockWaitsetNextEvent (os_sockWaitsetCtx ctx, ddsi_tran_conn_t * conn) #include #endif -#endif /* _WIN32 */ +#endif /* !_WIN32 && !LWIP_SOCKET */ typedef struct os_sockWaitsetSet { @@ -586,7 +586,7 @@ fail: closesocket (s2); return -1; } -#elif defined (VXWORKS_RTP) || defined (_WRS_KERNEL) +#elif defined(__VXWORKS__) static int make_pipe (int pfd[2]) { char pipename[OSPL_PIPENAMESIZE]; @@ -609,7 +609,7 @@ fail_open0: fail_pipedev: return -1; } -#else +#elif !defined(LWIP_SOCKET) static int make_pipe (int pfd[2]) { return pipe (pfd); @@ -644,7 +644,11 @@ os_sockWaitset os_sockWaitsetNew (void) ws->fdmax_plus_1 = FD_SETSIZE; #endif -#if defined (VXWORKS_RTP) || defined (_WRS_KERNEL) +#if defined(LWIP_SOCKET) + ws->pipe[0] = -1; + ws->pipe[1] = -1; + result = 0; +#elif defined(__VXWORKS__) int make_pipe (int pfd[2]) { char pipename[OSPL_PIPENAMESIZE]; @@ -679,15 +683,21 @@ os_sockWaitset os_sockWaitsetNew (void) assert (result != -1); (void) result; +#if !defined(LWIP_SOCKET) ws->set.fds[0] = ws->pipe[0]; +#else + ws->set.fds[0] = 0; +#endif ws->set.conns[0] = NULL; -#if ! defined (VXWORKS_RTP) && ! defined ( _WRS_KERNEL ) && ! defined (_WIN32) +#if !defined(__VXWORKS__) && !defined(_WIN32) && !defined(LWIP_SOCKET) fcntl (ws->pipe[0], F_SETFD, fcntl (ws->pipe[0], F_GETFD) | FD_CLOEXEC); fcntl (ws->pipe[1], F_SETFD, fcntl (ws->pipe[1], F_GETFD) | FD_CLOEXEC); #endif +#if !defined(LWIP_SOCKET) FD_SET (ws->set.fds[0], &ws->ctx.rdset); -#if ! defined (_WIN32) +#endif +#if !defined(_WIN32) ws->fdmax_plus_1 = ws->set.fds[0] + 1; #endif @@ -716,18 +726,18 @@ static void os_sockWaitsetFreeCtx (os_sockWaitsetCtx ctx) void os_sockWaitsetFree (os_sockWaitset ws) { -#ifdef VXWORKS_RTP +#if defined(__VXWORKS__) && defined(__RTP__) char nameBuf[OSPL_PIPENAMESIZE]; ioctl (ws->pipe[0], FIOGETNAME, &nameBuf); #endif -#if defined (_WIN32) +#if defined(_WIN32) closesocket (ws->pipe[0]); closesocket (ws->pipe[1]); -#else +#elif !defined(LWIP_SOCKET) close (ws->pipe[0]); close (ws->pipe[1]); #endif -#ifdef VXWORKS_RTP +#if defined(__VXWORKS__) && defined(__RTP__) pipeDevDelete ((char*) &nameBuf, 0); #endif os_sockWaitsetFreeSet (&ws->set); @@ -738,6 +748,9 @@ void os_sockWaitsetFree (os_sockWaitset ws) void os_sockWaitsetTrigger (os_sockWaitset ws) { +#if defined(LWIP_SOCKET) + (void)ws; +#else char buf = 0; int n; @@ -750,6 +763,7 @@ void os_sockWaitsetTrigger (os_sockWaitset ws) { DDS_WARNING("os_sockWaitsetTrigger: write failed on trigger pipe\n"); } +#endif } int os_sockWaitsetAdd (os_sockWaitset ws, ddsi_tran_conn_t conn) @@ -863,10 +877,19 @@ os_sockWaitsetCtx os_sockWaitsetWait (os_sockWaitset ws) rdset = &ctx->rdset; FD_ZERO (rdset); +#if !defined(LWIP_SOCKET) for (u = 0; u < dst->n; u++) { FD_SET (dst->fds[u], rdset); } +#else + for (u = 1; u < dst->n; u++) + { + DDSRT_WARNING_GNUC_OFF(sign-conversion) + FD_SET (dst->fds[u], rdset); + DDSRT_WARNING_GNUC_ON(sign-conversion) + } +#endif /* LWIP_SOCKET */ do { @@ -883,6 +906,7 @@ os_sockWaitsetCtx os_sockWaitsetWait (os_sockWaitset ws) { /* this simply skips the trigger fd */ ctx->index = 1; +#if ! defined(LWIP_SOCKET) if (FD_ISSET (dst->fds[0], rdset)) { char buf; @@ -898,19 +922,26 @@ os_sockWaitsetCtx os_sockWaitsetWait (os_sockWaitset ws) assert (0); } } +#endif /* LWIP_SOCKET */ return ctx; } return NULL; } +#if defined(LWIP_SOCKET) +DDSRT_WARNING_GNUC_OFF(sign-conversion) +#endif + int os_sockWaitsetNextEvent (os_sockWaitsetCtx ctx, ddsi_tran_conn_t * conn) { while (ctx->index < ctx->set.n) { unsigned idx = ctx->index++; ddsrt_socket_t fd = ctx->set.fds[idx]; +#if ! defined (LWIP_SOCKET) assert(idx > 0); +#endif if (FD_ISSET (fd, &ctx->rdset)) { *conn = ctx->set.conns[idx]; @@ -921,6 +952,10 @@ int os_sockWaitsetNextEvent (os_sockWaitsetCtx ctx, ddsi_tran_conn_t * conn) return -1; } +#if defined(LWIP_SOCKET) +DDSRT_WARNING_GNUC_ON(sign-conversion) +#endif + #else #error "no mode selected" #endif diff --git a/src/core/ddsi/src/sysdeps.c b/src/core/ddsi/src/sysdeps.c index 13fa3f1..c233f16 100644 --- a/src/core/ddsi/src/sysdeps.c +++ b/src/core/ddsi/src/sysdeps.c @@ -20,7 +20,7 @@ #include "dds/ddsi/q_config.h" #include "dds/ddsi/sysdeps.h" -#if !(defined __APPLE__ || defined __linux) || (__GNUC__ > 0 && (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40100) +#if DDSRT_WITH_FREERTOS || !(defined __APPLE__ || defined __linux) || (__GNUC__ > 0 && (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40100) void log_stacktrace (const char *name, ddsrt_thread_t tid) { DDSRT_UNUSED_ARG (name); diff --git a/src/ddsrt/CMakeLists.txt b/src/ddsrt/CMakeLists.txt index cda6f18..28069d0 100644 --- a/src/ddsrt/CMakeLists.txt +++ b/src/ddsrt/CMakeLists.txt @@ -11,12 +11,44 @@ # include(CheckCSourceCompiles) include(CheckLibraryExists) +include(GenerateDummyExportHeader) + +# Lightweight IP stack can be used on non-embedded targets too, but the +# runtime must be instructed to use it instead of the native stack. Of course +# for embedded targets there is no "native" stack and the runtime module must +# always be instructed to use an "alternative" stack. +option(WITH_LWIP "Use lightweight IP stack" OFF) +option(WITH_DNS "Enable domain name lookups" ON) +option(WITH_FREERTOS "Build for FreeRTOS" OFF) function(check_runtime_feature SOURCE_FILE) + get_target_property(_defs ddsrt INTERFACE_COMPILE_DEFINITIONS) + foreach(_def ${_defs}) + set(_strdefs "${_strdefs} -D${_def}") + endforeach() + + # Generate dummy export header required by feature tests. + generate_dummy_export_header( + ddsrt + BASE_NAME dds + EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/cmake/include/dds/export.h") + set(_strincs "${CMAKE_CURRENT_BINARY_DIR}/cmake/include") + + get_target_property(_incs ddsrt INTERFACE_INCLUDE_DIRECTORIES) + foreach(_inc ${_incs}) + set(_strincs "${_strincs};${_inc}") + endforeach() + + if(_strincs) + set(_strincs "-DINCLUDE_DIRECTORIES:STRING=${_strincs}") + endif() + set(expr "cmake_([_a-zA-Z0-9]+)=([_a-zA-Z0-9]+)") try_compile( foo "${CMAKE_BINARY_DIR}" SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE_FILE}" + CMAKE_FLAGS "${_strincs}" + COMPILE_DEFINITIONS "${_strdefs}" OUTPUT_VARIABLE output) string(REGEX MATCHALL "${expr}" matches "${output}") foreach(match ${matches}) @@ -26,7 +58,9 @@ function(check_runtime_feature SOURCE_FILE) endforeach() endfunction() -if(APPLE) +if(WITH_FREERTOS) + set(system_name freertos) +elseif(APPLE) set(system_name darwin) else() string(TOLOWER ${CMAKE_SYSTEM_NAME} system_name) @@ -38,6 +72,15 @@ endif() # ship an older version, so an interface library with public sources is used # as a workaround for now. add_library(ddsrt INTERFACE) + +foreach(opt WITH_LWIP WITH_DNS WITH_FREERTOS) + if(${opt}) + target_compile_definitions(ddsrt INTERFACE DDSRT_${opt}=1) + else() + target_compile_definitions(ddsrt INTERFACE DDSRT_${opt}=0) + endif() +endforeach() + target_include_directories( ddsrt INTERFACE "$" @@ -124,10 +167,9 @@ foreach(feature atomics cdtors environ heap ifaddrs random rusage # feature does not exist in cmake, the feature is expected to be # implemented for all targets. string(TOUPPER "${feature}" feature_uc) + set(HAVE_${feature_uc} TRUE) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${feature}.c") - check_runtime_feature(cmake/${feature}.c) - else() - set(HAVE_${feature_uc} TRUE) + check_runtime_feature("cmake/${feature}.c") endif() if(HAVE_${feature_uc}) @@ -137,7 +179,15 @@ foreach(feature atomics cdtors environ heap ifaddrs random rusage list(APPEND sources "${source_path}/${feature}.c") endif() set(system_exists FALSE) - foreach(system ${system_name} posix) + + # Allow custom implementations for a feature. e.g. lwip as opposed to + # windows or posix. + set(_system_name "${system_name}") + if(NOT HAVE_${feature_uc} MATCHES "[tT][rR][uU][eE]") + set(_system_name "${HAVE_${feature_uc}}") + endif() + + foreach(system ${_system_name} posix) # Headers that must remain private but are required by other runtime # source files must be located in src//dds/ddsrt. if(IS_DIRECTORY "${source_path}/${feature}/include") @@ -173,9 +223,13 @@ endforeach() target_sources(ddsrt INTERFACE ${sources}) -set(CMAKE_THREAD_PREFER_PTHREAD TRUE) -find_package(Threads REQUIRED) -target_link_libraries(ddsrt INTERFACE Threads::Threads) +set(HAVE_MULTI_PROCESS ${HAVE_MULTI_PROCESS} PARENT_SCOPE) + +if(NOT WITH_FREERTOS) + set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + find_package(Threads REQUIRED) + target_link_libraries(ddsrt INTERFACE Threads::Threads) +endif() if(WIN32) target_link_libraries(ddsrt INTERFACE wsock32 ws2_32 iphlpapi bcrypt) diff --git a/src/ddsrt/cmake/ifaddrs.c b/src/ddsrt/cmake/ifaddrs.c new file mode 100644 index 0000000..aaa61f5 --- /dev/null +++ b/src/ddsrt/cmake/ifaddrs.c @@ -0,0 +1,23 @@ +/* + * 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 "dds/ddsrt/ifaddrs.h" + +#if DDSRT_WITH_LWIP +# if LWIP_SOCKET +# error "cmake_HAVE_IFADDRS=lwip" +# else +# error "cmake_HAVE_IFADDRS=false" +# endif +#else +# error "cmake_HAVE_IFADDRS=true" +#endif + diff --git a/src/ddsrt/cmake/process.c b/src/ddsrt/cmake/process.c new file mode 100644 index 0000000..a1f3561 --- /dev/null +++ b/src/ddsrt/cmake/process.c @@ -0,0 +1,18 @@ +/* + * 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 "dds/ddsrt/process.h" + +#if DDSRT_HAVE_PROCESS +# error "cmake_HAVE_MULTI_PROCESS=true" +#else +# error "cmake_HAVE_MULTI_PROCESS=false" +#endif diff --git a/src/ddsrt/cmake/rusage.c b/src/ddsrt/cmake/rusage.c new file mode 100644 index 0000000..c9c6154 --- /dev/null +++ b/src/ddsrt/cmake/rusage.c @@ -0,0 +1,19 @@ +/* + * 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 "dds/ddsrt/rusage.h" + +#if DDSRT_HAVE_RUSAGE +# error "cmake_HAVE_RUSAGE=TRUE" +#else +# error "cmake_HAVE_RUSAGE=FALSE" +#endif + diff --git a/src/ddsrt/include/dds/ddsrt/endian.h b/src/ddsrt/include/dds/ddsrt/endian.h index a046b09..c1b831b 100644 --- a/src/ddsrt/include/dds/ddsrt/endian.h +++ b/src/ddsrt/include/dds/ddsrt/endian.h @@ -25,7 +25,15 @@ extern "C" { # else # define DDSRT_ENDIAN DDSRT_LITTLE_ENDIAN # endif -#else /* _WIN32 */ +/* _WIN32 */ +#elif defined(__IAR_SYSTEMS_ICC__) +# if __LITTLE_ENDIAN__ == 1 +# define DDSRT_ENDIAN DDSRT_LITTLE_ENDIAN +# else +# define DDSRT_ENDIAN DDSRT_BIG_ENDIAN +# endif +/* __IAR_SYSTEMS_ICC__ */ +#else # if defined(__BYTE_ORDER__) # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define DDSRT_ENDIAN DDSRT_BIG_ENDIAN diff --git a/src/ddsrt/include/dds/ddsrt/process.h b/src/ddsrt/include/dds/ddsrt/process.h index 45e7099..592508c 100644 --- a/src/ddsrt/include/dds/ddsrt/process.h +++ b/src/ddsrt/include/dds/ddsrt/process.h @@ -17,19 +17,30 @@ #include "dds/ddsrt/types.h" #include "dds/ddsrt/retcode.h" -#if defined(_WIN32) +#if DDSRT_WITH_FREERTOS +#include +#include +typedef TaskHandle_t ddsrt_pid_t; /* typedef void *TaskHandle_t */ +#define PRIdPID "p" +#define DDSRT_HAVE_MULTI_PROCESS 0 +/* DDSRT_WITH_FREERTOS */ +#elif defined(_WIN32) typedef DWORD ddsrt_pid_t; #define PRIdPID "u" -#else /* _WIN32 */ +#define DDSRT_HAVE_MULTI_PROCESS 1 +/* _WIN32 */ +#else #include #if defined(_WRS_KERNEL) typedef RTP_ID ddsrt_pid_t; /* typedef struct wind_rtp *RTP_ID */ #define PRIdPID PRIuPTR +#define DDSRT_HAVE_MULTI_PROCESS 0 #else typedef pid_t ddsrt_pid_t; #define PRIdPID "d" +#define DDSRT_HAVE_MULTI_PROCESS 1 +#endif #endif -#endif /* _WIN32 */ #if defined (__cplusplus) @@ -44,6 +55,7 @@ extern "C" { DDS_EXPORT ddsrt_pid_t ddsrt_getpid(void); +#if DDSRT_HAVE_MULTI_PROCESS /** * @brief Create new process. @@ -205,6 +217,7 @@ DDS_EXPORT dds_retcode_t ddsrt_proc_kill( ddsrt_pid_t pid); +#endif /* DDSRT_HAVE_MULTI_PROCESS */ #if defined (__cplusplus) } diff --git a/src/ddsrt/include/dds/ddsrt/rusage.h b/src/ddsrt/include/dds/ddsrt/rusage.h index 040ce2e..a58de6b 100644 --- a/src/ddsrt/include/dds/ddsrt/rusage.h +++ b/src/ddsrt/include/dds/ddsrt/rusage.h @@ -14,6 +14,18 @@ #include +#if DDSRT_WITH_FREERTOS +#include +# if configUSE_TRACE_FACILITY == 1 && \ + configGENERATE_RUN_TIME_STATS == 1 +# define DDSRT_HAVE_RUSAGE 1 +# else +# define DDSRT_HAVE_RUSAGE 0 +#endif +#else +# define DDSRT_HAVE_RUSAGE 1 +#endif + #include "dds/ddsrt/time.h" #include "dds/ddsrt/retcode.h" diff --git a/src/ddsrt/include/dds/ddsrt/sockets.h b/src/ddsrt/include/dds/ddsrt/sockets.h index ec5efa3..3876320 100644 --- a/src/ddsrt/include/dds/ddsrt/sockets.h +++ b/src/ddsrt/include/dds/ddsrt/sockets.h @@ -3,6 +3,10 @@ #include +#if !defined(DDSRT_WITH_DNS) +# define DDSRT_WITH_DNS 1 +#endif + #include "dds/export.h" #include "dds/ddsrt/types.h" #include "dds/ddsrt/attributes.h" diff --git a/src/ddsrt/include/dds/ddsrt/sockets/posix.h b/src/ddsrt/include/dds/ddsrt/sockets/posix.h index a9697af..a6f9574 100644 --- a/src/ddsrt/include/dds/ddsrt/sockets/posix.h +++ b/src/ddsrt/include/dds/ddsrt/sockets/posix.h @@ -12,12 +12,17 @@ #ifndef DDSRT_SOCKETS_POSIX_H #define DDSRT_SOCKETS_POSIX_H +#if DDSRT_WITH_LWIP +#include +#include +#else #include #include #include #include #include #include +#endif #if defined(__cplusplus) extern "C" { @@ -27,21 +32,38 @@ typedef int ddsrt_socket_t; #define DDSRT_INVALID_SOCKET (-1) #define PRIdSOCK "d" -#define DDSRT_HAVE_IPV6 1 -#define DDSRT_HAVE_DNS 1 -#define DDSRT_HAVE_SSM 1 +#if LWIP_SOCKET +# if LWIP_IPV6 +# define DDSRT_HAVE_IPV6 1 +# endif +# if LWIP_DNS && LWIP_SOCKET +# define DDSRT_HAVE_DNS DDSRT_WITH_DNS +# endif +# define DDSRT_HAVE_SSM 0 + +# define IFF_UP 0x1 +# define IFF_BROADCAST 0x2 +# define IFF_LOOPBACK 0x8 +# define IFF_POINTOPOINT 0x10 +# define IFF_MULTICAST 0x1000 +#else /* LWIP_SOCKET */ +# define DDSRT_HAVE_IPV6 1 +# define DDSRT_HAVE_DNS DDSRT_WITH_DNS +# define DDSRT_HAVE_SSM 1 +#endif /* LWIP_SOCKET */ typedef struct iovec ddsrt_iovec_t; typedef size_t ddsrt_iov_len_t; -#if defined(__linux) +#if defined(__linux) && !LWIP_SOCKET typedef size_t ddsrt_msg_iovlen_t; #else /* POSIX says int (which macOS, FreeBSD, Solaris do) */ typedef int ddsrt_msg_iovlen_t; #endif typedef struct msghdr ddsrt_msghdr_t; -#if defined(__sun) && !defined(_XPG4_2) +#if (defined(__sun) && !defined(_XPG4_2)) || \ + (defined(LWIP_SOCKET)) # define DDSRT_MSGHDR_FLAGS 0 #else # define DDSRT_MSGHDR_FLAGS 1 diff --git a/src/ddsrt/include/dds/ddsrt/sockets/windows.h b/src/ddsrt/include/dds/ddsrt/sockets/windows.h index 277dfe7..1735d39 100644 --- a/src/ddsrt/include/dds/ddsrt/sockets/windows.h +++ b/src/ddsrt/include/dds/ddsrt/sockets/windows.h @@ -13,7 +13,7 @@ typedef SOCKET ddsrt_socket_t; #define PRIdSOCK PRIuPTR #define DDSRT_HAVE_IPV6 1 -#define DDSRT_HAVE_DNS 1 +#define DDSRT_HAVE_DNS DDSRT_WITH_DNS #if defined(NTDDI_VERSION) && \ defined(_WIN32_WINNT_WS03) && \ diff --git a/src/ddsrt/include/dds/ddsrt/sync.h b/src/ddsrt/include/dds/ddsrt/sync.h index 8228aab..f071237 100644 --- a/src/ddsrt/include/dds/ddsrt/sync.h +++ b/src/ddsrt/include/dds/ddsrt/sync.h @@ -18,7 +18,9 @@ #include "dds/ddsrt/retcode.h" #include "dds/ddsrt/attributes.h" -#if _WIN32 +#if DDSRT_WITH_FREERTOS +#include "dds/ddsrt/sync/freertos.h" +#elif _WIN32 #include "dds/ddsrt/sync/windows.h" #else #include "dds/ddsrt/sync/posix.h" diff --git a/src/ddsrt/include/dds/ddsrt/sync/freertos.h b/src/ddsrt/include/dds/ddsrt/sync/freertos.h new file mode 100644 index 0000000..4d05578 --- /dev/null +++ b/src/ddsrt/include/dds/ddsrt/sync/freertos.h @@ -0,0 +1,93 @@ +/* + * 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 + */ +#ifndef DDSRT_SYNC_FREERTOS_H +#define DDSRT_SYNC_FREERTOS_H + +#include +#include +#include +#include + +#include "dds/ddsrt/atomics.h" + +#if (INCLUDE_vTaskSuspend != 1) +/* INCLUDE_vTaskSuspend must be set to 1 to make xSemaphoreTake wait + indefinitely when passed portMAX_DELAY. See reference manual. */ +#error "INCLUDE_vTaskSuspend != 1 in FreeRTOSConfig.h" +#endif + +#if defined (__cplusplus) +extern "C" { +#endif + +typedef struct { + SemaphoreHandle_t sem; +} ddsrt_mutex_t; + +typedef struct { + size_t len; + size_t cnt; + size_t off; + size_t end; + TaskHandle_t *tasks; +} ddsrt_tasklist_t; + +typedef struct { + SemaphoreHandle_t sem; + ddsrt_tasklist_t tasks; +} ddsrt_cond_t; + +/* This readers-writer lock implementation does not prefer writers over readers + or vice versa. Multiple readers are allowed to hold the lock simultaneously + and can acquire it directly if no writers are queued. However, if a writer + is queued, new readers and writers are queued behind it in order. Any reader + that acquires the lock after a writer frees it, notifies the next task. If + that task tries to acquire a write lock it waits until the reader frees the + lock. However, if the task tries to acquire a read lock it will succeed, and + notify the next task, etc. */ +typedef struct { + SemaphoreHandle_t sem; + ddsrt_tasklist_t tasks; + int32_t state; + uint32_t cnt; + uint32_t rdcnt; + uint32_t wrcnt; +} ddsrt_rwlock_t; + +typedef ddsrt_atomic_uint32_t ddsrt_once_t; +#define DDSRT_ONCE_INIT { .v = (1<<0) /* ONCE_NOT_STARTED */ } + + +/* The declarations below are here for tests and must be considered private. */ + +/* Number of buckets to grow buffer by. */ +#define DDSRT_TASKLIST_CHUNK (5) +/* Number of buckets to allocate initially. */ +#define DDSRT_TASKLIST_INITIAL (DDSRT_TASKLIST_CHUNK * 2) + +int ddsrt_tasklist_init(ddsrt_tasklist_t *list); +void ddsrt_tasklist_fini(ddsrt_tasklist_t *list); +void ddsrt_tasklist_ltrim(ddsrt_tasklist_t *list); +void ddsrt_tasklist_rtrim(ddsrt_tasklist_t *list); +void ddsrt_tasklist_pack(ddsrt_tasklist_t *list); +int ddsrt_tasklist_shrink(ddsrt_tasklist_t *list); +int ddsrt_tasklist_grow(ddsrt_tasklist_t *list); +ssize_t ddsrt_tasklist_find(ddsrt_tasklist_t *list, TaskHandle_t task); +TaskHandle_t ddsrt_tasklist_peek(ddsrt_tasklist_t *list, TaskHandle_t task); +TaskHandle_t ddsrt_tasklist_pop(ddsrt_tasklist_t *list, TaskHandle_t task); +int ddsrt_tasklist_push(ddsrt_tasklist_t *list, TaskHandle_t task); + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSRT_SYNC_FREERTOS_H */ diff --git a/src/ddsrt/include/dds/ddsrt/threads.h b/src/ddsrt/include/dds/ddsrt/threads.h index 7497002..32f49bd 100644 --- a/src/ddsrt/include/dds/ddsrt/threads.h +++ b/src/ddsrt/include/dds/ddsrt/threads.h @@ -25,7 +25,9 @@ #include "dds/ddsrt/attributes.h" #include "dds/ddsrt/retcode.h" -#if _WIN32 +#if DDSRT_WITH_FREERTOS +#include "dds/ddsrt/threads/freertos.h" +#elif _WIN32 #include "dds/ddsrt/threads/windows.h" #else #include "dds/ddsrt/threads/posix.h" @@ -206,9 +208,11 @@ ddsrt_thread_getname( * * @param[in] name Name for current thread. */ +#if DDSRT_HAVE_THREAD_SETNAME DDS_EXPORT void ddsrt_thread_setname( const char *__restrict name); +#endif /** * @brief Push cleanup handler onto the cleanup stack diff --git a/src/ddsrt/include/dds/ddsrt/threads/freertos.h b/src/ddsrt/include/dds/ddsrt/threads/freertos.h new file mode 100644 index 0000000..73b2c7a --- /dev/null +++ b/src/ddsrt/include/dds/ddsrt/threads/freertos.h @@ -0,0 +1,35 @@ +/* + * 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 + */ +#ifndef DDSRT_THREADS_FREERTOS_H +#define DDSRT_THREADS_FREERTOS_H + +#include +#include + +#define DDSRT_HAVE_THREAD_SETNAME (0) + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct { + TaskHandle_t task; +} ddsrt_thread_t; + +typedef UBaseType_t ddsrt_tid_t; +#define PRIdTID "lu" + +#if defined(__cplusplus) +} +#endif + +#endif /* DDSRT_THREADS_FREERTOS_H */ diff --git a/src/ddsrt/include/dds/ddsrt/threads/posix.h b/src/ddsrt/include/dds/ddsrt/threads/posix.h index 63d193d..0d15c27 100644 --- a/src/ddsrt/include/dds/ddsrt/threads/posix.h +++ b/src/ddsrt/include/dds/ddsrt/threads/posix.h @@ -14,6 +14,12 @@ #include +#if defined(__VXWORKS__) +#define DDSRT_HAVE_THREAD_SETNAME (0) +#else +#define DDSRT_HAVE_THREAD_SETNAME (1) +#endif + #if defined (__cplusplus) extern "C" { #endif diff --git a/src/ddsrt/include/dds/ddsrt/threads/windows.h b/src/ddsrt/include/dds/ddsrt/threads/windows.h index e7eda64..3354d65 100644 --- a/src/ddsrt/include/dds/ddsrt/threads/windows.h +++ b/src/ddsrt/include/dds/ddsrt/threads/windows.h @@ -14,6 +14,8 @@ #include "dds/ddsrt/types.h" +#define DDSRT_HAVE_THREAD_SETNAME (1) + #if defined (__cplusplus) extern "C" { #endif diff --git a/src/ddsrt/include/dds/ddsrt/time.h b/src/ddsrt/include/dds/ddsrt/time.h index 37fcfa3..a474fb4 100644 --- a/src/ddsrt/include/dds/ddsrt/time.h +++ b/src/ddsrt/include/dds/ddsrt/time.h @@ -146,4 +146,8 @@ DDS_EXPORT size_t ddsrt_ctime(dds_time_t abstime, char *str, size_t size); } #endif +#if DDSRT_WITH_FREERTOS +#include "dds/ddsrt/time/freertos.h" +#endif + #endif /* DDSRT_TIME_H */ diff --git a/src/ddsrt/include/dds/ddsrt/time/freertos.h b/src/ddsrt/include/dds/ddsrt/time/freertos.h new file mode 100644 index 0000000..09525ee --- /dev/null +++ b/src/ddsrt/include/dds/ddsrt/time/freertos.h @@ -0,0 +1,53 @@ +/* + * 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 + */ +#ifndef DDSRT_TIME_FREERTOS_H +#define DDSRT_TIME_FREERTOS_H + +#include +#include + +#if defined (__cplusplus) +extern "C" { +#endif + +#define DDSRT_NSECS_PER_TICK (DDS_NSECS_IN_SEC / configTICK_RATE_HZ) + +inline TickType_t +ddsrt_duration_to_ticks_ceil( + dds_duration_t reltime) +{ + TickType_t ticks = 0; + + assert(portMAX_DELAY > configTICK_RATE_HZ); + + if (reltime == DDS_INFINITY) { + ticks = portMAX_DELAY; + } else if (reltime > 0) { + dds_duration_t max_nsecs = + (DDS_INFINITY / DDSRT_NSECS_PER_TICK < portMAX_DELAY + ? DDS_INFINITY - 1 : portMAX_DELAY * DDSRT_NSECS_PER_TICK); + + if (reltime > max_nsecs - (DDSRT_NSECS_PER_TICK - 1)) { + ticks = portMAX_DELAY; + } else { + ticks = (TickType_t)((reltime + (DDSRT_NSECS_PER_TICK - 1)) / DDSRT_NSECS_PER_TICK); + } + } + + return ticks; +} + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSRT_TIME_FREERTOS_H */ diff --git a/src/ddsrt/include/dds/ddsrt/types/posix.h b/src/ddsrt/include/dds/ddsrt/types/posix.h index 6088737..1344dc1 100644 --- a/src/ddsrt/include/dds/ddsrt/types/posix.h +++ b/src/ddsrt/include/dds/ddsrt/types/posix.h @@ -14,6 +14,10 @@ #include #include +#if defined(__IAR_SYSTEMS_ICC__) +typedef long int ssize_t; +#else #include +#endif #endif /* DDSRT_TYPES_POSIX_H */ diff --git a/src/ddsrt/src/heap/freertos/heap.c b/src/ddsrt/src/heap/freertos/heap.c new file mode 100644 index 0000000..01782c4 --- /dev/null +++ b/src/ddsrt/src/heap/freertos/heap.c @@ -0,0 +1,136 @@ +/* + * 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 + +#if defined(configSUPPORT_DYNAMIC_ALLOCATION) && \ + (configSUPPORT_DYNAMIC_ALLOCATION == 0) +# error Dynamic memory allocation is not supported +#endif + +#include +#include +#include +#include + +static const size_t ofst = sizeof(size_t); + +void *ddsrt_malloc_s(size_t size) +{ + void *ptr = NULL; + + if (size == 0) { + size = 1; + } + + if ((SIZE_MAX - size) < ofst) { + errno = ERANGE; + } else { + ptr = pvPortMalloc(size + ofst); + if (ptr == NULL) { + errno = ENOMEM; + } else { + *((size_t *)ptr) = size; + ptr += ofst; + } + } + + return ptr; +} + +void *ddsrt_malloc(size_t size) +{ + void *ptr; + + if ((ptr = ddsrt_malloc_s(size)) == NULL) { + abort(); + } + + return ptr; +} + +void *ddsrt_calloc_s(size_t nmemb, size_t size) +{ + void *ptr = NULL; + + if (nmemb == 0 || size == 0) { + nmemb = size = 1; + } + + if ((SIZE_MAX / nmemb) <= size) { + errno = ERANGE; + } else { + ptr = ddsrt_malloc_s(nmemb * size); + (void)memset(ptr, 0, nmemb * size); + } + + return ptr; +} + +void *ddsrt_calloc(size_t nmemb, size_t size) +{ + void *ptr = NULL; + + if ((ptr = ddsrt_calloc_s(nmemb, size)) == NULL) { + abort(); + } + + return ptr; +} + +/* pvPortMalloc may be used instead of directly invoking malloc and free as + offered by the standard C library. Unfortunately FreeRTOS does not offer a + realloc compatible function and extra information must be embedded in every + memory block in order to support reallocation of memory (otherwise the + number of bytes that must be copied is unavailable). */ +void *ddsrt_realloc_s(void *memblk, size_t size) +{ + void *ptr = NULL; + size_t origsize = 0; + + if (memblk != NULL) { + origsize = *((size_t *)(memblk - ofst)); + } + + if (size != origsize || origsize == 0) { + if ((ptr = ddsrt_malloc_s(size)) == NULL) { + return NULL; + } + if (memblk != NULL) { + if (size > 0) { + (void)memcpy(ptr, memblk, size > origsize ? origsize : size); + } + vPortFree(memblk - ofst); + } + memblk = ptr; + } + + return memblk; +} + +void *ddsrt_realloc(void *memblk, size_t size) +{ + void *ptr = NULL; + + if ((ptr = ddsrt_realloc_s(memblk, size)) == NULL) { + abort(); + } + + return ptr; +} + +void +ddsrt_free(void *ptr) +{ + if (ptr != NULL) { + vPortFree(ptr - ofst); + } +} diff --git a/src/ddsrt/src/ifaddrs/lwip/ifaddrs.c b/src/ddsrt/src/ifaddrs/lwip/ifaddrs.c new file mode 100644 index 0000000..9d1e64e --- /dev/null +++ b/src/ddsrt/src/ifaddrs/lwip/ifaddrs.c @@ -0,0 +1,207 @@ +/* + * 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 +#include +#include +#include /* netif_list */ +#include + +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/io.h" +#include "dds/ddsrt/ifaddrs.h" +#include "dds/ddsrt/retcode.h" +#include "dds/ddsrt/string.h" + +extern const int *const os_supp_afs; + +static uint32_t +getflags( + const struct netif *netif, + const ip_addr_t *addr) +{ + uint32_t flags = 0; + + if (netif->flags & NETIF_FLAG_UP) { + flags |= IFF_UP; + } + if (netif->flags & NETIF_FLAG_BROADCAST) { + flags |= IFF_BROADCAST; + } + if (netif->flags & NETIF_FLAG_IGMP) { + flags |= IFF_MULTICAST; + } + if (ip_addr_isloopback(addr)) { + flags |= IFF_LOOPBACK; + } + + return flags; +} + +static void +sockaddr_from_ip_addr( + struct sockaddr *sockaddr, + const ip_addr_t *addr) +{ + if (IP_IS_V4(addr)) { + memset(sockaddr, 0, sizeof(struct sockaddr_in)); + ((struct sockaddr_in *)sockaddr)->sin_len = sizeof(struct sockaddr_in); + ((struct sockaddr_in *)sockaddr)->sin_family = AF_INET; + inet_addr_from_ip4addr(&((struct sockaddr_in *)sockaddr)->sin_addr, addr); +#if DDSRT_HAVE_IPV6 + } else { + assert(IP_IS_V6(addr)); + memset(sockaddr, 0, sizeof(struct sockaddr_in6)); + ((struct sockaddr_in6 *)sockaddr)->sin6_len = sizeof(struct sockaddr_in6); + ((struct sockaddr_in6 *)sockaddr)->sin6_family = AF_INET6; + inet6_addr_from_ip6addr(&((struct sockaddr_in6 *)sockaddr)->sin6_addr, addr); +#endif + } +} + +static dds_retcode_t +copyaddr( + ddsrt_ifaddrs_t **ifap, + const struct netif *netif, + const ip_addr_t *addr) +{ + dds_retcode_t rc = DDS_RETCODE_OK; + ddsrt_ifaddrs_t *ifa; + struct sockaddr_storage sa; + + assert(ifap != NULL); + assert(netif != NULL); + assert(addr != NULL); + + sockaddr_from_ip_addr((struct sockaddr *)&sa, addr); + + /* Network interface name is of the form "et0", where the first two letters + are the "name" field and the digit is the num field of the netif + structure as described in lwip/netif.h */ + + if ((ifa = ddsrt_calloc_s(1, sizeof(*ifa))) == NULL || + (ifa->addr = ddsrt_memdup(&sa, sa.s2_len)) == NULL || + (ddsrt_asprintf(&ifa->name, "%s%d", netif->name, netif->num) == -1)) + { + rc = DDS_RETCODE_OUT_OF_RESOURCES; + } else { + ifa->flags = getflags(netif, addr); + ifa->index = netif->num; + + if (IP_IS_V4(addr)) { + static const size_t sz = sizeof(struct sockaddr_in); + if ((ifa->netmask = ddsrt_calloc_s(1, sz)) == NULL || + (ifa->broadaddr = ddsrt_calloc_s(1, sz)) == NULL) + { + rc = DDS_RETCODE_OUT_OF_RESOURCES; + } else { + ip_addr_t broadaddr = IPADDR4_INIT( + ip_2_ip4(&netif->ip_addr)->addr | + ip_2_ip4(&netif->netmask)->addr); + + sockaddr_from_ip_addr((struct sockaddr*)ifa->netmask, &netif->netmask); + sockaddr_from_ip_addr((struct sockaddr*)ifa->broadaddr, &broadaddr); + } + } + } + + if (rc == DDS_RETCODE_OK) { + *ifap = ifa; + } else { + ddsrt_freeifaddrs(ifa); + } + + return rc; +} + +dds_retcode_t +ddsrt_getifaddrs( + ddsrt_ifaddrs_t **ifap, + const int *afs) +{ + dds_retcode_t rc = DDS_RETCODE_OK; + int use_ip4, use_ip6; + struct netif *netif; + ddsrt_ifaddrs_t *ifa, *next_ifa, *root_ifa; + + assert(ifap != NULL); + + if (afs == NULL) { + afs = os_supp_afs; + } + + use_ip4 = use_ip6 = 0; + for (int i = 0; afs[i] != DDSRT_AF_TERM; i++) { + if (afs[i] == AF_INET) { + use_ip4 = 1; + } else if (afs[i] == AF_INET6) { + use_ip6 = 1; + } + } + + ifa = next_ifa = root_ifa = NULL; + + for (netif = netif_list; + netif != NULL && rc == DDS_RETCODE_OK; + netif = netif->next) + { + if (use_ip4 && IP_IS_V4(&netif->ip_addr)) { + rc = copyaddr(&next_ifa, netif, &netif->ip_addr); + if (rc == DDS_RETCODE_OK) { + if (ifa == NULL) { + ifa = root_ifa = next_ifa; + } else { + ifa->next = next_ifa; + ifa = next_ifa; + } + } + } + +#if DDSRT_HAVE_IPV6 + if (use_ip6) { + int pref = 1; +again: + /* List preferred IPv6 address first. */ + for (int i = 0; + i < LWIP_IPV6_NUM_ADDRESSES && rc == DDS_RETCODE_OK; + i++) + { + if ((ip6_addr_ispreferred(netif->ip_addr_state[i]) && pref) || + (ip6_addr_isvalid(netif->ip_addr_state[i]) && !pref)) + { + rc = copyaddr(&next_ifa, netif, &netif->ip_addr[i]); + if (rc == DDS_RETCODE_OK) { + if (ifa == NULL) { + ifa = root_ifa = next_ifa; + } else { + ifa->next = next_ifa; + ifa = next_ifa; + } + } + } + } + + if (rc == DDS_RETCODE_OK && pref) { + pref = 0; + goto again; + } + } +#endif + } + + if (rc == DDS_RETCODE_OK) { + *ifap = ifa; + } else { + ddsrt_freeifaddrs(root_ifa); + } + + return rc; +} diff --git a/src/ddsrt/src/process/freertos/process.c b/src/ddsrt/src/process/freertos/process.c new file mode 100644 index 0000000..b299888 --- /dev/null +++ b/src/ddsrt/src/process/freertos/process.c @@ -0,0 +1,22 @@ +/* + * 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 "dds/ddsrt/process.h" + +#include +#include + +ddsrt_pid_t +ddsrt_getpid(void) +{ + return xTaskGetCurrentTaskHandle(); +} + diff --git a/src/ddsrt/src/rusage/freertos/rusage.c b/src/ddsrt/src/rusage/freertos/rusage.c new file mode 100644 index 0000000..1f9d2f3 --- /dev/null +++ b/src/ddsrt/src/rusage/freertos/rusage.c @@ -0,0 +1,96 @@ +/* + * 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 +#include +#include + +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/rusage.h" + +/* Task CPU time statistics require a high resolution timer. FreeRTOS + recommends a time base between 10 and 100 times faster than the tick + interrupt (https://www.freertos.org/rtos-run-time-stats.html), but does not + define a macro or function to retrieve the base. */ + +/* Require time base to be defined for conversion to nanoseconds. */ + +#define DDSRT_NSECS_IN_RUSAGE_TIME_BASE (1) /* FIXME: Make configurable! */ + +#if !defined(DDSRT_NSECS_IN_RUSAGE_TIME_BASE) +#error "Time base for run time stats is not defined" +#endif + +static dds_retcode_t +rusage_self(ddsrt_rusage_t *usage) +{ + dds_retcode_t rc = DDS_RETCODE_OK; + dds_duration_t nsecs; + UBaseType_t cnt, len; + TaskStatus_t *states = NULL, *ptr; + size_t size; + + do { + len = uxTaskGetNumberOfTasks(); + size = len * sizeof(*states); + if ((ptr = ddsrt_realloc_s(states, size)) == NULL) { + rc = DDS_RETCODE_OUT_OF_RESOURCES; + } else { + states = ptr; + /* uxTaskGetSystemState returns 0 if the TaskStatus_t buffer is not + sufficiently large enough. */ + cnt = uxTaskGetSystemState(states, len, NULL); + } + } while (rc == DDS_RETCODE_OK && cnt == 0); + + if (rc == DDS_RETCODE_OK) { + memset(usage, 0, sizeof(*usage)); + + for (len = cnt, cnt = 0; cnt < len; cnt++) { + nsecs = states[cnt].ulRunTimeCounter * DDSRT_NSECS_IN_RUSAGE_TIME_BASE; + usage->stime += nsecs; /* FIXME: Protect against possible overflow! */ + } + } + + ddsrt_free(states); + + return rc; +} + +static dds_retcode_t +rusage_thread(ddsrt_rusage_t *usage) +{ + TaskStatus_t states; + + memset(usage, 0, sizeof(*usage)); + memset(&states, 0, sizeof(states)); + vTaskGetInfo(xTaskGetCurrentTaskHandle(), &states, pdFALSE, eInvalid); + usage->stime = states.ulRunTimeCounter * DDSRT_NSECS_IN_RUSAGE_TIME_BASE; + + return DDS_RETCODE_OK; +} + +dds_retcode_t +ddsrt_getrusage(int who, ddsrt_rusage_t *usage) +{ + dds_retcode_t rc; + + assert(who == DDSRT_RUSAGE_SELF || who == DDSRT_RUSAGE_THREAD); + assert(usage != NULL); + + if (who == DDSRT_RUSAGE_THREAD) { + rc = rusage_thread(usage); + } else { + rc = rusage_self(usage); + } + + return rc; +} diff --git a/src/ddsrt/src/sockets.c b/src/ddsrt/src/sockets.c index d559a80..48e3282 100644 --- a/src/ddsrt/src/sockets.c +++ b/src/ddsrt/src/sockets.c @@ -15,19 +15,21 @@ #include #include -#if !defined(_WIN32) -#include -#include -#include -# if defined(__linux) -# include /* sockaddr_ll */ -# endif /* __linux */ -#endif /* _WIN32 */ - #include "dds/ddsrt/heap.h" #include "dds/ddsrt/log.h" #include "dds/ddsrt/sockets_priv.h" +#if !LWIP_SOCKET +# if !defined(_WIN32) +# include +# include +# include +# if defined(__linux) +# include /* sockaddr_ll */ +# endif /* __linux */ +# endif /* _WIN32 */ +#endif /* LWIP_SOCKET */ + extern inline struct timeval * ddsrt_duration_to_timeval_ceil(dds_duration_t reltime, struct timeval *tv); @@ -37,7 +39,7 @@ const struct in6_addr ddsrt_in6addr_loopback = IN6ADDR_LOOPBACK_INIT; #endif const int afs[] = { -#ifdef __linux +#if defined(__linux) && !LWIP_SOCKET AF_PACKET, #endif /* __linux */ #if DDSRT_HAVE_IPV6 @@ -62,7 +64,7 @@ ddsrt_sockaddr_get_size(const struct sockaddr *const sa) sz = sizeof(struct sockaddr_in6); break; #endif /* DDSRT_HAVE_IPV6 */ -#ifdef __linux +#if defined(__linux) && !LWIP_SOCKET case AF_PACKET: sz = sizeof(struct sockaddr_ll); break; @@ -218,6 +220,9 @@ dds_retcode_t ddsrt_sockaddrtostr(const void *sa, char *buf, size_t size) assert(sa != NULL); assert(buf != NULL); +#if LWIP_SOCKET +DDSRT_WARNING_GNUC_OFF(sign-conversion) +#endif switch (((struct sockaddr *)sa)->sa_family) { case AF_INET: ptr = inet_ntop( @@ -232,6 +237,9 @@ dds_retcode_t ddsrt_sockaddrtostr(const void *sa, char *buf, size_t size) default: return DDS_RETCODE_BAD_PARAMETER; } +#if LWIP_SOCKET +DDSRT_WARNING_GNUC_ON(sign-conversion) +#endif if (ptr == NULL) { return DDS_RETCODE_NOT_ENOUGH_SPACE; @@ -312,10 +320,14 @@ ddsrt_gethostbyname(const char *name, int af, ddsrt_hostent_t **hentp) /* Other system error. */ return DDS_RETCODE_ERROR; #endif +#if defined(EAI_BADFLAGS) case EAI_BADFLAGS: /* Invalid flags in hints.ai_flags. */ +#endif case EAI_FAMILY: /* Address family not supported. */ case EAI_SERVICE: /* Service not available for socket type. */ +#if defined(EAI_SOCKTYPE) case EAI_SOCKTYPE: /* Socket type not supported. */ +#endif case 0: { struct addrinfo *ai; size_t addrno, naddrs, size; diff --git a/src/ddsrt/src/sockets/include/dds/ddsrt/sockets_priv.h b/src/ddsrt/src/sockets/include/dds/ddsrt/sockets_priv.h index 8a41203..6c793d3 100644 --- a/src/ddsrt/src/sockets/include/dds/ddsrt/sockets_priv.h +++ b/src/ddsrt/src/sockets/include/dds/ddsrt/sockets_priv.h @@ -52,10 +52,10 @@ ddsrt_duration_to_timeval_ceil(dds_duration_t reltime, struct timeval *tv) return NULL; } else if (reltime > 0) { dds_duration_t max_nsecs; - if (DDS_INFINITY > DDSRT_TIME_T_MAX) { - assert(DDSRT_TIME_T_MAX == INT32_MAX); + if (DDSRT_TIME_T_MAX == INT32_MAX) { max_nsecs = INT32_MAX * DDS_NSECS_IN_SEC; } else { + assert(DDSRT_TIME_T_MAX == INT64_MAX); max_nsecs = DDSRT_TIME_T_MAX / DDS_NSECS_IN_SEC; } diff --git a/src/ddsrt/src/sockets/posix/gethostname.c b/src/ddsrt/src/sockets/posix/gethostname.c index de71a44..fbdd7c4 100644 --- a/src/ddsrt/src/sockets/posix/gethostname.c +++ b/src/ddsrt/src/sockets/posix/gethostname.c @@ -10,21 +10,41 @@ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ #include -#include #include #include +#include "dds/ddsrt/sockets.h" +#include "dds/ddsrt/string.h" + +#if !LWIP_SOCKET +#include +#endif + #if defined(__VXWORKS__) #include #endif /* __VXWORKS__ */ -#if !defined(HOST_NAME_MAX) && defined(_POSIX_HOST_NAME_MAX) -# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#if !defined(HOST_NAME_MAX) +# if LWIP_SOCKET +# define HOST_NAME_MAX DNS_MAX_NAME_LENGTH +# elif defined(_POSIX_HOST_NAME_MAX) +# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +# endif #endif -#include "dds/ddsrt/sockets.h" -#include "dds/ddsrt/string.h" +#if LWIP_SOCKET +dds_retcode_t +ddsrt_gethostname( + char *name, + size_t len) +{ + if (ddsrt_strlcpy(name, "localhost", len) >= len) { + return DDS_RETCODE_NOT_ENOUGH_SPACE; + } + return DDS_RETCODE_OK; +} +#else dds_retcode_t ddsrt_gethostname( char *name, @@ -59,3 +79,4 @@ ddsrt_gethostname( return DDS_RETCODE_ERROR; } +#endif diff --git a/src/ddsrt/src/sockets/posix/socket.c b/src/ddsrt/src/sockets/posix/socket.c index 94b7f89..7334483 100644 --- a/src/ddsrt/src/sockets/posix/socket.c +++ b/src/ddsrt/src/sockets/posix/socket.c @@ -10,10 +10,14 @@ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ #include -#include #include #include +#include "dds/ddsrt/log.h" +#include "dds/ddsrt/misc.h" +#include "dds/ddsrt/sockets_priv.h" + +#if !LWIP_SOCKET #if defined(__VXWORKS__) #include #include @@ -21,6 +25,7 @@ #else #include #endif /* __VXWORKS__ */ +#include #include #include #ifdef __sun @@ -30,10 +35,7 @@ #ifdef __APPLE__ #include #endif /* __APPLE__ */ - -#include "dds/ddsrt/log.h" -#include "dds/ddsrt/misc.h" -#include "dds/ddsrt/sockets_priv.h" +#endif /* LWIP_SOCKET */ dds_retcode_t ddsrt_socket(ddsrt_socket_t *sockptr, int domain, int type, int protocol) @@ -254,6 +256,15 @@ ddsrt_getsockopt( void *optval, socklen_t *optlen) { +#if LWIP_SOCKET + if (optname == SO_SNDBUF || optname == SO_RCVBUF) + return DDS_RETCODE_BAD_PARAMETER; +# if !SO_REUSE + if (optname == SO_REUSEADDR) + return DDS_RETCODE_BAD_PARAMETER; +# endif /* SO_REUSE */ +#endif /* LWIP_SOCKET */ + if (getsockopt(sock, level, optname, optval, optlen) == 0) return DDS_RETCODE_OK; @@ -279,6 +290,15 @@ ddsrt_setsockopt( const void *optval, socklen_t optlen) { +#if LWIP_SOCKET + if (optname == SO_SNDBUF || optname == SO_RCVBUF) + return DDS_RETCODE_BAD_PARAMETER; +# if !SO_REUSE + if (optname == SO_REUSEADDR) + return DDS_RETCODE_BAD_PARAMETER; +# endif /* SO_REUSE */ +#endif /* LWIP_SOCKET */ + switch (optname) { case SO_SNDBUF: case SO_RCVBUF: @@ -405,6 +425,24 @@ ddsrt_recv( return recv_error_to_retcode(errno); } +#if LWIP_SOCKET && !defined(recvmsg) +static ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) +{ + assert(msg->msg_iovlen == 1); + assert(msg->msg_controllen == 0); + + msg->msg_flags = 0; + + return recvfrom( + sockfd, + msg->msg_iov[0].iov_base, + msg->msg_iov[0].iov_len, + flags, + msg->msg_name, + &msg->msg_namelen); +} +#endif /* LWIP_SOCKET */ + dds_retcode_t ddsrt_recvmsg( ddsrt_socket_t sock, diff --git a/src/ddsrt/src/sync/freertos/sync.c b/src/ddsrt/src/sync/freertos/sync.c new file mode 100644 index 0000000..6b75f7f --- /dev/null +++ b/src/ddsrt/src/sync/freertos/sync.c @@ -0,0 +1,469 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/log.h" +#include "dds/ddsrt/sync.h" +#include "dds/ddsrt/timeconv.h" + +void ddsrt_mutex_init(ddsrt_mutex_t *mutex) +{ + SemaphoreHandle_t sem; + + assert(mutex != NULL); + + if ((sem = xSemaphoreCreateMutex()) == NULL) { + abort(); + } + + (void)memset(mutex, 0, sizeof(*mutex)); + mutex->sem = sem; +} + +void ddsrt_mutex_destroy(ddsrt_mutex_t *mutex) +{ + assert(mutex != NULL); + + vSemaphoreDelete(mutex->sem); + (void)memset(mutex, 0, sizeof(*mutex)); +} + +static bool +mutex_lock(ddsrt_mutex_t *mutex, int blk) +{ + assert(mutex != NULL); + + if (xSemaphoreTake(mutex->sem, (blk == 1 ? portMAX_DELAY : 0)) != pdPASS) { + DDS_TRACE("Failed to lock 0x%p", mutex); + /* xSemaphoreTake will only return pdFAIL on timeout. The wait will be + indefinite if INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h + and portMAX_DELAY was passed. */ + assert(blk == 0); + return false; + } + + return true; +} + +void ddsrt_mutex_lock(ddsrt_mutex_t *mutex) +{ + if (!mutex_lock(mutex, 1)) { + abort(); + } +} + +bool ddsrt_mutex_trylock(ddsrt_mutex_t *mutex) +{ + return mutex_lock(mutex, 0); +} + +void ddsrt_mutex_unlock(ddsrt_mutex_t *mutex) +{ + assert(mutex != NULL); + + if (xSemaphoreGive(mutex->sem) != pdPASS) { + DDS_TRACE("Failed to unlock 0x%p", mutex->sem); + abort(); + } +} + +static dds_retcode_t +cond_timedwait( + ddsrt_cond_t *cond, + ddsrt_mutex_t *mutex, + dds_duration_t reltime) +{ + dds_retcode_t rc = DDS_RETCODE_OK; + dds_time_t abstime; + TaskHandle_t task; + TickType_t ticks = 0; + + assert(cond != NULL); + assert(mutex != NULL); + + abstime = ddsrt_time_add_duration(dds_time(), reltime); + ticks = ddsrt_duration_to_ticks_ceil(reltime); + + xSemaphoreTake(cond->sem, portMAX_DELAY); + ddsrt_mutex_unlock(mutex); + + task = xTaskGetCurrentTaskHandle(); + /* Register current task with condition. */ + ddsrt_tasklist_push(&cond->tasks, task); + /* Discard pending notifications. */ + ulTaskNotifyTake(1, 0); + + xSemaphoreGive(cond->sem); + /* Wait to be notified. */ + switch (ulTaskNotifyTake(1, ticks)) { + case 0: + xSemaphoreTake(cond->sem, ticks); + ddsrt_tasklist_pop(&cond->tasks, task); + xSemaphoreGive(cond->sem); + break; + default: + /* Task already removed from condition. */ + break; + } + + /* Timeout must only be returned if the time has actually passed. */ + if (dds_time() >= abstime) { + rc = DDS_RETCODE_TIMEOUT; + } + + ddsrt_mutex_lock(mutex); + + return rc; +} + +void ddsrt_cond_init(ddsrt_cond_t *cond) +{ + SemaphoreHandle_t sem; + ddsrt_tasklist_t tasks; + + assert(cond != NULL); + + if (ddsrt_tasklist_init(&tasks) == -1) { + abort(); + } + if ((sem = xSemaphoreCreateMutex()) == NULL) { + ddsrt_tasklist_fini(&tasks); + abort(); + } + + (void)memset(cond, 0, sizeof(*cond)); + cond->sem = sem; + cond->tasks = tasks; +} + +void ddsrt_cond_destroy(ddsrt_cond_t *cond) +{ + assert(cond != NULL); + + vSemaphoreDelete(cond->sem); + ddsrt_tasklist_fini(&cond->tasks); + (void)memset(cond, 0, sizeof(*cond)); +} + +void ddsrt_cond_wait(ddsrt_cond_t *cond, ddsrt_mutex_t *mutex) +{ + assert(cond != NULL); + assert(mutex != NULL); + + (void)cond_timedwait(cond, mutex, DDS_INFINITY); +} + +bool +ddsrt_cond_waitfor( + ddsrt_cond_t *cond, + ddsrt_mutex_t *mutex, + dds_duration_t reltime) +{ + dds_retcode_t rc; + + assert(cond != NULL); + assert(mutex != NULL); + + switch ((rc = cond_timedwait(cond, mutex, reltime))) { + case DDS_RETCODE_OUT_OF_RESOURCES: + abort(); + case DDS_RETCODE_TIMEOUT: + return false; + default: + assert(rc == DDS_RETCODE_OK); + break; + } + + return true; +} + +bool +ddsrt_cond_waituntil( + ddsrt_cond_t *cond, + ddsrt_mutex_t *mutex, + dds_time_t abstime) +{ + dds_retcode_t rc; + dds_time_t time; + dds_duration_t reltime; + + assert(cond != NULL); + assert(mutex != NULL); + + time = dds_time(); + reltime = (abstime > time ? abstime - time : 0); + + switch ((rc = cond_timedwait(cond, mutex, reltime))) { + case DDS_RETCODE_OUT_OF_RESOURCES: + abort(); + case DDS_RETCODE_TIMEOUT: + return false; + default: + assert(rc == DDS_RETCODE_OK); + break; + } + + return true; +} + +void ddsrt_cond_signal(ddsrt_cond_t *cond) +{ + TaskHandle_t task; + + assert(cond != NULL); + + xSemaphoreTake(cond->sem, portMAX_DELAY); + if ((task = ddsrt_tasklist_pop(&cond->tasks, NULL)) != NULL) { + xTaskNotifyGive(task); + } + xSemaphoreGive(cond->sem); +} + +void ddsrt_cond_broadcast(ddsrt_cond_t *cond) +{ + TaskHandle_t task; + + assert(cond != NULL); + + xSemaphoreTake(cond->sem, portMAX_DELAY); + while ((task = ddsrt_tasklist_pop(&cond->tasks, NULL)) != NULL) { + xTaskNotifyGive(task); + } + xSemaphoreGive(cond->sem); +} + +#define WRITE_LOCKED (-1) +#define UNLOCKED (0) +#define READ_LOCKED (1) + +void ddsrt_rwlock_init(ddsrt_rwlock_t *rwlock) +{ + SemaphoreHandle_t sem; + ddsrt_tasklist_t tasks; + + assert(rwlock != NULL); + + if (ddsrt_tasklist_init(&tasks) == -1) { + abort(); + } + if ((sem = xSemaphoreCreateMutex()) == NULL) { + ddsrt_tasklist_fini(&tasks); + abort(); + } + + memset(rwlock, 0, sizeof(*rwlock)); + rwlock->sem = sem; + rwlock->tasks = tasks; + rwlock->state = UNLOCKED; +} + +void ddsrt_rwlock_destroy(ddsrt_rwlock_t *rwlock) +{ + assert(rwlock != NULL); + + vSemaphoreDelete(rwlock->sem); + ddsrt_tasklist_fini(&rwlock->tasks); + memset(rwlock, 0, sizeof(*rwlock)); +} + +void ddsrt_rwlock_read(ddsrt_rwlock_t *rwlock) +{ + TaskHandle_t task = xTaskGetCurrentTaskHandle(); + + assert(rwlock != NULL); + + xSemaphoreTake(rwlock->sem, portMAX_DELAY); + rwlock->rdcnt++; + if (rwlock->wrcnt != 0) { + ddsrt_tasklist_push(&rwlock->tasks, task); + /* Discard pending notifications. */ + ulTaskNotifyTake(1, 0); + xSemaphoreGive(rwlock->sem); + /* Wait to be notified. */ + ulTaskNotifyTake(1, portMAX_DELAY); + xSemaphoreTake(rwlock->sem, portMAX_DELAY); + ddsrt_tasklist_pop(&rwlock->tasks, task); + } + assert(rwlock->state == UNLOCKED || + rwlock->state == READ_LOCKED); + rwlock->cnt++; + rwlock->state = READ_LOCKED; + /* Notify next task, if any. */ + if ((task = ddsrt_tasklist_peek(&rwlock->tasks, NULL)) != NULL) { + xTaskNotifyGive(task); + } + xSemaphoreGive(rwlock->sem); +} + +void ddsrt_rwlock_write(ddsrt_rwlock_t *rwlock) +{ + TaskHandle_t task = xTaskGetCurrentTaskHandle(); + + assert(rwlock != NULL); + + xSemaphoreTake(rwlock->sem, portMAX_DELAY); + rwlock->wrcnt++; + if (rwlock->rdcnt != 0 || rwlock->wrcnt != 1) { + ddsrt_tasklist_push(&rwlock->tasks, task); + do { + /* Discard pending notifications. */ + ulTaskNotifyTake(1, 0); + xSemaphoreGive(rwlock->sem); + /* Wait to be notified. */ + ulTaskNotifyTake(1, portMAX_DELAY); + xSemaphoreTake(rwlock->sem, portMAX_DELAY); + } while (rwlock->state != UNLOCKED); + ddsrt_tasklist_pop(&rwlock->tasks, task); + } + assert(rwlock->cnt == 0); + assert(rwlock->state == UNLOCKED); + rwlock->cnt++; + rwlock->state = WRITE_LOCKED; + xSemaphoreGive(rwlock->sem); +} + +bool ddsrt_rwlock_tryread(ddsrt_rwlock_t *rwlock) +{ + bool locked = false; + TaskHandle_t task; + + assert(rwlock != NULL); + + xSemaphoreTake(rwlock->sem, portMAX_DELAY); + if (rwlock->wrcnt == 0) { + locked = true; + rwlock->cnt++; + rwlock->rdcnt++; + rwlock->state = READ_LOCKED; + /* Notify next task, if any. */ + if ((task = ddsrt_tasklist_peek(&rwlock->tasks, NULL)) != NULL) { + xTaskNotifyGive(task); + } + } + xSemaphoreGive(rwlock->sem); + + return locked; +} + +bool ddsrt_rwlock_trywrite(ddsrt_rwlock_t *rwlock) +{ + bool locked = false; + + assert(rwlock != NULL); + + xSemaphoreTake(rwlock->sem, 0); + if (rwlock->rdcnt == 0 && rwlock->wrcnt == 0) { + locked = true; + rwlock->cnt++; + rwlock->wrcnt++; + rwlock->state = WRITE_LOCKED; + } + xSemaphoreGive(rwlock->sem); + + return locked; +} + +void ddsrt_rwlock_unlock(ddsrt_rwlock_t *rwlock) +{ + TaskHandle_t task; + + assert(rwlock != NULL); + + xSemaphoreTake(rwlock->sem, portMAX_DELAY); + assert(rwlock->cnt != 0); + rwlock->cnt--; + if (rwlock->state == READ_LOCKED) { + assert(rwlock->rdcnt != 0); + rwlock->rdcnt--; + if (rwlock->rdcnt == 0) { + rwlock->state = UNLOCKED; + } + } else { + assert(rwlock->state == WRITE_LOCKED); + assert(rwlock->wrcnt != 0); + assert(rwlock->cnt == 0); + rwlock->wrcnt--; + rwlock->state = UNLOCKED; + } + /* Notify next task, if any. */ + if ((rwlock->state == UNLOCKED) && + (task = ddsrt_tasklist_peek(&rwlock->tasks, NULL)) != NULL) + { + assert(rwlock->rdcnt != 0 || + rwlock->wrcnt != 0); + xTaskNotifyGive(task); + } + xSemaphoreGive(rwlock->sem); +} + +#define ONCE_NOT_STARTED (1<<0) +#define ONCE_IN_PROGRESS (1<<1) +#define ONCE_FINISHED (1<<2) + +/* Wait one millisecond (tick) between polls. */ +static const TickType_t once_delay = (configTICK_RATE_HZ / 1000); + +void +ddsrt_once( + ddsrt_once_t *control, + ddsrt_once_fn init_fn) +{ + int ret, brk = 0; + uint32_t stat; + + while (brk == 0) { + stat = ddsrt_atomic_ld32(control); + /* Verify once control was initialized properly. */ + assert(stat == ONCE_NOT_STARTED || + stat == ONCE_IN_PROGRESS || + stat == ONCE_FINISHED); + + if ((stat & ONCE_FINISHED) != 0) { + /* The initialization function has been executed. No reason to block + execution of this thread. Continue. */ + brk = 1; + } else if ((stat & ONCE_IN_PROGRESS) != 0) { + /* Another thread is executing the initialization function. Wait around + for it to be finished. The polling loop is required because FreeRTOS + does not offer futexes. */ + vTaskDelay(once_delay); + /* Repeat. */ + } else { + /* No thread was executing the initialization function (one might be + executing it now) at the time of the load. If the atomic compare and + swap operation is successful, this thread will run the initialization + function. */ + if (ddsrt_atomic_cas32( + control, ONCE_NOT_STARTED, ONCE_IN_PROGRESS) != 0) + { + /* Function must never block or yield, see reference manual. */ + init_fn(); + + ret = (0 == ddsrt_atomic_cas32( + control, ONCE_IN_PROGRESS, ONCE_FINISHED)); + assert(ret == 0); (void)ret; + + brk = 1; + } else { + /* Another thread updated the state first. Repeat. */ + } + } + } + + return; +} diff --git a/src/ddsrt/src/sync/freertos/tasklist.c b/src/ddsrt/src/sync/freertos/tasklist.c new file mode 100644 index 0000000..6d03415 --- /dev/null +++ b/src/ddsrt/src/sync/freertos/tasklist.c @@ -0,0 +1,396 @@ +/* + * 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 +#include +#include + +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/sync.h" + +/* Task list is a buffer used to keep track of blocked tasks. The buffer is + cyclic to avoid memory (re)allocation as much as possible. To avoid memory + relocation, the window is allowed to be sparse too. + + Active buckets must always reside in the window denoted by the first active + bucket and the last active bucket. + + A buffer with 10 buckets will be neatly packed at first. + + X : Used bucket in window. + o : Empty (invalidated) bucket. + + ----------------------- + | X X X X X o o o o o | length: 10, count: 5 + --^-------^------------ + 1st nth + + As soon as the first task is unblocked. + + ----------------------- + | o X X X X o o o o o | length: 10, count: 4 + ----^-----^------------ + 1st nth + + After a while the window will wrap around. + + ----------------------- + | X X X o o o o o X X | length: 10, count: 5 + ------^-----------^---- + nth 1st + + When a task is popped, e.g. a task was not notified in due time. + + ----------------------- + | o X X o X X o o o o | length: 10, count: 4 + ----^-------^---------- + 1st nth +*/ + +#ifndef NDEBUG + +static void tasklist_assert(ddsrt_tasklist_t *list) +{ + size_t i; + + assert(list != NULL); + + if (list->cnt == 0) { + assert(list->off == 0); + assert(list->end == 0); + assert(list->len == DDSRT_TASKLIST_INITIAL); + for (i = 0; i < list->len; i++) { + assert(list->tasks[i] == NULL); + } + } + + /* FIXME: add more checks */ +} +#else +#define tasklist_assert(...) +#endif /* NDEBUG */ + +int ddsrt_tasklist_init(ddsrt_tasklist_t *list) +{ + TaskHandle_t *p; + + assert(list != NULL); + + p = ddsrt_malloc(DDSRT_TASKLIST_INITIAL * sizeof(*list->tasks)); + if (p == NULL) { + return -1; + } + + memset(list, 0, sizeof(*list)); + memset(p, 0, DDSRT_TASKLIST_INITIAL * sizeof(*list->tasks)); + list->tasks = p; + list->len = DDSRT_TASKLIST_INITIAL; + + return 0; +} + +void ddsrt_tasklist_fini(ddsrt_tasklist_t *list) +{ + ddsrt_free(list->tasks); + memset(list, 0, sizeof(*list)); +} + +void ddsrt_tasklist_ltrim(ddsrt_tasklist_t *list) +{ + size_t i; + + assert(list != NULL); + assert(list->cnt != 0); + + i = list->off; + for (; i < list->len - 1 && list->tasks[i] == NULL; i++) { } + /* Take into account wrap around. */ + if (list->tasks[i] == NULL) { + assert(i == list->len - 1); + assert(list->off > list->end); + i = 0; + /* Trim invalidated buckets from head. */ + for (; i < list->len - 1 && list->tasks[i] == NULL; i++) { } + } + list->off = i; +} + +void ddsrt_tasklist_rtrim(ddsrt_tasklist_t *list) +{ + size_t i; + + assert(list != NULL); + assert(list->cnt != 0); + + i = list->end; + for (; i > 0 && list->tasks[i] == NULL; i--) { } + /* Take into account wrap around. */ + if (list->tasks[i] == NULL) { + assert(i == 0); + assert(list->off > list->end); + i = list->len - 1; + /* Trim invalidated buckets from tail. */ + for (; i > 0 && list->tasks[i] == NULL; i--) { } + } + list->end = i; +} + +void ddsrt_tasklist_pack(ddsrt_tasklist_t *list) +{ + size_t i, j; + + /* Pack operation is trickier on wrap around. */ + if (list->end < list->off) { + /* Compress tail. + * + * ------------------------- ----------------------- + * | c . d . e | . a . b . | >> | c d e . . | . a . b | + * ------------------------- ----------------------- + */ + for (i = j = 0; i <= list->end; i++) { + if (list->tasks[i] != NULL) { + if (i != j) { + list->tasks[j] = list->tasks[i]; + } + j++; + } + } + + assert(j != 0); + list->end = (j == 0 ? 0 : j - 1); + + /* Compress head. + * + * ------------------------- ------------------------- + * | c d e . . | . a . b . | >> | c d e . . | . . . a b | + * ------------------------- ------------------------- + */ + for (i = j = list->len - 1; i >= list->off; i--) { + if (list->tasks[i] != NULL) { + if (i != j) { + list->tasks[j] = list->tasks[i]; + } + j--; + } + } + + assert(j != list->len - 1); + list->off = (j == list->len - 1 ? list->len - 1 : j + 1); + } else { + /* Compress. + * + * ------------------------- -------------------------- + * | . . a . . | b . c d e | >> | a b c d e | . . . . . | + * ------------------------- -------------------------- + */ + for (i = list->off, j = 0; i <= list->end; i++) { + if (list->tasks[i] != NULL) { + if (i != j) { + list->tasks[j] = list->tasks[i]; + } + j++; + } + } + assert(j != 0); + list->off = 0; + list->end = j - 1; + assert(list->end == list->cnt - 1); + } +} + +int ddsrt_tasklist_shrink(ddsrt_tasklist_t *list) +{ + static const size_t x = DDSRT_TASKLIST_CHUNK; + TaskHandle_t *p; + size_t mv = 0, n; + + assert(list != NULL); + + /* Shrink by one chunk too, but only if the difference is at least two + chunks to avoid memory (re)allocation if a task is pushed and popped + just over the boundary. */ + if (list->cnt > (list->len - (x * 2)) || (list->len - x) < DDSRT_TASKLIST_INITIAL) + { + return 0; + } + + /* List can be sparse. Pack to ensure list can be compacted. */ + ddsrt_tasklist_pack(list); + + /* Pack operation moved head to end of buffer on wrap around. Move head back + to not discard it on reallocation. */ + if (list->off != 0) { + assert(list->end < list->off); + mv = (list->len - list->off) * sizeof(*p); + memmove(list->tasks + (list->off - x), list->tasks + list->off, mv); + list->off -= x; + } + + n = list->len - x; + if ((p = ddsrt_realloc(list->tasks, n * sizeof(*p))) == NULL) { + /* Move head back to end of buffer. */ + if (mv != 0) { + memmove(list->tasks + (list->off + x), list->tasks + list->off, mv); + list->off += x; + } + return -1; + } + + list->tasks = p; + list->len = n; + + return 0; +} + +int ddsrt_tasklist_grow(ddsrt_tasklist_t *list) +{ + static const size_t x = DDSRT_TASKLIST_CHUNK; + TaskHandle_t *p; + size_t n; + + assert(list != NULL); + /* Should not be called if room is available. */ + assert(list->cnt == list->len); + + n = list->len + x; + if ((p = ddsrt_realloc(list->tasks, n * sizeof(*p))) == NULL) { + return -1; + } + + /* Move head to end of newly allocated memory. */ + if (list->off != 0) { + assert(list->end < list->off); + memmove(p + (list->off + x), p + list->off, (list->len - list->off) * sizeof(*p)); + list->off += x; + } + + /* Zero newly allocated memory. */ + memset(p + (list->end + 1), 0, x * sizeof(*p)); + + list->tasks = p; + list->len = n; + + return 0; +} + +ssize_t ddsrt_tasklist_find(ddsrt_tasklist_t *list, TaskHandle_t task) +{ + size_t i, n; + + assert(task != NULL); + + /* No need to check if list is empty. */ + if (list->cnt != 0) { + /* Task list is circular, so window does not have to be consecutive. */ + n = list->off <= list->end ? list->end : list->len - 1; + for (i = list->off; i <= n; i++) { + if (list->tasks[i] == task) + return (ssize_t)i; + } + + if (list->off > list->end) { + n = list->end; + for (i = 0; i <= n; i++) { + if (list->tasks[i] == task) + return (ssize_t)i; + } + } + } + + return -1; +} + +TaskHandle_t ddsrt_tasklist_peek(ddsrt_tasklist_t *list, TaskHandle_t task) +{ + tasklist_assert(list); + + if (list->cnt == 0) { + return NULL; + } else if (task != NULL) { + return ddsrt_tasklist_find(list, task) == -1 ? NULL : task; + } + + return list->tasks[list->off]; +} + +TaskHandle_t ddsrt_tasklist_pop(ddsrt_tasklist_t *list, TaskHandle_t task) +{ + ssize_t i; + + tasklist_assert(list); + + if (list->cnt == 0) { + return NULL; + } else if (task == NULL) { + i = (ssize_t)list->off; + } else if ((i = ddsrt_tasklist_find(list, task)) == -1) { + return NULL; + } + + task = list->tasks[i]; + if (task != NULL) { + /* Invalidate bucket. */ + list->tasks[i] = NULL; + list->cnt--; + + if (list->cnt == 0) { + list->off = list->end = 0; + } else if (i == (ssize_t)list->end) { + /* Trim invalidated buckets from tail of window. */ + ddsrt_tasklist_rtrim(list); + } else if (i == (ssize_t)list->off) { + /* Trim invalidated buckets from head of window. */ + ddsrt_tasklist_ltrim(list); + } else { + /* Window is now sparse. */ + } + + if (list->cnt <= (list->len - DDSRT_TASKLIST_CHUNK*2)) { + /* Shrink operation failure can safely be ignored. */ + (void)ddsrt_tasklist_shrink(list); + } + } + + return task; +} + +int ddsrt_tasklist_push(ddsrt_tasklist_t *list, TaskHandle_t task) +{ + tasklist_assert(list); + assert(task != NULL); + + /* Ensure task is not listed. */ + if (ddsrt_tasklist_find(list, task) != -1) { + return 0; + } + /* Grow number of buckets if none are available. */ + if (list->cnt == list->len) { + if (ddsrt_tasklist_grow(list) == -1) { + return -1; + } + list->end++; + /* Wrap around if there is room at the head. */ + } else if (list->end == list->len - 1 && list->off != 0) { + list->end = 0; + } else { + /* List can be sparse. */ + if (list->end == list->len - 1 || list->end + 1 == list->off) { + ddsrt_tasklist_pack(list); + } + /* Room is guaranteed to be available at the tail. */ + list->end += (list->cnt > 0); + } + + list->tasks[list->end] = task; + list->cnt++; + + return 0; +} diff --git a/src/ddsrt/src/threads/freertos/threads.c b/src/ddsrt/src/threads/freertos/threads.c new file mode 100644 index 0000000..20a536e --- /dev/null +++ b/src/ddsrt/src/threads/freertos/threads.c @@ -0,0 +1,545 @@ +/* + * 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 +#include +#include + +#include "dds/ddsrt/heap.h" +#include "dds/ddsrt/retcode.h" +#include "dds/ddsrt/string.h" +#include "dds/ddsrt/sync.h" +#include "dds/ddsrt/threads_priv.h" + +typedef enum { + THREAD_STARTING = 0, + THREAD_RUNNING, + THREAD_EXITING /* Indicates the thread has returned from the specified + start_routine, but FreeRTOS may not report it as deleted + yet. */ +} thread_state_t; + +typedef struct { + ddsrt_thread_routine_t func; + void *arg; + TaskHandle_t task; /* Thread identifier for looking up thread context from + another thread. Read-only, read by other threads. */ + thread_state_t stat; + thread_cleanup_t *dtors; /* Cleanup routines. Private. */ + uint32_t ret; /* Return value. NULL if thread has not terminated, maybe + NULL if thread has terminated, read by other thread(s) + after termination. */ + TaskHandle_t blkd; /* Thread blocked until thread terminates. may or may + not be empty when thread terminates. Written by other + thread, protected by registry mutex. */ +} thread_context_t; + +/* Thread registry (in combination with thread context) is required to properly + implement thread join functionality. */ + +/* Threads have their own context. The context is automatically allocated and + initialized, either when the thread is created (local threads) or when the + API is first used. */ + +/* FIXME: The same mechanism more-or-less exists in DDSI, perhaps more of the + logic in DDSI can be moved down at some point? */ +typedef struct { + ddsrt_mutex_t mutex; + /* The number of available spaces in the thread context array does not have + to equal the number of used spaces. e.g. when a memory allocation for a + new thread context array fails when destroying a context it is better to + leave one space unused. */ + thread_context_t **ctxs; + size_t cnt; + size_t len; +} thread_registry_t; + +static ddsrt_thread_local thread_context_t *thread_context = NULL; + +static thread_registry_t thread_registry; + +static ddsrt_once_t thread_registry_once = DDSRT_ONCE_INIT; + +static uint32_t non_local_thread(void *arg) { (void)arg; return 0;} + +/* FreeRTOS documentation states vTaskGetInfo is intended for debugging because + its use results in the scheduler remaining suspended for an extended period, + but the scheduler is only suspended if eTaskState is not eInvalid. */ +ddsrt_tid_t +ddsrt_gettid(void) +{ + TaskStatus_t status; + + vTaskGetInfo(xTaskGetCurrentTaskHandle(), &status, pdFALSE, eInvalid); + + return status.xTaskNumber; +} + +ddsrt_thread_t +ddsrt_thread_self(void) +{ + ddsrt_thread_t thr = { .task = xTaskGetCurrentTaskHandle() }; + + return thr; +} + +bool ddsrt_thread_equal(ddsrt_thread_t a, ddsrt_thread_t b) +{ + return (a.task == b.task); +} + +size_t +ddsrt_thread_getname(char *__restrict name, size_t size) +{ + char *ptr; + + assert(name != NULL); + assert(size >= 1); + + if ((ptr = pcTaskGetName(NULL)) == NULL) { + ptr = ""; + } + + return ddsrt_strlcpy(name, ptr, size); +} + +static void +thread_registry_init(void) +{ + /* One time initialization guaranteed by ddsrt_once. */ + (void)memset(&thread_registry, 0, sizeof(thread_registry)); + ddsrt_mutex_init(&thread_registry.mutex); +} + +static thread_context_t * +thread_context_find(TaskHandle_t task) +{ + thread_context_t *ctx = NULL; + + for (size_t i = 0; i < thread_registry.cnt && ctx == NULL; i++) { + if (thread_registry.ctxs[i] != NULL && + thread_registry.ctxs[i]->task == task) + { + ctx = thread_registry.ctxs[i]; + } + } + + return ctx; +} + +static dds_retcode_t +thread_context_create(thread_context_t **ctxptr) +{ + dds_retcode_t rc = DDS_RETCODE_OK; + size_t len; + thread_context_t *ctx = NULL, **ctxs = NULL; + + assert(ctxptr != NULL); + + ctx = ddsrt_calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + rc = DDS_RETCODE_OUT_OF_RESOURCES; + } else { + if (thread_registry.cnt < thread_registry.len) { + len = thread_registry.len; + ctxs = thread_registry.ctxs; + } else { + assert(thread_registry.cnt == thread_registry.len); + len = thread_registry.len + 1; + ctxs = ddsrt_realloc(thread_registry.ctxs, len * sizeof(ctx)); + } + + if (ctxs == NULL) { + ddsrt_free(ctx); + rc = DDS_RETCODE_OUT_OF_RESOURCES; + } else { + ctxs[thread_registry.cnt++] = *ctxptr = ctx; + thread_registry.len = len; + thread_registry.ctxs = ctxs; + } + } + + return rc; +} + +#define thread_context_require() thread_context_acquire(NULL) + +static dds_retcode_t +thread_context_acquire(thread_context_t **ctxptr) +{ + dds_retcode_t rc = DDS_RETCODE_OK; + thread_context_t *ctx = thread_context; + + if (ctx == NULL) { + /* Dynamically initialize global thread registry (exactly once). */ + ddsrt_once(&thread_registry_once, &thread_registry_init); + + ddsrt_mutex_lock(&thread_registry.mutex); + if ((rc = thread_context_create(&ctx)) == 0) { + /* This situation only arises for non-native (not created in our code) + threads. Some members must therefore still be initialized to ensure + proper operation. */ + ctx->func = &non_local_thread; + ctx->stat = THREAD_RUNNING; + ctx->task = xTaskGetCurrentTaskHandle(); + } + ddsrt_mutex_unlock(&thread_registry.mutex); + thread_context = ctx; + } else { + assert(ctx->func != NULL); + assert(ctx->stat == THREAD_RUNNING); + assert(ctx->task == xTaskGetCurrentTaskHandle()); + } + + if (rc == DDS_RETCODE_OK && ctxptr != NULL) { + assert(ctx != NULL); + *ctxptr = ctx; + } + + return rc; +} + +static void +thread_context_destroy(thread_context_t *ctx) +{ + size_t i = 0; + thread_context_t **arr; + + if (ctx != NULL) { + while (i < thread_registry.cnt && thread_registry.ctxs[i] != ctx) { + i++; + } + + if (i < thread_registry.cnt) { + thread_registry.ctxs[i] = NULL; + if (i < (thread_registry.cnt - 1)) { + (void)memmove( + thread_registry.ctxs + (i), + thread_registry.ctxs + (i+1), + (thread_registry.cnt - (i+1)) * sizeof(*thread_registry.ctxs)); + } + thread_registry.cnt--; + + /* Free contexts when count reaches zero. */ + if (thread_registry.cnt == 0) { + ddsrt_free(thread_registry.ctxs); + thread_registry.ctxs = NULL; + thread_registry.len = 0; + } else { + arr = ddsrt_realloc( + thread_registry.ctxs, + thread_registry.cnt * sizeof(*thread_registry.ctxs)); + /* Ignore allocation failure, save free spot. */ + if (arr != NULL) { + thread_registry.ctxs = arr; + thread_registry.len = thread_registry.cnt; + } + } + } + + ddsrt_free(ctx); + } +} + +static void +thread_fini(thread_context_t *ctx, uint32_t ret) +{ + thread_cleanup_t *tail; + + assert(ctx != NULL); + + /* Acquire registry lock to publish task result and state. */ + ddsrt_mutex_lock(&thread_registry.mutex); + + /* Pop all cleanup handlers from the thread's cleanup stack. */ + while ((tail = ctx->dtors) != NULL) { + ctx->dtors = tail->prev; + if (tail->routine != 0) { + tail->routine(tail->arg); + } + ddsrt_free(tail); + } + + /* FreeRTOS can report task state, but doesn't register the result or + notifies a thread that wants to join. */ + ctx->ret = ret; + ctx->stat = THREAD_EXITING; + + /* Thread resources will be leaked (especially for non-local threads) + if not reclaimed by a thread join. Local threads (threads created + within the DDS stack) are required to be joined. Thread resource + leakage for local threads must be considered a bug. Non-local + threads, however, are not aware that there are resources that must + be reclaimed and local threads might not be aware that there are + non-local threads that must be joined. Therefore, if a non-local thread + exits, it's resources are reclaimed if no thread is waiting to join. */ + if (ctx->blkd != NULL) { + /* Task join functionality is based on notifications, as it is + significantly faster than using a queue, semaphore or event group to + perform an equivalent operation. + + When a task receives a notification, it's notification state is set to + pending. When it reads it's notification state, the notification state + is set to not-pending. A task can wait, with an optional time out, for + it's notification state to become pending. */ + + /* Ignore result, there's nothing that can be done on failure and it always + returns pdPASS. */ + (void)xTaskNotifyGive(ctx->blkd); + } else if (ctx->func == &non_local_thread) { + assert(ret == 0); + thread_context_destroy(ctx); + } + + ddsrt_mutex_unlock(&thread_registry.mutex); +} + +static void +thread_start_routine(void *arg) +{ + thread_context_t *ctx = (thread_context_t *)arg; + uint32_t ret; + + ddsrt_mutex_lock(&thread_registry.mutex); + /* Context for the current task is always correctly initialized and + registered at this stage. It's not strictly required to update task + state, but the synchronization itself is. */ + ctx->stat = THREAD_RUNNING; + ddsrt_mutex_unlock(&thread_registry.mutex); + + /* Thread-local storage is initialized by the function that creates the + thread because a reference to the thread's context is stored and + synchronization is considerably easier if it's handled there. */ + + thread_context = ctx; + ret = ctx->func(ctx->arg); + + thread_fini(ctx, ret); /* DO NOT DEREFERENCE THREAD CONTEXT ANYMORE! */ + + /* Delete current task. */ + vTaskDelete(NULL); +} + +/* xTaskCreate takes the stack depth in the number of words (NOT bytes). In + practice this simply means it multiplies the given number with the size + of StackType_t in bytes (see tasks.c). FreeRTOSConfig.h must define + configMINIMAL_STACK_SIZE, which is the stack size in words allocated for + the idle task. */ +#define WORD_SIZE (sizeof(StackType_t)) +/* configMINIMAL_STACK_SIZE is applied as the default stack size. Whether or + not this is considered a sane default depends on the target. The default can + be adjusted in FreeRTOSConfig.h Of course the configuration file also allows + the user to change it on a per-thread basis at runtime. */ +#define MIN_STACK_SIZE ((uint16_t)(configMINIMAL_STACK_SIZE * WORD_SIZE)) + +dds_retcode_t +ddsrt_thread_create( + ddsrt_thread_t *thread, + const char *name, + const ddsrt_threadattr_t *attr, + ddsrt_thread_routine_t start_routine, + void *arg) +{ + dds_retcode_t rc; + TaskHandle_t task; + UBaseType_t prio; + uint16_t size = MIN_STACK_SIZE; + thread_context_t *ctx = NULL; + + assert(thread != NULL); + assert(name != NULL); + assert(attr != NULL); + assert(start_routine != 0); + + if ((rc = thread_context_require()) != DDS_RETCODE_OK) { + return rc; + } + + /* Non-realtime scheduling does not exist in FreeRTOS. */ + if (attr->schedClass != DDSRT_SCHED_DEFAULT && + attr->schedClass != DDSRT_SCHED_REALTIME) + { + return DDS_RETCODE_BAD_PARAMETER; + } else if (attr->schedPriority < 0 || + attr->schedPriority > (configMAX_PRIORITIES - 1)) + { + return DDS_RETCODE_BAD_PARAMETER; + } + + /* Stack size is quietly increased to match at least the minimum. */ + if (attr->stackSize > size) { + size = (uint16_t)(attr->stackSize / WORD_SIZE); + if (attr->stackSize % WORD_SIZE) { + size++; + } + } + + /* Assume that when the default priority of zero (0) is specified, the user + wants the thread to inherit the priority of the calling thread. */ + assert(0 == tskIDLE_PRIORITY); + if (attr->schedPriority == 0) { + prio = uxTaskPriorityGet(NULL); + } else { + prio = (UBaseType_t)attr->schedPriority; + } + + ddsrt_mutex_lock(&thread_registry.mutex); + + /* Thread context is allocated here so that it can be handled when no more + memory is available. Simply storing the entire context in thread-local + storage would have been possible, but would require the implementation to + define and allocate a separate struct in order to support thread joins. */ + if ((rc = thread_context_create(&ctx)) == DDS_RETCODE_OK) { + ctx->func = start_routine; + ctx->arg = arg; + + if (pdPASS != xTaskCreate( + &thread_start_routine, name, size, ctx, prio, &task)) + { + thread_context_destroy(ctx); + rc = DDS_RETCODE_OUT_OF_RESOURCES; + } else { + thread->task = ctx->task = task; + } + } + + ddsrt_mutex_unlock(&thread_registry.mutex); + + return rc; +} + +void +ddsrt_thread_init(void) +{ + if (thread_context_require() != DDS_RETCODE_OK) { + assert(0); + } +} + +void +ddsrt_thread_fini(void) +{ + thread_context_t *ctx; + + /* NO-OP if no context exists since thread-local storage and cleanup + handler references are both stored in the thread context. */ + if ((ctx = thread_context) != NULL) { + assert(ctx->func != &non_local_thread); + thread_fini(ctx, 0); + } +} + +dds_retcode_t +ddsrt_thread_join(ddsrt_thread_t thread, uint32_t *thread_result) +{ + dds_retcode_t rc; + thread_context_t *ctx; + eTaskState status; + + if ((rc = thread_context_require()) != DDS_RETCODE_OK) { + return rc; + } + + ddsrt_mutex_lock(&thread_registry.mutex); + ctx = thread_context_find(thread.task); + if (ctx != NULL) { + /* Task should never be joined by multiple tasks simultaneously */ + assert(ctx->blkd == NULL); + rc = DDS_RETCODE_TRY_AGAIN; + + do { + (void)memset(&status, 0, sizeof(status)); + status = eTaskGetState(thread.task); + if (status == eDeleted) { + /* FreeRTOS reports the task is deleted. Require the context to exist, + fetch the result and free the context afterwards. */ + assert(ctx != NULL); + rc = DDS_RETCODE_OK; + } else if (status != eInvalid) { + assert(ctx != NULL); + /* FreeRTOS reports the task is still active. That does not mean the + task has not yet returned from start_routine. */ + if (ctx->stat == THREAD_EXITING) { + /* Thread context will not be accessed by the thread itself anymore + and it should be safe to free it. */ + rc = DDS_RETCODE_OK; + } else { + ctx->blkd = xTaskGetCurrentTaskHandle(); + + /* Reset notify state and counter. */ + ulTaskNotifyTake(pdTRUE, 0); + + ddsrt_mutex_unlock(&thread_registry.mutex); + + /* Wait to be notified. */ + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + ddsrt_mutex_lock(&thread_registry.mutex); + } + } else { + rc = DDS_RETCODE_BAD_PARAMETER; + } + } while (rc == DDS_RETCODE_TRY_AGAIN); + + if (rc == DDS_RETCODE_OK) { + if (thread_result != NULL) { + *thread_result = ctx->ret; + } + thread_context_destroy(ctx); + } + } + + ddsrt_mutex_unlock(&thread_registry.mutex); + + return rc; +} + +dds_retcode_t +ddsrt_thread_cleanup_push(void (*routine)(void *), void *arg) +{ + dds_retcode_t rc = DDS_RETCODE_OK; + thread_cleanup_t *tail = NULL; + thread_context_t *ctx; + + assert(routine != NULL); + + if (thread_context_acquire(&ctx) == 0) { + if ((tail = ddsrt_malloc(sizeof(*tail))) == NULL) { + rc = DDS_RETCODE_OUT_OF_RESOURCES; + } else { + tail->prev = ctx->dtors; + tail->routine = routine; + tail->arg = arg; + ctx->dtors = tail; + } + } + + return rc; +} + +dds_retcode_t +ddsrt_thread_cleanup_pop(int execute) +{ + thread_cleanup_t *tail; + thread_context_t *ctx; + + if (thread_context_acquire(&ctx) == 0) { + if ((tail = ctx->dtors) != NULL) { + ctx->dtors = tail->prev; + if (execute) { + tail->routine(tail->arg); + } + ddsrt_free(tail); + } + } + + return DDS_RETCODE_OK; +} diff --git a/src/ddsrt/src/threads/include/dds/ddsrt/threads_priv.h b/src/ddsrt/src/threads/include/dds/ddsrt/threads_priv.h index b10118c..659a201 100644 --- a/src/ddsrt/src/threads/include/dds/ddsrt/threads_priv.h +++ b/src/ddsrt/src/threads/include/dds/ddsrt/threads_priv.h @@ -14,12 +14,6 @@ #include "dds/ddsrt/threads.h" -typedef struct { - char *name; - ddsrt_thread_routine_t routine; - void *arg; -} thread_context_t; - /** \brief Internal structure used to store cleanup handlers (private) */ typedef struct { void *prev; diff --git a/src/ddsrt/src/threads/posix/threads.c b/src/ddsrt/src/threads/posix/threads.c index f8dbf4c..92eec22 100644 --- a/src/ddsrt/src/threads/posix/threads.c +++ b/src/ddsrt/src/threads/posix/threads.c @@ -31,6 +31,12 @@ #include "dds/ddsrt/threads_priv.h" #include "dds/ddsrt/types.h" +typedef struct { + char *name; + ddsrt_thread_routine_t routine; + void *arg; +} thread_context_t; + #if defined(__linux) #include #define MAXTHREADNAMESIZE (15) /* 16 bytes including null-terminating byte. */ diff --git a/src/ddsrt/src/threads/windows/threads.c b/src/ddsrt/src/threads/windows/threads.c index ffba453..23830ab 100644 --- a/src/ddsrt/src/threads/windows/threads.c +++ b/src/ddsrt/src/threads/windows/threads.c @@ -16,6 +16,12 @@ #include "dds/ddsrt/string.h" #include "dds/ddsrt/threads_priv.h" +typedef struct { + char *name; + ddsrt_thread_routine_t routine; + void *arg; +} thread_context_t; + static uint32_t os_startRoutineWrapper( void *threadContext) diff --git a/src/ddsrt/src/time.c b/src/ddsrt/src/time.c index 01e4033..3f344f3 100644 --- a/src/ddsrt/src/time.c +++ b/src/ddsrt/src/time.c @@ -18,7 +18,7 @@ extern inline dds_time_t ddsrt_time_add_duration(dds_time_t abstime, dds_duration_t reltime); -#if !defined(_WIN32) +#if !_WIN32 && !DDSRT_WITH_FREERTOS #include void dds_sleepfor(dds_duration_t n) diff --git a/src/ddsrt/src/time/freertos/time.c b/src/ddsrt/src/time/freertos/time.c new file mode 100644 index 0000000..39ee345 --- /dev/null +++ b/src/ddsrt/src/time/freertos/time.c @@ -0,0 +1,53 @@ +/* + * 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 +#include +#define _POSIX_TIMERS +#include + +#include "dds/ddsrt/time.h" + +extern inline TickType_t ddsrt_duration_to_ticks_ceil(dds_duration_t reltime); + +dds_time_t dds_time(void) +{ + struct timespec ts; + +#if __STDC_VERSION__ >= 201112L + timespec_get(&ts, TIME_UTC); +#else + (void)clock_gettime(CLOCK_REALTIME, &ts); +#endif + + return (ts.tv_sec * DDS_NSECS_IN_SEC) + ts.tv_nsec; +} + +#define NSECS_PER_TICK (DDS_NSECS_IN_SEC / configTICK_RATE_HZ) + +dds_time_t ddsrt_time_monotonic (void) +{ + return (xTaskGetTickCount() * NSECS_PER_TICK); +} + +dds_time_t ddsrt_time_elapsed (void) +{ + /* Elapsed time clock not (yet) supported on this platform. */ + return ddsrt_time_monotonic (); +} + +void dds_sleepfor (dds_duration_t reltime) +{ + TickType_t ticks; + + ticks = ddsrt_duration_to_ticks_ceil(reltime); + vTaskDelay(ticks); +} diff --git a/src/ddsrt/tests/CMakeLists.txt b/src/ddsrt/tests/CMakeLists.txt index 02c92ef..42a8df4 100644 --- a/src/ddsrt/tests/CMakeLists.txt +++ b/src/ddsrt/tests/CMakeLists.txt @@ -10,59 +10,63 @@ # SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause # include(CUnit) +include(GenerateDummyExportHeader) -set(sources - "atomics.c" - "environ.c" - "heap.c" - "ifaddrs.c" - "sync.c" - "strtoll.c" - "thread.c" - "thread_cleanup.c" - "string.c" - "log.c" - "random.c" - "strlcpy.c" - "socket.c" - "process.c" - "select.c") +list(APPEND sources + "atomics.c" + "environ.c" + "heap.c" + "ifaddrs.c" + "sync.c" + "strtoll.c" + "thread.c" + "thread_cleanup.c" + "string.c" + "log.c" + "random.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") +if(HAVE_MULTI_PROCESS) + list(APPEND sources "process.c") +endif() +if(WITH_FREERTOS) + list(APPEND sources "tasklist.c") endif() +add_cunit_executable(cunit_ddsrt ${sources}) +target_link_libraries( + cunit_ddsrt PRIVATE ddsrt) target_include_directories( cunit_ddsrt PRIVATE "$") +generate_dummy_export_header( + cunit_ddsrt + BASE_NAME dds + EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/include/dds/export.h") -# Create a separate test application that will be used to -# test process management. -add_executable(process_app process_app.c) -target_link_libraries(process_app PRIVATE ddsrt) -target_include_directories( - process_app - PRIVATE - "$") -# Force the app to be at the same location, no matter what platform or build type. -set_target_properties( - process_app - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR} - RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR} - RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_CURRENT_BINARY_DIR} - RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_CURRENT_BINARY_DIR} ) -# Let the cunit application know the location and name of the test application. -set(process_app_name "${CMAKE_CURRENT_BINARY_DIR}/process_app${CMAKE_EXECUTABLE_SUFFIX}") -configure_file( - "process_test.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/process_test.h" @ONLY) +if(HAVE_MULTI_PROCESS) + # A separate application is required to test process management. + add_executable(process_app process_app.c) + target_link_libraries(process_app PRIVATE ddsrt) + target_include_directories( + process_app + PRIVATE + "$") + # Force the app to be at the same location, no matter what platform or build type. + # FIXME: What if custom targets are added? + # FIXME: What debug and release builds are mixed on Windows and macOS? + set_target_properties( + process_app + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR} + RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR} + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_CURRENT_BINARY_DIR} + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_CURRENT_BINARY_DIR} ) + # Let the cunit application know the location and name of the test application. + set(process_app_name "${CMAKE_CURRENT_BINARY_DIR}/process_app${CMAKE_EXECUTABLE_SUFFIX}") + configure_file( + "process_test.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/process_test.h" @ONLY) +endif() diff --git a/src/ddsrt/tests/ifaddrs.c b/src/ddsrt/tests/ifaddrs.c index ae8ea0b..50166da 100644 --- a/src/ddsrt/tests/ifaddrs.c +++ b/src/ddsrt/tests/ifaddrs.c @@ -9,10 +9,10 @@ * * 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" +#include "CUnit/Test.h" /* FIXME: It's not possible to predict what network interfaces are available on a given host. To properly test all combinations the abstracted @@ -117,9 +117,9 @@ CU_Test(ddsrt_getifaddrs, empty_filter) ddsrt_freeifaddrs(ifa_root); } -#ifdef DDSRT_HAVE_IPV6 CU_Test(ddsrt_getifaddrs, ipv6) { +#ifdef DDSRT_HAVE_IPV6 if (ipv6_enabled == 1) { dds_retcode_t ret; int have_ipv6 = 0; @@ -149,12 +149,16 @@ CU_Test(ddsrt_getifaddrs, ipv6) } else { CU_PASS("IPv6 disabled in test environment"); } +#else + CU_PASS("IPv6 is not supported"); +#endif } /* 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 DDSRT_HAVE_IPV6 if (ipv6_enabled == 1) { dds_retcode_t ret; int have_ipv4 = 0; @@ -182,6 +186,8 @@ CU_Test(ddsrt_getifaddrs, ipv4_n_ipv6) } else { CU_PASS("IPv6 disabled in test environment"); } +#else + CU_PASS("IPv6 is not supported"); +#endif /* DDSRT_HAVE_IPV6 */ } -#endif /* DDSRT_HAVE_IPV6 */ diff --git a/src/ddsrt/tests/log.c b/src/ddsrt/tests/log.c index 2c619c9..a5e66cf 100644 --- a/src/ddsrt/tests/log.c +++ b/src/ddsrt/tests/log.c @@ -311,8 +311,8 @@ static ddsrt_mutex_t mutex; struct arg { ddsrt_cond_t *cond; ddsrt_mutex_t *mutex; - dds_time_t stamp; - dds_duration_t pause; + dds_time_t before; + dds_time_t after; }; static void dummy(void *ptr, const dds_log_data_t *data) @@ -326,10 +326,10 @@ 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(); + arg->before = dds_time(); ddsrt_cond_broadcast(arg->cond); ddsrt_mutex_unlock(arg->mutex); - dds_sleepfor(arg->pause); + arg->after = dds_time(); } static uint32_t run(void *ptr) @@ -347,7 +347,6 @@ static uint32_t run(void *ptr) CU_Test(dds_log, synchronous_sink_changes, .fini=reset) { struct arg arg; - dds_time_t diff, stamp; ddsrt_thread_t tid; ddsrt_threadattr_t tattr; dds_retcode_t ret; @@ -357,7 +356,6 @@ CU_Test(dds_log, synchronous_sink_changes, .fini=reset) (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); @@ -366,9 +364,7 @@ CU_Test(dds_log, synchronous_sink_changes, .fini=reset) 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); + CU_ASSERT(arg.before < arg.after); + CU_ASSERT(arg.after < dds_time()); } diff --git a/src/ddsrt/tests/select.c b/src/ddsrt/tests/select.c index ab7bf0e..7d99096 100644 --- a/src/ddsrt/tests/select.c +++ b/src/ddsrt/tests/select.c @@ -9,10 +9,10 @@ * * 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/cdtors.h" #include "dds/ddsrt/threads.h" +#include "CUnit/Theory.h" CU_Init(ddsrt_select) { @@ -139,7 +139,7 @@ static const char mesg[] = "foobar"; static uint32_t select_timeout_routine(void *ptr) { - int cnt = -1; + int32_t cnt = -1; dds_retcode_t rc; dds_time_t before, after; dds_duration_t delay; @@ -148,7 +148,13 @@ static uint32_t select_timeout_routine(void *ptr) uint32_t res = 0; FD_ZERO(&rdset); +#if LWIP_SOCKET + DDSRT_WARNING_GNUC_OFF(sign-conversion) +#endif FD_SET(arg->sock, &rdset); +#if LWIP_SOCKET + DDSRT_WARNING_GNUC_ON(sign-conversion) +#endif before = dds_time(); rc = ddsrt_select(arg->sock + 1, &rdset, NULL, NULL, arg->delay, &cnt); @@ -157,11 +163,15 @@ static uint32_t select_timeout_routine(void *ptr) 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); + fprintf(stderr, "ddsrt_select returned %"PRId32"\n", rc); + fprintf(stderr, "ddsrt_select reported %"PRId32" ready\n", cnt); if (rc == DDS_RETCODE_TIMEOUT) { res = (((after - delay) >= (arg->delay - arg->skew)) && (cnt == 0)); + /* Running in the FreeRTOS simulator causes some trouble as interrupts are + simulated using signals causing the select call to be interrupted. */ + } else if (rc == DDS_RETCODE_INTERRUPTED) { + res = (cnt == -1); } return res; @@ -207,13 +217,19 @@ static uint32_t recv_routine(void *ptr) { thread_arg_t *arg = (thread_arg_t*)ptr; - int nfds = 0; + int32_t nfds = 0; fd_set rdset; ssize_t rcvd = -1; char buf[sizeof(mesg)]; FD_ZERO(&rdset); +#if LWIP_SOCKET + DDSRT_WARNING_GNUC_OFF(sign-conversion) +#endif FD_SET(arg->sock, &rdset); +#if LWIP_SOCKET + DDSRT_WARNING_GNUC_ON(sign-conversion) +#endif (void)ddsrt_select(arg->sock + 1, &rdset, NULL, NULL, arg->delay, &nfds); @@ -260,7 +276,7 @@ static uint32_t recvmsg_routine(void *ptr) { thread_arg_t *arg = (thread_arg_t*)ptr; - int nfds = 0; + int32_t nfds = 0; fd_set rdset; ssize_t rcvd = -1; char buf[sizeof(mesg)]; @@ -274,7 +290,13 @@ static uint32_t recvmsg_routine(void *ptr) msg.msg_iovlen = 1; FD_ZERO(&rdset); +#if LWIP_SOCKET + DDSRT_WARNING_GNUC_OFF(sign-conversion) +#endif FD_SET(arg->sock, &rdset); +#if LWIP_SOCKET + DDSRT_WARNING_GNUC_ON(sign-conversion) +#endif (void)ddsrt_select(arg->sock + 1, &rdset, NULL, NULL, arg->delay, &nfds); diff --git a/src/ddsrt/tests/socket.c b/src/ddsrt/tests/socket.c index db89e2a..366c4b2 100644 --- a/src/ddsrt/tests/socket.c +++ b/src/ddsrt/tests/socket.c @@ -9,17 +9,17 @@ * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ -#include -#include -#include -#include - -#include "CUnit/Theory.h" +#include "dds/ddsrt/sockets.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" +#include "dds/ddsrt/string.h" +#include "CUnit/Theory.h" + +#include +#include +#include DDSRT_WARNING_MSVC_OFF(4305) #if DDSRT_ENDIAN == DDSRT_BIG_ENDIAN @@ -79,8 +79,8 @@ CU_Theory((char *str, int af, dds_retcode_t exp), ddsrt_sockaddrfromstr, ipv4, . sockaddrfromstr_test(str, af, exp); } -#if DDSRT_HAVE_IPV6 CU_TheoryDataPoints(ddsrt_sockaddrfromstr, ipv6) = { +#if DDSRT_HAVE_IPV6 CU_DataPoints(char *, "127.0.0.1", "::1", "::1", "::", "nip"), @@ -90,13 +90,20 @@ CU_TheoryDataPoints(ddsrt_sockaddrfromstr, ipv6) = { CU_DataPoints(dds_retcode_t, DDS_RETCODE_BAD_PARAMETER, DDS_RETCODE_OK, DDS_RETCODE_BAD_PARAMETER, DDS_RETCODE_OK, DDS_RETCODE_BAD_PARAMETER) +#endif /* DDSRT_HAVE_IPV6 */ }; CU_Theory((char *str, int af, dds_retcode_t exp), ddsrt_sockaddrfromstr, ipv6, .init=setup, .fini=teardown) { +#if DDSRT_HAVE_IPV6 sockaddrfromstr_test(str, af, exp); -} +#else + (void)str; + (void)af; + (void)exp; + CU_PASS("IPV6 is not supported"); #endif /* DDSRT_HAVE_IPV6 */ +} CU_Test(ddsrt_sockaddrtostr, bad_sockaddr, .init=setup, .fini=teardown) { @@ -128,16 +135,19 @@ CU_Test(ddsrt_sockaddrtostr, ipv4) CU_Test(ddsrt_sockaddrtostr, ipv6) { +#if DDSRT_HAVE_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"); +#else + CU_PASS("IPv6 is not supported"); +#endif } CU_Test(ddsrt_sockets, gethostname) { - int ret; dds_retcode_t rc; char sysbuf[200], buf[200]; @@ -146,8 +156,12 @@ CU_Test(ddsrt_sockets, gethostname) CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK); sysbuf[0] = '\0'; - ret = gethostname(sysbuf, sizeof(sysbuf)); +#if LWIP_SOCKET + ddsrt_strlcpy(sysbuf, "localhost", sizeof(sysbuf)); +#else + int ret = gethostname(sysbuf, sizeof(sysbuf)); CU_ASSERT_EQUAL(ret, 0); +#endif CU_ASSERT(strcmp(buf, sysbuf) == 0); rc = ddsrt_gethostname(buf, strlen(buf) - 1); @@ -169,6 +183,7 @@ static void gethostbyname_test(char *name, int af, dds_retcode_t exp) } ddsrt_free(hent); } +#endif CU_TheoryDataPoints(ddsrt_gethostbyname, ipv4) = { CU_DataPoints(char *, "", "127.0.0.1", "127.0.0.1"), @@ -178,21 +193,34 @@ CU_TheoryDataPoints(ddsrt_gethostbyname, ipv4) = { CU_Theory((char *name, int af, dds_retcode_t exp), ddsrt_gethostbyname, ipv4, .init=setup, .fini=teardown) { +#if DDSRT_HAVE_DNS gethostbyname_test(name, af, exp); +#else + (void)name; + (void)af; + (void)exp; + CU_PASS("DNS is not supported"); +#endif } -#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) = { +#if DDSRT_HAVE_IPV6 && DDSRT_HAVE_DNS 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) +#endif /* DDSRT_HAVE_IPV6 */ }; CU_Theory((char *name, int af, dds_retcode_t exp), ddsrt_gethostbyname, ipv6, .init=setup, .fini=teardown) { +#if DDSRT_HAVE_IPV6 && DDSRT_HAVE_DNS gethostbyname_test(name, af, exp); -} +#else + (void)name; + (void)af; + (void)exp; + CU_PASS("DNS and IPv6 are not supported"); #endif /* DDSRT_HAVE_IPV6 */ -#endif /* DDSRT_HAVE_DNS */ +} diff --git a/src/ddsrt/tests/sync.c b/src/ddsrt/tests/sync.c index 1f8031c..d009353 100644 --- a/src/ddsrt/tests/sync.c +++ b/src/ddsrt/tests/sync.c @@ -270,7 +270,7 @@ static uint32_t waitfor_routine(void *ptr) 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); + fprintf(stderr, "woke up %"PRIu32" times\n", cnt); ddsrt_mutex_unlock(&arg->lock); if (reltime >= arg->reltime) { /* Ensure that the condition variable at least waited for the amount of @@ -322,7 +322,7 @@ static uint32_t waituntil_routine(void *ptr) 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); + fprintf(stderr, "woke up %"PRIu32" 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. */ diff --git a/src/ddsrt/tests/tasklist.c b/src/ddsrt/tests/tasklist.c new file mode 100644 index 0000000..81134c8 --- /dev/null +++ b/src/ddsrt/tests/tasklist.c @@ -0,0 +1,341 @@ +/* + * 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 "dds/ddsrt/sync.h" + +#include "CUnit/Theory.h" + +/* FreeRTOS specific! */ + +static void fill(ddsrt_tasklist_t *list) +{ + CU_ASSERT_PTR_NOT_NULL_FATAL(list); + CU_ASSERT_EQUAL_FATAL(list->len, DDSRT_TASKLIST_INITIAL); + + for (size_t i = 1; i <= DDSRT_TASKLIST_INITIAL; i++) { + ddsrt_tasklist_push(list, (TaskHandle_t)i); + CU_ASSERT_EQUAL_FATAL(list->cnt, i); + CU_ASSERT_EQUAL_FATAL(list->off, 0); + CU_ASSERT_EQUAL_FATAL(list->end, i - 1); + } + + CU_ASSERT_EQUAL_FATAL(list->len, DDSRT_TASKLIST_INITIAL); + CU_ASSERT_EQUAL_FATAL(list->cnt, DDSRT_TASKLIST_INITIAL); + CU_ASSERT_EQUAL_FATAL(list->off, 0); + CU_ASSERT_EQUAL_FATAL(list->end, DDSRT_TASKLIST_INITIAL - 1); +} + +static void fill_wrapped(ddsrt_tasklist_t *list) +{ + size_t i; + + fill(list); + + for (i = 1; i <= DDSRT_TASKLIST_CHUNK; i++) { + ddsrt_tasklist_pop(list, NULL); + CU_ASSERT_EQUAL_FATAL(list->cnt, DDSRT_TASKLIST_INITIAL - i); + CU_ASSERT_EQUAL_FATAL(list->off, i); + CU_ASSERT_EQUAL_FATAL(list->end, DDSRT_TASKLIST_INITIAL - 1); + } + + for (i = (DDSRT_TASKLIST_INITIAL+1); i <= (DDSRT_TASKLIST_INITIAL+DDSRT_TASKLIST_CHUNK); i++) { + ddsrt_tasklist_push(list, (TaskHandle_t)i); + CU_ASSERT_EQUAL_FATAL(list->cnt, i - DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL_FATAL(list->off, DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL_FATAL(list->end, (i - 1) - DDSRT_TASKLIST_INITIAL); + } + + CU_ASSERT_EQUAL_FATAL(list->len, DDSRT_TASKLIST_INITIAL); + CU_ASSERT_EQUAL_FATAL(list->cnt, DDSRT_TASKLIST_INITIAL); + CU_ASSERT_EQUAL_FATAL(list->off, DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL_FATAL(list->end, DDSRT_TASKLIST_CHUNK - 1); +} + +typedef void(*fill_t)(ddsrt_tasklist_t *); + +CU_TheoryDataPoints(ddsrt_sync, tasklist_pop_all) = { + CU_DataPoints(fill_t, &fill, &fill_wrapped), + CU_DataPoints(size_t, 1, DDSRT_TASKLIST_CHUNK + 1), + CU_DataPoints(size_t, DDSRT_TASKLIST_INITIAL, DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK) +}; + +/* Most basic test to verify behavior is correct for simple use case. */ +CU_Theory((fill_t func, size_t first, size_t last), ddsrt_sync, tasklist_pop_all) +{ + TaskHandle_t task; + ddsrt_tasklist_t list; + + ddsrt_tasklist_init(&list); + func(&list); + + task = ddsrt_tasklist_pop(&list, NULL); + CU_ASSERT_PTR_EQUAL(task, (TaskHandle_t)first); + + for (size_t i = first + 1; i < last; i++) { + task = ddsrt_tasklist_pop(&list, NULL); + CU_ASSERT_PTR_EQUAL(task, (TaskHandle_t)i); + } + + CU_ASSERT_EQUAL(list.cnt, 1); + CU_ASSERT_EQUAL(list.off, ((DDSRT_TASKLIST_INITIAL*2) - last) - 1); + CU_ASSERT_EQUAL(list.end, ((DDSRT_TASKLIST_INITIAL*2) - last) - 1); + task = ddsrt_tasklist_pop(&list, NULL); + CU_ASSERT_PTR_EQUAL(task, (TaskHandle_t)last); + task = ddsrt_tasklist_pop(&list, NULL); + CU_ASSERT_PTR_NULL(task); + CU_ASSERT_EQUAL(list.cnt, 0); + CU_ASSERT_EQUAL(list.off, 0); + CU_ASSERT_EQUAL(list.end, 0); + + ddsrt_tasklist_fini(&list); +} + +CU_TheoryDataPoints(ddsrt_sync, tasklist_pop_n_push) = { + CU_DataPoints(fill_t, + &fill, &fill, &fill, &fill, + &fill_wrapped, &fill_wrapped, &fill_wrapped, &fill_wrapped, &fill_wrapped), + CU_DataPoints(TaskHandle_t, /* Task to pop. */ + (TaskHandle_t)NULL, + (TaskHandle_t)1, + (TaskHandle_t)DDSRT_TASKLIST_CHUNK, + (TaskHandle_t)DDSRT_TASKLIST_INITIAL, + (TaskHandle_t)NULL, + (TaskHandle_t)(DDSRT_TASKLIST_CHUNK + 1), + (TaskHandle_t)DDSRT_TASKLIST_INITIAL, + (TaskHandle_t)(DDSRT_TASKLIST_INITIAL + 1), + (TaskHandle_t)(DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK)), + CU_DataPoints(size_t, /* Expected position to clear. */ + 0, 0, DDSRT_TASKLIST_CHUNK - 1, DDSRT_TASKLIST_INITIAL - 1, + DDSRT_TASKLIST_CHUNK, DDSRT_TASKLIST_CHUNK, DDSRT_TASKLIST_INITIAL - 1, 0, DDSRT_TASKLIST_CHUNK - 1), + CU_DataPoints(size_t, /* Expected position of pushed task. */ + 0, 0, DDSRT_TASKLIST_INITIAL - 1, DDSRT_TASKLIST_INITIAL - 1, + DDSRT_TASKLIST_CHUNK, DDSRT_TASKLIST_CHUNK, DDSRT_TASKLIST_CHUNK, DDSRT_TASKLIST_CHUNK - 1, DDSRT_TASKLIST_CHUNK - 1) +}; + +/* Test to verify tasklist is correctly updated (trimmed and packed) when the + tasklist is sparse. */ +CU_Theory((fill_t func, TaskHandle_t task, size_t pos, size_t end), ddsrt_sync, tasklist_pop_n_push) +{ + ddsrt_tasklist_t list; + + ddsrt_tasklist_init(&list); + func(&list); + + if (task == NULL) { + ddsrt_tasklist_pop(&list, NULL); + } else { + CU_ASSERT_PTR_EQUAL(ddsrt_tasklist_pop(&list, task), task); + CU_ASSERT_PTR_NULL(ddsrt_tasklist_pop(&list, task)); + } + CU_ASSERT_PTR_EQUAL(list.tasks[pos], NULL); + task = (TaskHandle_t)(DDSRT_TASKLIST_INITIAL*2); + CU_ASSERT_NOT_EQUAL_FATAL(ddsrt_tasklist_push(&list, task), -1); + CU_ASSERT_PTR_EQUAL(list.tasks[end], task); + CU_ASSERT_EQUAL(list.len, DDSRT_TASKLIST_INITIAL); + CU_ASSERT_EQUAL(list.cnt, DDSRT_TASKLIST_INITIAL); + + ddsrt_tasklist_fini(&list); +} + +CU_Test(ddsrt_sync, tasklist_ltrim) +{ + ddsrt_tasklist_t list; + + ddsrt_tasklist_init(&list); + fill(&list); + + ddsrt_tasklist_pop(&list, (TaskHandle_t)2); + ddsrt_tasklist_pop(&list, (TaskHandle_t)3); + CU_ASSERT_EQUAL(list.cnt, DDSRT_TASKLIST_INITIAL - 2); + CU_ASSERT_EQUAL(list.off, 0); + CU_ASSERT_EQUAL(list.end, 9); + ddsrt_tasklist_pop(&list, (TaskHandle_t)1); + CU_ASSERT_EQUAL(list.cnt, DDSRT_TASKLIST_INITIAL - 3); + CU_ASSERT_EQUAL(list.off, 3); + CU_ASSERT_EQUAL(list.end, 9); + + ddsrt_tasklist_fini(&list); +} + +CU_Test(ddsrt_sync, tasklist_rtrim) +{ + ddsrt_tasklist_t list; + + ddsrt_tasklist_init(&list); + fill(&list); + + ddsrt_tasklist_pop(&list, (TaskHandle_t)(DDSRT_TASKLIST_INITIAL - 1)); + ddsrt_tasklist_pop(&list, (TaskHandle_t)(DDSRT_TASKLIST_INITIAL - 2)); + CU_ASSERT_EQUAL(list.cnt, DDSRT_TASKLIST_INITIAL - 2); + CU_ASSERT_EQUAL(list.off, 0); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_INITIAL - 1); + ddsrt_tasklist_pop(&list, (TaskHandle_t)DDSRT_TASKLIST_INITIAL); + CU_ASSERT_EQUAL(list.cnt, DDSRT_TASKLIST_INITIAL - 3); + CU_ASSERT_EQUAL(list.off, 0); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_INITIAL - 4); + + ddsrt_tasklist_fini(&list); +} + +CU_Test(ddsrt_sync, tasklist_wrapped_ltrim) +{ + ddsrt_tasklist_t list; + + ddsrt_tasklist_init(&list); + fill_wrapped(&list); + + for (size_t i = DDSRT_TASKLIST_CHUNK+2; i < DDSRT_TASKLIST_INITIAL; i++) { + ddsrt_tasklist_pop(&list, (TaskHandle_t)i); + } + CU_ASSERT_EQUAL(list.cnt, DDSRT_TASKLIST_INITIAL - (DDSRT_TASKLIST_CHUNK - 2)); + CU_ASSERT_EQUAL(list.off, DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_CHUNK - 1); + ddsrt_tasklist_pop(&list, (TaskHandle_t)(DDSRT_TASKLIST_CHUNK+1)); + CU_ASSERT_EQUAL(list.cnt, DDSRT_TASKLIST_INITIAL - (DDSRT_TASKLIST_CHUNK - 1)); + CU_ASSERT_EQUAL(list.off, DDSRT_TASKLIST_INITIAL - 1); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_CHUNK - 1); + ddsrt_tasklist_pop(&list, (TaskHandle_t)(DDSRT_TASKLIST_INITIAL+1)); + ddsrt_tasklist_pop(&list, (TaskHandle_t)DDSRT_TASKLIST_INITIAL); + CU_ASSERT_EQUAL(list.cnt, DDSRT_TASKLIST_INITIAL - (DDSRT_TASKLIST_CHUNK + 1)); + CU_ASSERT_EQUAL(list.off, 1); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_CHUNK - 1); + + ddsrt_tasklist_fini(&list); +} + +CU_Test(ddsrt_sync, tasklist_wrapped_rtrim) +{ + ddsrt_tasklist_t list; + size_t last = DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK; + + ddsrt_tasklist_init(&list); + fill_wrapped(&list); + + for (size_t i = last - 1; i > DDSRT_TASKLIST_INITIAL + 1; i--) { + ddsrt_tasklist_pop(&list, (TaskHandle_t)i); + } + CU_ASSERT_EQUAL(list.cnt, (DDSRT_TASKLIST_INITIAL - DDSRT_TASKLIST_CHUNK) + 2); + CU_ASSERT_EQUAL(list.off, DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_CHUNK - 1); + ddsrt_tasklist_pop(&list, (TaskHandle_t)(DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK)); + CU_ASSERT_EQUAL(list.cnt, (DDSRT_TASKLIST_INITIAL - DDSRT_TASKLIST_CHUNK) + 1); + CU_ASSERT_EQUAL(list.off, DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL(list.end, 0); + ddsrt_tasklist_pop(&list, (TaskHandle_t)(DDSRT_TASKLIST_INITIAL - 1)); + ddsrt_tasklist_pop(&list, (TaskHandle_t)(DDSRT_TASKLIST_INITIAL - 2)); + ddsrt_tasklist_pop(&list, (TaskHandle_t)(DDSRT_TASKLIST_INITIAL + 1)); + CU_ASSERT_EQUAL(list.cnt, (DDSRT_TASKLIST_INITIAL - DDSRT_TASKLIST_CHUNK) - 2); + CU_ASSERT_EQUAL(list.off, DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_INITIAL - 1); + ddsrt_tasklist_pop(&list, (TaskHandle_t)DDSRT_TASKLIST_INITIAL); + CU_ASSERT_EQUAL(list.cnt, (DDSRT_TASKLIST_INITIAL - DDSRT_TASKLIST_CHUNK) - 3); + CU_ASSERT_EQUAL(list.off, DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_INITIAL - 4); + + ddsrt_tasklist_fini(&list); +} + +CU_Test(ddsrt_sync, tasklist_resize) +{ + ddsrt_tasklist_t list; + int ret; + + ddsrt_tasklist_init(&list); + fill(&list); + + /* Grow one past initial. Buffer should increase by chunk. */ + ret = ddsrt_tasklist_push(&list, (TaskHandle_t)(DDSRT_TASKLIST_INITIAL + 1)); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL(list.len, DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL(list.off, 0); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_INITIAL); + /* Grow one past initial+chunk. Buffer should increase by chunk again. */ + for (size_t i = 2; i <= DDSRT_TASKLIST_CHUNK + 1; i++) { + ret = ddsrt_tasklist_push(&list, (TaskHandle_t)(DDSRT_TASKLIST_INITIAL + i)); + CU_ASSERT_EQUAL_FATAL(ret, 0); + } + CU_ASSERT_EQUAL(list.len, DDSRT_TASKLIST_INITIAL + (DDSRT_TASKLIST_CHUNK*2)); + CU_ASSERT_EQUAL(list.off, 0); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK); + + /* Shrink one past initial+chunk. Buffer should not decrease by chunk. */ + for (size_t i = 1; i <= DDSRT_TASKLIST_CHUNK; i++) { + ddsrt_tasklist_pop(&list, (TaskHandle_t)i); + } + CU_ASSERT_EQUAL(list.len, DDSRT_TASKLIST_INITIAL + (DDSRT_TASKLIST_CHUNK*2)); + CU_ASSERT_EQUAL(list.off, DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK); + + /* Shrink to initial. Buffer should decrease by chunk. */ + ddsrt_tasklist_pop(&list, (TaskHandle_t)(DDSRT_TASKLIST_CHUNK + 1)); + CU_ASSERT_EQUAL(list.len, DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL(list.off, 0); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_INITIAL - 1); + + /* Shrink to initial-chunk. Buffer should decrease by chunk. */ + for (size_t i = DDSRT_TASKLIST_CHUNK+1; i <= (DDSRT_TASKLIST_CHUNK*2)+1; i++) { + ddsrt_tasklist_pop(&list, (TaskHandle_t)i); + CU_ASSERT_EQUAL_FATAL(ret, 0); + } + CU_ASSERT_EQUAL(list.len, DDSRT_TASKLIST_INITIAL); + CU_ASSERT_EQUAL(list.off, 0); + CU_ASSERT_EQUAL(list.end, (DDSRT_TASKLIST_INITIAL - DDSRT_TASKLIST_CHUNK) - 1); + + ddsrt_tasklist_fini(&list); +} + +CU_Test(ddsrt_sync, tasklist_wrapped_resize) +{ + ddsrt_tasklist_t list; + int ret; + + ddsrt_tasklist_init(&list); + fill_wrapped(&list); + + /* Grow one past initial. Buffer should increase by chunk. */ + ret = ddsrt_tasklist_push(&list, (TaskHandle_t)(DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK + 1)); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL(list.len, DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL(list.off, DDSRT_TASKLIST_INITIAL); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_CHUNK); + /* Grow one past initial+chunk. Buffer should increase by chunk again. */ + for (size_t i = 2; i <= (DDSRT_TASKLIST_CHUNK + 1); i++) { + ret = ddsrt_tasklist_push(&list, (TaskHandle_t)(DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK + i)); + CU_ASSERT_EQUAL_FATAL(ret, 0); + } + CU_ASSERT_EQUAL(list.len, DDSRT_TASKLIST_INITIAL + (DDSRT_TASKLIST_CHUNK*2)); + CU_ASSERT_EQUAL(list.off, DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_INITIAL); + + /* Shrink one past initial+chunk. Buffer should not decrease by chunk. */ + for (size_t i = 1; i <= DDSRT_TASKLIST_CHUNK; i++) { + ddsrt_tasklist_pop(&list, (TaskHandle_t)(DDSRT_TASKLIST_CHUNK + i)); + } + CU_ASSERT_EQUAL(list.len, DDSRT_TASKLIST_INITIAL + (DDSRT_TASKLIST_CHUNK*2)); + CU_ASSERT_EQUAL(list.off, 0); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_INITIAL); + + /* Shrink to initial. Buffer should decrease by chunk. */ + ddsrt_tasklist_pop(&list, (TaskHandle_t)((DDSRT_TASKLIST_CHUNK*2) + 1)); + CU_ASSERT_EQUAL(list.len, DDSRT_TASKLIST_INITIAL + DDSRT_TASKLIST_CHUNK); + CU_ASSERT_EQUAL(list.off, 0); + CU_ASSERT_EQUAL(list.end, DDSRT_TASKLIST_INITIAL - 1); + + /* Shrink to initial-chunk. Buffer should decrease by chunk. */ + for (size_t i = 2; i <= DDSRT_TASKLIST_CHUNK + 1; i++) { + ddsrt_tasklist_pop(&list, (TaskHandle_t)((DDSRT_TASKLIST_CHUNK*2) + i)); + } + CU_ASSERT_EQUAL(list.len, DDSRT_TASKLIST_INITIAL); + CU_ASSERT_EQUAL(list.off, 0); + CU_ASSERT_EQUAL(list.end, (DDSRT_TASKLIST_INITIAL - DDSRT_TASKLIST_CHUNK) - 1); + + ddsrt_tasklist_fini(&list); +} diff --git a/src/ddsrt/tests/thread.c b/src/ddsrt/tests/thread.c index 8b3ff5d..8814440 100644 --- a/src/ddsrt/tests/thread.c +++ b/src/ddsrt/tests/thread.c @@ -11,9 +11,12 @@ */ #include #include -#if !defined(_WIN32) -#include -#include +#if DDSRT_WITH_FREERTOS +# include +# include +#elif !defined(_WIN32) +# include +# include #endif #include "CUnit/Theory.h" @@ -30,7 +33,10 @@ static int32_t min_other_prio = 250; CU_Init(ddsrt_thread) { ddsrt_init(); -#if defined(WIN32) +#if DDSRT_WITH_FREERTOS + max_other_prio = max_fifo_prio = configMAX_PRIORITIES - 1; + min_other_prio = min_fifo_prio = tskIDLE_PRIORITY + 1; +#elif defined(WIN32) max_fifo_prio = THREAD_PRIORITY_HIGHEST; min_fifo_prio = THREAD_PRIORITY_LOWEST; max_other_prio = THREAD_PRIORITY_HIGHEST; @@ -68,7 +74,12 @@ uint32_t thread_main(void *ptr) attr = arg->attr; -#if _WIN32 +#if DDSRT_WITH_FREERTOS + int prio = (int)uxTaskPriorityGet(NULL); + if (prio == attr->schedPriority) { + arg->res = 1; + } +#elif _WIN32 int prio = GetThreadPriority(GetCurrentThread()); if (prio == THREAD_PRIORITY_ERROR_RETURN) abort(); @@ -113,7 +124,12 @@ CU_Theory((ddsrt_sched_t sched, int32_t *prio, uint32_t exp), ddsrt_thread, crea ddsrt_threadattr_t attr; thread_arg_t arg; -#if defined(__VXWORKS__) +#if DDSRT_WITH_FREERTOS + if (sched == DDSRT_SCHED_TIMESHARE) { + skip = 1; + CU_PASS("FreeRTOS only support SCHED_FIFO"); + } +#elif defined(__VXWORKS__) # if defined(_WRS_KERNEL) if (sched == DDSRT_SCHED_TIMESHARE) { skip = 1; @@ -150,7 +166,9 @@ CU_Test(ddsrt_thread, thread_id) { int eq = 0; ddsrt_thread_t thr; -#if defined(_WIN32) +#if DDSRT_WITH_FREERTOS + TaskHandle_t task; +#elif defined(_WIN32) DWORD _tid; #else pthread_t _thr; @@ -158,7 +176,10 @@ CU_Test(ddsrt_thread, thread_id) thr = ddsrt_thread_self(); -#if defined(_WIN32) +#if DDSRT_WITH_FREERTOS + task = xTaskGetCurrentTaskHandle(); + eq = (thr.task == task); +#elif defined(_WIN32) _tid = GetCurrentThreadId(); eq = (thr.tid == _tid); #else @@ -230,4 +251,3 @@ CU_Test(ddsrt_thread, attribute) CU_ASSERT_EQUAL(attr.schedPriority, 0); CU_ASSERT_EQUAL(attr.stackSize, 0); } - diff --git a/src/examples/roundtrip/ping.c b/src/examples/roundtrip/ping.c index 9b09c3a..d24541e 100644 --- a/src/examples/roundtrip/ping.c +++ b/src/examples/roundtrip/ping.c @@ -116,7 +116,7 @@ static bool CtrlHandler (DWORD fdwCtrlType) dds_waitset_set_trigger (waitSet, true); return true; //Don't let other handlers handle this key } -#else +#elif !DDSRT_WITH_FREERTOS static void CtrlHandler (int sig) { (void)sig; @@ -249,7 +249,7 @@ int main (int argc, char *argv[]) /* Register handler for Ctrl-C */ #ifdef _WIN32 SetConsoleCtrlHandler ((PHANDLER_ROUTINE)CtrlHandler, TRUE); -#else +#elif !DDSRT_WITH_FREERTOS struct sigaction sat, oldAction; sat.sa_handler = CtrlHandler; sigemptyset (&sat.sa_mask); @@ -411,7 +411,7 @@ done: #ifdef _WIN32 SetConsoleCtrlHandler (0, FALSE); -#else +#elif !DDSRT_WITH_FREERTOS sigaction (SIGINT, &oldAction, 0); #endif diff --git a/src/examples/roundtrip/pong.c b/src/examples/roundtrip/pong.c index 010c0c5..992ba75 100644 --- a/src/examples/roundtrip/pong.c +++ b/src/examples/roundtrip/pong.c @@ -20,7 +20,7 @@ static bool CtrlHandler (DWORD fdwCtrlType) dds_waitset_set_trigger (waitSet, true); return true; //Don't let other handlers handle this key } -#else +#elif !DDSRT_WITH_FREERTOS static void CtrlHandler (int sig) { (void)sig; @@ -87,7 +87,7 @@ int main (int argc, char *argv[]) #ifdef _WIN32 SetConsoleCtrlHandler ((PHANDLER_ROUTINE)CtrlHandler, TRUE); -#else +#elif !DDSRT_WITH_FREERTOS struct sigaction sat, oldAction; sat.sa_handler = CtrlHandler; sigemptyset (&sat.sa_mask); @@ -130,7 +130,7 @@ int main (int argc, char *argv[]) #ifdef _WIN32 SetConsoleCtrlHandler (0, FALSE); -#else +#elif !DDSRT_WITH_FREERTOS sigaction (SIGINT, &oldAction, 0); #endif diff --git a/src/tools/ddsperf/ddsperf.c b/src/tools/ddsperf/ddsperf.c index cef4870..06f1970 100644 --- a/src/tools/ddsperf/ddsperf.c +++ b/src/tools/ddsperf/ddsperf.c @@ -19,9 +19,6 @@ #include #include #include -#ifndef _WIN32 -#include -#endif #include #include "dds/dds.h" @@ -36,6 +33,10 @@ #include "dds/ddsrt/avl.h" #include "dds/ddsrt/fibheap.h" +#if !defined(_WIN32) && !defined(LWIP_SOCKET) +#include +#endif + #define UDATA_MAGIC "DDSPerf:" #define UDATA_MAGIC_SIZE (sizeof (UDATA_MAGIC) - 1) @@ -1383,14 +1384,16 @@ static void subthread_arg_fini (struct subthread_arg *arg) free (arg->iseq); } +#if !DDSRT_WITH_FREERTOS static void signal_handler (int sig) { (void) sig; termflag = 1; dds_set_guardcondition (termcond, true); } +#endif -#ifndef _WIN32 +#if !_WIN32 && !DDSRT_WITH_FREERTOS static uint32_t sigthread (void *varg) { sigset_t *set = varg; @@ -1642,7 +1645,7 @@ int main (int argc, char *argv[]) int opt; ddsrt_threadattr_t attr; ddsrt_thread_t pubtid, subtid, subpingtid, subpongtid; -#ifndef _WIN32 +#if !_WIN32 && !DDSRT_WITH_FREERTOS sigset_t sigset, osigset; ddsrt_thread_t sigtid; #endif @@ -1841,7 +1844,7 @@ int main (int argc, char *argv[]) /* I hate Unix signals in multi-threaded processes ... */ #ifdef _WIN32 signal (SIGINT, signal_handler); -#else +#elif !DDSRT_WITH_FREERTOS sigemptyset (&sigset); sigaddset (&sigset, SIGINT); sigaddset (&sigset, SIGTERM); @@ -1989,7 +1992,7 @@ int main (int argc, char *argv[]) #if _WIN32 signal_handler (SIGINT); -#else +#elif !DDSRT_WITH_FREERTOS { /* get the attention of the signal handler thread */ void (*osigint) (int);