diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab22e79..31f5976 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: cmake -S test -B build/ \ -G "Unix Makefiles" \ -DCMAKE_BUILD_TYPE=Debug \ - -DBUILD_CLONE_SUBMODULES=ON \ + -DBUILD_UNIT_TESTS=ON \ -DCMAKE_C_FLAGS='--coverage -Wall -Wextra -Werror' make -C build/ all - name: Test @@ -35,6 +35,15 @@ jobs: uses: FreeRTOS/CI-CD-Github-Actions/coverage-cop@main with: path: ./build/coverage.info + build-code-example: + runs-on: ubuntu-latest + steps: + - name: Clone This Repo + uses: actions/checkout@v2 + - name: Build code example + run: | + cmake -S test -B Build -DBUILD_CODE_EXAMPLE=ON + make -C Build code_example_posix -j8 complexity: runs-on: ubuntu-latest steps: diff --git a/docs/doxygen/code_examples/backoff_algorithm_posix.c b/docs/doxygen/code_examples/backoff_algorithm_posix.c new file mode 100644 index 0000000..978a63e --- /dev/null +++ b/docs/doxygen/code_examples/backoff_algorithm_posix.c @@ -0,0 +1,84 @@ +#include "backoff_algorithm.h" +#include +#include +#include +#include +#include + +/* The maximum number of retries for the example code. */ +#define RETRY_MAX_ATTEMPTS ( 5U ) + +/* The maximum back-off delay (in milliseconds) for between retries in the example. */ +#define RETRY_MAX_BACKOFF_DELAY_MS ( 5000U ) + +/* The base back-off delay (in milliseconds) for retry configuration in the example. */ +#define RETRY_BACKOFF_BASE_MS ( 500U ) + +int main() +{ + /* @[code_example_backoffalgorithm_initializeparams] */ + /* Variables used in this example. */ + BackoffAlgorithmStatus_t retryStatus = BackoffAlgorithmSuccess; + BackoffAlgorithmContext_t retryParams; + char serverAddress[] = "amazon.com"; + uint16_t nextRetryBackoff = 0; + + /* Initialize reconnect attempts and interval. */ + BackoffAlgorithm_InitializeParams( &retryParams, + RETRY_BACKOFF_BASE_MS, + RETRY_MAX_BACKOFF_DELAY_MS, + RETRY_MAX_ATTEMPTS ); + /* @[code_example_backoffalgorithm_initializeparams] */ + + int32_t dnsStatus = -1; + struct addrinfo hints; + struct addrinfo ** pListHead = NULL; + struct timespec tp; + + /* Add hints to retrieve only TCP sockets in getaddrinfo. */ + ( void ) memset( &hints, 0, sizeof( hints ) ); + + /* Address family of either IPv4 or IPv6. */ + hints.ai_family = AF_UNSPEC; + /* TCP Socket. */ + hints.ai_socktype = ( int32_t ) SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + /* @[code_example_backoffalgorithm_getnextbackoff] */ + + /* Seed the pseudo random number generator used in this example (with call to + * rand() function provided by ISO C standard library) for use in backoff period + * calculation when retrying failed DNS resolution. */ + + /* Get current time to seed pseudo random number generator. */ + ( void ) clock_gettime( CLOCK_REALTIME, &tp ); + /* Seed pseudo random number generator with seconds. */ + srand( tp.tv_sec ); + + do + { + /* Perform a DNS lookup on the given host name. */ + dnsStatus = getaddrinfo( serverAddress, NULL, &hints, pListHead ); + + /* Retry if DNS resolution query failed. */ + if( dnsStatus != 0 ) + { + /* Generate a random number and get back-off value (in milliseconds) for the next retry. + * Note: It is recommended to use a random number generator that is seeded with + * device-specific entropy source so that backoff calculation across devices is different + * and possibility of network collision between devices attempting retries can be avoided. + * + * For the simplicity of this code example, the pseudo random number generator, rand() + * function is used. */ + retryStatus = BackoffAlgorithm_GetNextBackoff( &retryParams, rand(), &nextRetryBackoff ); + + /* Wait for the calculated backoff period before the next retry attempt of querying DNS. + * As usleep() takes nanoseconds as the parameter, we multiply the backoff period by 1000. */ + ( void ) usleep( nextRetryBackoff * 1000U ); + } + } while( ( dnsStatus != 0 ) && ( retryStatus != BackoffAlgorithmRetriesExhausted ) ); + + /* @[code_example_backoffalgorithm_getnextbackoff] */ + + return dnsStatus; +} diff --git a/docs/doxygen/config.doxyfile b/docs/doxygen/config.doxyfile index 01da3ef..665f932 100644 --- a/docs/doxygen/config.doxyfile +++ b/docs/doxygen/config.doxyfile @@ -797,7 +797,7 @@ EXCLUDE_SYMLINKS = NO # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = source/include docs/doxygen/include +EXAMPLE_PATH = source/include docs/doxygen/include docs/doxygen/code_examples # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and diff --git a/docs/doxygen/pages.dox b/docs/doxygen/pages.dox index 89d5e3a..1c1b260 100644 --- a/docs/doxygen/pages.dox +++ b/docs/doxygen/pages.dox @@ -61,19 +61,34 @@ every path of execution and achieve 100% branch coverage.

*/ +/** +@page backoff_algorithm_example Code Example for backoffAlgorithm API +@brief Example POSIX application that retries DNS resolution operation with exponential backoff-and-jitter using the backoffAlgorithm library. + +@include backoff_algorithm_posix.c + */ + /** @page backoff_algorithm_functions Functions @brief Primary functions of the backoffAlgorithm library:

@subpage define_backoffalgorithm_initializeparams
@subpage define_backoffalgorithm_getnextbackoff
+For a code example of using backoffAlgorithm library for retrying operations with exponential back-off and jitter, refer to @ref backoff_algorithm_example. + @page define_backoffalgorithm_initializeparams BackoffAlgorithm_InitializeParams @snippet backoff_algorithm.h define_backoffalgorithm_initializeparams @copydoc BackoffAlgorithm_InitializeParams +From the @ref backoff_algorithm_example, following is the part relevant to the @ref BackoffAlgorithm_InitializeParams API. +@snippet backoff_algorithm_posix.c code_example_backoffalgorithm_initializeparams + @page define_backoffalgorithm_getnextbackoff BackoffAlgorithm_GetNextBackoff -@snippet backoff_algorithm.h define_backoffalgorithm_getnextbackoff @copydoc BackoffAlgorithm_GetNextBackoff + +From the @ref backoff_algorithm_example, following is the part relevant to the @ref BackoffAlgorithm_GetNextBackoff API. +@snippet backoff_algorithm_posix.c code_example_backoffalgorithm_getnextbackoff + */ diff --git a/lexicon.txt b/lexicon.txt index 4eadb40..5e17bca 100644 --- a/lexicon.txt +++ b/lexicon.txt @@ -41,9 +41,11 @@ os param pcontext pnextbackoff +posix pretrycontext pretryparams prng +rand randomvalue readme rm @@ -57,4 +59,5 @@ td toolchain tr trng -utils +usleep +utils \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 13aad94..becb94e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,8 +22,8 @@ set( UNIT_TEST_DIR ${MODULE_ROOT_DIR}/test/unit-test CACHE INTERNAL "backoffAlgo set( UNITY_DIR ${UNIT_TEST_DIR}/Unity CACHE INTERNAL "Unity library source directory." ) # Configure options to always show in CMake GUI. -option( BUILD_CLONE_SUBMODULES - "Set this to ON to automatically clone any required Git submodules. When OFF, submodules must be manually cloned." +option( BUILD_UNIT_TESTS + "Set this to ON to build unit tests. This will clone the required Unity test framework submodule if it is not cloned already." OFF ) # Set output directories. @@ -47,41 +47,53 @@ target_include_directories( coverity_analysis # ==================================== Unit Test Configuration ==================================== -# Include Unity build configuration. -include( unit-test/unity_build.cmake ) +if(${BUILD_CODE_EXAMPLE}) -# Check if the Unity source directory exists, and if not present, clone the submodule -# if BUILD_CLONE_SUBMODULES configuration is enabled. -if( NOT EXISTS ${UNITY_DIR}/src ) - # Attempt to clone Unity. - if( ${BUILD_CLONE_SUBMODULES} ) + # Target for code example binary. + add_executable( code_example_posix + ${MODULE_ROOT_DIR}/docs/doxygen/code_examples/backoff_algorithm_posix.c ) + + target_link_libraries( code_example_posix coverity_analysis ) + +endif() + +# ==================================== Unit Test Configuration ==================================== + +if(${BUILD_UNIT_TESTS}) + + # Include Unity build configuration. + include( unit-test/unity_build.cmake ) + + # Check if the Unity source directory exists, and if not present, clone the submodule + # if BUILD_CLONE_SUBMODULES configuration is enabled. + if( NOT EXISTS ${UNITY_DIR}/src ) + # Attempt to clone Unity. clone_unity() - else() - message( FATAL_ERROR "The required submodule Unity does not exist. Either clone it manually, or set BUILD_CLONE_SUBMODULES to 1 to automatically clone it during build." ) endif() -endif() -# Add unit test and coverage configuration. + # Add unit test and coverage configuration. -# Use CTest utility for managing test runs. This has to be added BEFORE -# defining test targets with add_test() -enable_testing() + # Use CTest utility for managing test runs. This has to be added BEFORE + # defining test targets with add_test() + enable_testing() -# Add build targets for Unity and Unit, required for unit testing. -add_unity_targets() + # Add build targets for Unity and Unit, required for unit testing. + add_unity_targets() -# Add function to enable Unity based tests and coverage. -include( ${MODULE_ROOT_DIR}/tools/unity/create_test.cmake ) + # Add function to enable Unity based tests and coverage. + include( ${MODULE_ROOT_DIR}/tools/unity/create_test.cmake ) -# Include build configuration for unit tests. -add_subdirectory( unit-test ) + # Include build configuration for unit tests. + add_subdirectory( unit-test ) -# ==================================== Coverage Analysis configuration ============================ + # ==================================== Coverage Analysis configuration ============================ -# Add a target for running coverage on tests. -add_custom_target( coverage - COMMAND ${CMAKE_COMMAND} -DUNITY_DIR=${UNITY_DIR} - -P ${MODULE_ROOT_DIR}/tools/unity/coverage.cmake - DEPENDS unity backoff_algorithm_utest - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} -) + # Add a target for running coverage on tests. + add_custom_target( coverage + COMMAND ${CMAKE_COMMAND} -DUNITY_DIR=${UNITY_DIR} + -P ${MODULE_ROOT_DIR}/tools/unity/coverage.cmake + DEPENDS unity backoff_algorithm_utest + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + +endif()