Skip to content

Commit

Permalink
New files, reorg files, enhance doc
Browse files Browse the repository at this point in the history
  • Loading branch information
cliffg-softwarelibre committed May 20, 2024
1 parent 2fea241 commit 24691a2
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 57 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/build_run_unit_test_cmake.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Build, run unit tests
name: CMake build and run unit test matrix

on:
push:
branches:
- main
- develop
env:
BUILD_TYPE: Release
jobs:
build_matrix:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-14]
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- name: checkout
uses: actions/checkout@v4
- name: create-build-dir
run: mkdir build
- name: configure-cmake
run: cd build && cmake -D WAIT_QUEUE_BUILD_TESTS:BOOL=ON -D JM_CIRCULAR_BUFFER_BUILD_TESTS:BOOL=OFF ..
- name: build
run: cd build && cmake --build . --config $BUILD_TYPE
- name: run-unit-test
run: cd build && ctest -C $BUILD_TYPE
28 changes: 28 additions & 0 deletions .github/workflows/gen_docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Build, run unit tests
name: Generate documentation

on:
push:
branches:
- main
jobs:
build_matrix:
strategy:
matrix:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- name: checkout
uses: actions/checkout@v4
- name: run-doxygen
uses: mattnotmitt/[email protected]
with:
working-directory: doc
- name: deploy-pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./doc/doc_output/html
30 changes: 18 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,51 @@
#
# I'm still learning CMake, so improvement suggestions are always welcome.
#
# The Asio CMake code is taken from CPM.cmake examples/asio-standalone.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

cmake_minimum_required ( VERSION 3.14 FATAL_ERROR )

project ( periodic_timer
LANGUAGES CXX
DESCRIPTION "A multi producer / multi consumer thread safe wait queue"
HOMEPAGE_URL "https://githug.com/connectivecpp/wait-queue/" )
DESCRIPTION "An asynchronous periodic timer based on Asio"
HOMEPAGE_URL "https://github.com/connectivecpp/periodic-timer/" )

option ( WAIT_QUEUE_BUILD_TESTS "Build unit tests" OFF )
option ( WAIT_QUEUE_BUILD_EXAMPLES "Build examples" OFF )
option ( WAIT_QUEUE_INSTALL "Install header only library" OFF )
option ( PERIODIC_TIMER_BUILD_TESTS "Build unit tests" OFF )
option ( PERIODIC_TIMER_BUILD_EXAMPLES "Build examples" OFF )
option ( PERIODIC_TIMER_INSTALL "Install header only library" OFF )

# add library targets

add_library ( wait_queue INTERFACE )
add_library ( chops::wait_queue ALIAS wait_queue )
add_library ( periodic_timer INTERFACE )
add_library ( chops::periodic_timer ALIAS periodic_timer )

# thread support specified in Asio download

include ( cmake/download_asio_cpm.cmake )

# configure library target

target_include_directories ( wait_queue INTERFACE
target_include_directories ( periodic_timer INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
$<INSTALL_INTERFACE:include/> )
target_compile_features ( wait_queue INTERFACE cxx_std_20 )
target_compile_features ( periodic_timer INTERFACE cxx_std_20 )

# check to build unit tests
if ( ${WAIT_QUEUE_BUILD_TESTS} )
if ( ${PERIODIC_TIMER_BUILD_TESTS} )
enable_testing()
add_subdirectory ( test )
endif ()

# check to build example code
if ( ${WAIT_QUEUE_BUILD_EXAMPLES} )
if ( ${PERIODIC_TIMER_BUILD_EXAMPLES} )
add_subdirectory ( example )
endif ()

# check to install
if ( ${WAIT_QUEUE_INSTALL} )
if ( ${PERIODIC_TIMER_INSTALL} )
set ( CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt )
include ( CPack )
endif ()
Expand Down
67 changes: 66 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,67 @@
# periodic-timer
# Periodic Timer, a Header-Only C++ 20 Asynchronous Periodic Timer

#### Unit Test and Documentation Generation Workflow Status

![GH Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/connectivecpp/periodic-timer/build_run_unit_test_cmake.yml?branch=main&label=GH%20Actions%20build,%20unit%20tests%20on%20main)

![GH Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/connectivecpp/periodic-timer/build_run_unit_test_cmake.yml?branch=develop&label=GH%20Actions%20build,%20unit%20tests%20on%20develop)

![GH Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/connectivecpp/periodic-timer/gen_docs.yml?branch=main&label=GH%20Actions%20generate%20docs)

## Overview

`periodic_timer` is an asynchronous periodic timer that wraps and simplifies Asio timers (see [References](https://connectivecpp.github.io/doc/references.html)) when periodic callbacks are needed. The periodicity can be based on either a simple duration or on timepoints based on a duration.

Asynchronous timers from Asio are relatively easy to use. However, there are no timers that are periodic. This class simplifies the usage, using application supplied function object callbacks. When the timer is started, the application specifies whether each callback is invoked based on a duration (e.g. one second after the last callback), or on timepoints (e.g. a callback will be invoked each second according to the clock).

An asynchronous periodic timer simplifying Asio timers, with function object timer callbacks

## Generated Documentation

The generated Doxygen documentation for `periodic_timer` is [here](https://connectivecpp.github.io/periodic-timer/).

## Dependencies

The `periodic_timer` header file has the stand-alone Asio library for a dependency. Specific version (or branch) specs for the Asio dependency is in `test/CMakeLists.txt`.

## C++ Standard

`periodic_timer` uses C++ 20 features, including `std::stop_token`, `std::stop_source`, `std::condition_variable_any`, `std::scoped_lock`, and `concepts` / `requires`.

## Supported Compilers

Continuous integration workflows build and unit test on g++ (through Ubuntu) and MSVC (through Windows). Note that clang support for C++ 20 `std::jthread` and `std::stop_token` is still experimental (and possibly incomplete) as of May 2024, so has not (yet) been tested with `periodic_timer`.

## Unit Test Dependencies

The unit test code uses [Catch2](https://github.com/catchorg/Catch2). If the `WAIT_QUEUE_BUILD_TESTS` flag is provided to Cmake (see commands below) the Cmake configure / generate will download the Catch2 library as appropriate using the [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) dependency manager. If Catch2 (v3 or greater) is already installed using a different package manager (such as Conan or vcpkg), the `CPM_USE_LOCAL_PACKAGES` variable can be set which results in `find_package` being attempted. Note that v3 (or later) of Catch2 is required.

The unit test uses two third-party libraries (each is a single header-only file):

- Martin Moene's [ring-span-lite](https://github.com/martinmoene/ring-span-lite)
- Justas Masiulis' [circular_buffer](https://github.com/JustasMasiulis/circular_buffer)

Specific version (or branch) specs for the dependencies are in `test/CMakeLists.txt`.

## Build and Run Unit Tests

To build and run the unit test program:

First clone the `periodic-timer` repository, then create a build directory in parallel to the `wait-queue` directory (this is called "out of source" builds, which is recommended), then `cd` (change directory) into the build directory. The CMake commands:

```
cmake -D WAIT_QUEUE_BUILD_TESTS:BOOL=ON -D JM_CIRCULAR_BUFFER_BUILD_TESTS:BOOL=OFF ../wait-queue
cmake --build .
ctest
```

For additional test output, run the unit test individually, for example:

```
test/periodic_timer_test -s
```

The example can be built by adding `-D WAIT_QUEUE_BUILD_EXAMPLES:BOOL=ON` to the CMake configure / generate step.

64 changes: 64 additions & 0 deletions cmake/download_asio_cpm.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# This Asio CMake code is taken from CPM.cmake examples/asio-standalone.
#
# This code assumes it is run from a top-level CMakeLists.txt file,
# with a download_cpm.cmake file in a parallel cmake directory.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

set ( CMAKE_THREAD_PREFER_PTHREAD TRUE )
set ( THREADS_PREFER_PTHREAD_FLAG TRUE )
find_package ( Threads REQUIRED )

include ( cmake/download_cpm.cmake )
CPMAddPackage ( "gh:chriskohlhoff/asio#[email protected]" )

# ASIO doesn't use CMake, we have to configure it manually. Extra notes for using on Windows:
#
# 1) If _WIN32_WINNT is not set, ASIO assumes _WIN32_WINNT=0x0501, i.e. Windows XP target, which is
# definitely not the platform which most users target.
#
# 2) WIN32_LEAN_AND_MEAN is defined to make Winsock2 work.

if(asio_ADDED)
add_library(asio INTERFACE)

target_include_directories(asio SYSTEM INTERFACE ${asio_SOURCE_DIR}/asio/include)

target_compile_definitions(asio INTERFACE ASIO_STANDALONE ASIO_NO_DEPRECATED)

target_link_libraries(asio INTERFACE Threads::Threads)

if(WIN32)
# macro see @ https://stackoverflow.com/a/40217291/1746503
macro(get_win32_winnt version)
if(CMAKE_SYSTEM_VERSION)
set(ver ${CMAKE_SYSTEM_VERSION})
string(REGEX MATCH "^([0-9]+).([0-9])" ver ${ver})
string(REGEX MATCH "^([0-9]+)" verMajor ${ver})
# Check for Windows 10, b/c we'll need to convert to hex 'A'.
if("${verMajor}" MATCHES "10")
set(verMajor "A")
string(REGEX REPLACE "^([0-9]+)" ${verMajor} ver ${ver})
endif("${verMajor}" MATCHES "10")
# Remove all remaining '.' characters.
string(REPLACE "." "" ver ${ver})
# Prepend each digit with a zero.
string(REGEX REPLACE "([0-9A-Z])" "0\\1" ver ${ver})
set(${version} "0x${ver}")
endif()
endmacro()

if(NOT DEFINED _WIN32_WINNT)
get_win32_winnt(ver)
set(_WIN32_WINNT ${ver})
endif()

message(STATUS "Set _WIN32_WINNET=${_WIN32_WINNT}")

target_compile_definitions(asio INTERFACE _WIN32_WINNT=${_WIN32_WINNT} WIN32_LEAN_AND_MEAN)
endif()
endif()

# end of file

50 changes: 23 additions & 27 deletions include/timer/periodic_timer.hpp
Original file line number Diff line number Diff line change
@@ -1,40 +1,36 @@
/** @file
/** @mainpage Periodic Timer, Lightweight Timer Utility Using Asio
*
* @defgroup timer_module Classes and functions for timer functionality.
*
* based periods.
*
* @ingroup timer_module
* Writing code using asynchronous timers from the Asio library is relatively easy.
* However, there are no timers that are periodic. This class simplifies the task,
* using application supplied function object callbacks.
*
* @brief An asynchronous periodic timer providing both duration and timepoint
* based periods.
* A @c periodic_timer stops when the application supplied function object
* returns @c false rather than @c true.
*
* Writing code using asynchronous timers from the Asio library is relatively easy.
* However, there are no timers that are periodic. This class simplifies the task,
* using application supplied function object callbacks.
* A periodic timer can be used as a "one-shot" timer by finishing after
* one invocation (i.e. unconditionally return @c false from the function
* object).
*
* A @c periodic_timer stops when the application supplied function object
* returns @c false rather than @c true.
* @note This class does not perform "this" reference counting. It is up to
* the application code to guarantee that a @c periodic_timer has not been
* destructed before handlers (function object callbacks) are invoked.
*
* A periodic timer can be used as a "one-shot" timer by finishing after
* one invocation (i.e. unconditionally return @c false from the function
* object).
*
* @note This class does not perform "this" reference counting. It is up to
* the application code to guarantee that a @c periodic_timer has not been
* destructed before handlers (function object callbacks) are invoked.
*
* A common idiom is to use @c std::enable_shared_from_this, call
* @c std::shared_from_this, and store the result in the function object
* callback object.
* A common idiom is to use @c std::enable_shared_from_this, call
* @c std::shared_from_this, and store the result in the function object
* callback object.
*
* @note @c std::chrono facilities seem to be underspecified on @c noexcept,
* very few of the functions in @c periodic_timer are @c noexcept.
* @note @c std::chrono facilities seem to be underspecified on @c noexcept,
* very few of the functions in @c periodic_timer are @c noexcept.
*
* @author Cliff Green
* @author Cliff Green
*
* Copyright (c) 2017-2019 by Cliff Green
* Copyright (c) 2017-2024 by Cliff Green
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*
*/

Expand Down
18 changes: 7 additions & 11 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,29 @@
cmake_minimum_required ( VERSION 3.14 FATAL_ERROR )

# create project
project ( wait_queue_test LANGUAGES CXX )
project ( periodic_timer_test LANGUAGES CXX )

# add executable
add_executable ( wait_queue_test wait_queue_test.cpp )
target_compile_features ( wait_queue_test PRIVATE cxx_std_20 )
add_executable ( periodic_timer_test periodic_timer_test.cpp )
target_compile_features ( periodic_timer_test PRIVATE cxx_std_20 )

# add dependencies
include ( ../cmake/download_cpm.cmake )

CPMAddPackage ( "gh:catchorg/[email protected]" )
CPMAddPackage ( "gh:martinmoene/[email protected]" )
# CPMAddPackage ( "gh:JustasMasiulis/circular_buffer@master" )
CPMAddPackage ( NAME circular_buffer
URL https://github.com/JustasMasiulis/circular_buffer/archive/refs/heads/master.zip )

set ( CMAKE_THREAD_PREFER_PTHREAD TRUE )
set ( THREADS_PREFER_PTHREAD_FLAG TRUE )
find_package ( Threads REQUIRED )

# link dependencies
target_link_libraries ( wait_queue_test PRIVATE
Threads::Threads wait_queue ring-span-lite circular_buffer Catch2::Catch2WithMain )
target_link_libraries ( periodic_timer_test PRIVATE
Threads::Threads periodic_timer asio Catch2::Catch2WithMain )

enable_testing()

add_test ( NAME run_wait_queue_test COMMAND wait_queue_test )
set_tests_properties ( run_wait_queue_test
add_test ( NAME run_periodic_timer_test COMMAND periodic_timer_test )
set_tests_properties ( run_periodic_timer_test
PROPERTIES PASS_REGULAR_EXPRESSION "All tests passed"
)

9 changes: 3 additions & 6 deletions test/periodic_timer_test.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
/** @file
*
* @ingroup test_module
*
* @brief Test scenarios for @c periodic_timer class template.
*
* @author Cliff Green
*
* Copyright (c) 2017-2018 by Cliff Green
* Copyright (c) 2017-2024 by Cliff Green
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Expand All @@ -15,7 +13,7 @@

#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER

#include "catch2/catch.hpp"
#include "catch2/catch_test_macros.hpp"


#include <chrono>
Expand All @@ -24,10 +22,9 @@
#include <system_error>

#include "asio/executor_work_guard.hpp"
#include "asio/executor.hpp"
#include "asio/executor_work_guard.hpp"

#include "timer/periodic_timer.hpp"
#include "utility/repeat.hpp"

constexpr int Expected = 9;
int count = 0;
Expand Down

0 comments on commit 24691a2

Please sign in to comment.