diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 209c976..f330540 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -17,7 +17,7 @@ on: jobs: build: - runs-on: macOS-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v1 name: checkout @@ -27,20 +27,29 @@ jobs: fetch-depth: 1 - name: install run: | - brew install ninja llvm + set -e + sudo apt-get update || true + sudo apt-get install -y ninja-build gcovr gcc-13 g++-13 clang-15 clang++-15 llvm-15 + + sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 90 + sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-15 999 + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 999 + sudo update-alternatives --install /usr/bin/clang clang /usr/lib/llvm-15/bin/clang-15 999 + sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-15 999 + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 90 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 90 + sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-13 90 + sudo python3 -m pip install --upgrade pip sudo python3 -m pip install scikit-build - sudo python3 -m pip install cmake requests gitpython gcovr pyyaml + sudo python3 -m pip install cmake==3.25 requests gitpython gcovr pyyaml - name: run checks run: | #!/bin/bash - LLVM_DIR=/usr/local/Cellar/llvm - test -d $LLVM_DIR || echo $(echo "llvm is absent, cannot continue" && exit 1) - VER_COUNT=$(ls -1 ${LLVM_DIR} | wc -l) + LLVM_DIR=/usr/lib/llvm-?? + VER_COUNT=$(ls -d -1 ${LLVM_DIR} | wc -l) test ${VER_COUNT} -eq 0 && echo "no llvm version detected" && exit 1 - test $VER_COUNT -gt 1 && echo "wrong llvm installation" && exit 1 - LLVM_VER=$(ls -1 ${LLVM_DIR}) - export LLVM_ROOT=${LLVM_DIR}/${LLVM_VER} + export LLVM_ROOT=$(ls -r -d -1 ${LLVM_DIR} | head -1) export PATH=${LLVM_ROOT}/bin:${LLVM_ROOT}/share/clang:${PATH} - cmake . -GNinja -Bbuild + cmake . -Bbuild .github/aux/clang-tidy.sh build diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index e29efa4..4a5b007 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -21,16 +21,16 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macOS-latest] + os: [ubuntu-22.04, macOS-14] compiler: [{ "cc": "gcc", "cxx": "g++" }, { "cc": "clang", "cxx": "clang++" - }] + }] exclude: - - os: macOS-latest + - os: macOS-14 compiler: cc: gcc steps: @@ -49,11 +49,20 @@ jobs: brew install ninja else sudo apt-get update || true - sudo apt-get install -y ninja-build + sudo apt-get install -y ninja-build gcc-13 g++-13 clang-15 clang++-15 + sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 90 + sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-15 999 + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 999 + sudo update-alternatives --install /usr/bin/clang clang /usr/lib/llvm-15/bin/clang-15 999 + sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-15 999 + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 90 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 90 + sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-13 90 fi sudo python3 -m pip install --upgrade pip - sudo pip3 install cmake requests gitpython gcovr pyyaml + sudo python3 -m pip install scikit-build + sudo python3 -m pip install cmake==3.25 requests gitpython gcovr pyyaml - name: cmake env: @@ -62,7 +71,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # has to be included to access other secrets GITHUB_HUNTER_USERNAME: ${{ secrets.GITHUB_HUNTER_USERNAME }} GITHUB_HUNTER_TOKEN: ${{ secrets.GITHUB_HUNTER_TOKEN }} - run: cmake . -GNinja -Bbuild + run: cmake . -Bbuild - name: build run: cmake --build build -- -j4 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 217d65e..f813a76 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -18,7 +18,7 @@ on: jobs: coverage: name: "codecov" - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: checkout uses: actions/checkout@v1 @@ -30,16 +30,27 @@ jobs: run: | set -e sudo apt-get update || true - sudo apt-get install -y ninja-build gcovr + sudo apt-get install -y ninja-build gcovr gcc-13 g++-13 clang-15 clang++-15 + + sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 90 + sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-15 999 + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 999 + sudo update-alternatives --install /usr/bin/clang clang /usr/lib/llvm-15/bin/clang-15 999 + sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-15 999 + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 90 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 90 + sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-13 90 + sudo python3 -m pip install --upgrade pip - sudo pip3 install scikit-build - sudo pip3 install cmake requests gitpython gcovr pyyaml + sudo python3 -m pip install scikit-build + sudo python3 -m pip install cmake==3.25 requests gitpython gcovr pyyaml + - name: "cmake" env: CC: clang CXX: clang++ run: | - cmake . -GNinja -Bbuild-coverage -DCOVERAGE=ON -DEXAMPLES=OFF + cmake . -Bbuild-coverage -DCOVERAGE=ON -DEXAMPLES=OFF cmake --build build-coverage - name: "build report" env: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 54cbae9..503309f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -38,7 +38,7 @@ jobs: - name: "Undefined Behavior Sanitizer" sanitizer: UBSAN name: "${{ matrix.options.name }}" - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: checkout uses: actions/checkout@v1 @@ -62,7 +62,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # has to be included to access other secrets GITHUB_HUNTER_USERNAME: ${{ secrets.GITHUB_HUNTER_USERNAME }} GITHUB_HUNTER_TOKEN: ${{ secrets.GITHUB_HUNTER_TOKEN }} - run: cmake . -GNinja -Bbuild -D${{ matrix.options.sanitizer }}=ON + run: cmake . -Bbuild -D${{ matrix.options.sanitizer }}=ON - name: build run: cmake --build build -- -j4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 02b6854..47a3280 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ cmake_minimum_required(VERSION 3.12) include("cmake/Hunter/init.cmake") cmake_policy(SET CMP0048 NEW) -project(soralog VERSION 0.2.1 LANGUAGES CXX) +project(soralog VERSION 0.2.2 LANGUAGES CXX) find_program(CCACHE_FOUND ccache) if (CCACHE_FOUND) diff --git a/cmake/Hunter/init.cmake b/cmake/Hunter/init.cmake index 58397f9..b275bf4 100644 --- a/cmake/Hunter/init.cmake +++ b/cmake/Hunter/init.cmake @@ -27,14 +27,14 @@ set( set( HUNTER_CACHE_SERVERS - "https://github.com/qdrvm/hunter-binary-cache;https://github.com/soramitsu/hunter-binary-cache" + "https://github.com/qdrvm/hunter-binary-cache" CACHE STRING "Binary cache server" ) include(${CMAKE_CURRENT_LIST_DIR}/HunterGate.cmake) HunterGate( - URL https://github.com/qdrvm/hunter/archive/refs/tags/v0.23.257-qdrvm3.zip - SHA1 6bfad5e6ec7b6821814b1bd0c9d3c4fef72c6731 + URL https://github.com/qdrvm/hunter/archive/refs/tags/v0.25.3-qdrvm3.zip + SHA1 8989599eaa462f367805e2d36a30150c93b1d660 LOCAL ) diff --git a/cmake/toolchain/compiler/clang.cmake b/cmake/toolchain/compiler/clang.cmake index eb58cf6..aa5be39 100644 --- a/cmake/toolchain/compiler/clang.cmake +++ b/cmake/toolchain/compiler/clang.cmake @@ -64,8 +64,8 @@ set( ) string(REGEX MATCH "([0-9]+).([0-9]+).([0-9]+)" v ${CMAKE_CXX_COMPILER_VERSION}) -if (${CMAKE_MATCH_1} LESS 11) - print("Requires Clang compiler at least version 11") +if (${CMAKE_MATCH_1} LESS 15) + print("Requires Clang compiler at least version 15") endif() if (${CMAKE_MATCH_1} GREATER_EQUAL 10) diff --git a/cmake/toolchain/flags/sanitize_thread.cmake b/cmake/toolchain/flags/sanitize_thread.cmake index 1ba2fb8..a9e7a4d 100644 --- a/cmake/toolchain/flags/sanitize_thread.cmake +++ b/cmake/toolchain/flags/sanitize_thread.cmake @@ -14,6 +14,7 @@ endif () set(FLAGS -fsanitize=thread -g + -O1 ) foreach(FLAG IN LISTS FLAGS) diff --git a/include/soralog/circular_buffer.hpp b/include/soralog/circular_buffer.hpp index e55d7fa..7ed69e5 100644 --- a/include/soralog/circular_buffer.hpp +++ b/include/soralog/circular_buffer.hpp @@ -26,6 +26,7 @@ namespace soralog { public: using element_type = T; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init) struct Node final { template void init(Args &&...args) { @@ -36,9 +37,11 @@ namespace soralog { return *reinterpret_cast(item_); } + // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) std::atomic_flag busy = ATOMIC_VAR_INIT(false); private: + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) alignas(std::alignment_of_v) char item_[sizeof(T)]; }; @@ -98,6 +101,7 @@ namespace soralog { } }; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init) explicit CircularBuffer(size_t capacity) : CircularBuffer(capacity, 0){}; size_t capacity() const noexcept { diff --git a/include/soralog/event.hpp b/include/soralog/event.hpp index 8ae370d..7417c28 100644 --- a/include/soralog/event.hpp +++ b/include/soralog/event.hpp @@ -71,11 +71,13 @@ namespace soralog { return *pos; } constexpr auto &operator++() { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) ++pos; return *this; } constexpr auto operator++(int) { auto origin = *this; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) ++pos; return origin; } diff --git a/include/soralog/impl/sink_to_console.hpp b/include/soralog/impl/sink_to_console.hpp index b00f819..f8ab384 100644 --- a/include/soralog/impl/sink_to_console.hpp +++ b/include/soralog/impl/sink_to_console.hpp @@ -57,7 +57,7 @@ namespace soralog { std::atomic_bool need_to_flush_ = false; std::atomic next_flush_ = std::chrono::steady_clock::time_point(); - std::atomic_bool flush_in_progress_ = false; + std::atomic_flag flush_in_progress_ = false; }; } // namespace soralog diff --git a/include/soralog/impl/sink_to_file.hpp b/include/soralog/impl/sink_to_file.hpp index a9e42a5..08fd6e6 100644 --- a/include/soralog/impl/sink_to_file.hpp +++ b/include/soralog/impl/sink_to_file.hpp @@ -58,7 +58,7 @@ namespace soralog { std::atomic_bool need_to_rotate_ = false; std::atomic next_flush_ = std::chrono::steady_clock::time_point(); - std::atomic_bool flush_in_progress_ = false; + std::atomic_flag flush_in_progress_ = false; }; } // namespace soralog diff --git a/include/soralog/impl/sink_to_syslog.hpp b/include/soralog/impl/sink_to_syslog.hpp index 8fcfe77..2435752 100644 --- a/include/soralog/impl/sink_to_syslog.hpp +++ b/include/soralog/impl/sink_to_syslog.hpp @@ -55,7 +55,7 @@ namespace soralog { std::atomic_bool need_to_flush_ = false; std::atomic next_flush_ = std::chrono::steady_clock::time_point(); - std::atomic_bool flush_in_progress_ = false; + std::atomic_flag flush_in_progress_ = false; }; } // namespace soralog diff --git a/include/soralog/logger.hpp b/include/soralog/logger.hpp index bef725d..8105f71 100644 --- a/include/soralog/logger.hpp +++ b/include/soralog/logger.hpp @@ -40,7 +40,8 @@ namespace soralog { * name and event's data ({@param format} and {@param args}) to sink */ template - void push(Level level, const Format &format, const Args &...args) { + void __attribute__((no_sanitize("thread"))) + push(Level level, const Format &format, const Args &...args) { if (level_ >= level) { if (level != Level::OFF and level != Level::IGNORE) { sink_->push(name_, level, format, args...); diff --git a/include/soralog/sink.hpp b/include/soralog/sink.hpp index 85b6fd2..366c0a3 100644 --- a/include/soralog/sink.hpp +++ b/include/soralog/sink.hpp @@ -22,17 +22,17 @@ #define IF_RELEASE false #endif -#if not defined(likely_if) -#if __cplusplus > 201703L -#define likely_if(x) [[likely]] if (x) +#if not defined(LIKELY_IF) +#if __cplusplus >= 202002L +#define LIKELY_IF(x) [[likely]] if (x) #elif defined(__has_builtin) #if __has_builtin(__builtin_expect) -#define likely_if(x) if (__builtin_expect((x), 1)) +#define LIKELY_IF(x) if (__builtin_expect((x), 1)) #else -#define likely_if(x) if (x) +#define LIKELY_IF(x) if (x) #endif #else -#define likely_if(x) if (x) +#define LIKELY_IF(x) if (x) #endif #endif @@ -107,7 +107,7 @@ namespace soralog { max_message_length_, args...); // Event is queued successfully - likely_if((bool)node) { + LIKELY_IF((bool)node) { size_ += node->message().size(); break; } @@ -165,4 +165,4 @@ namespace soralog { } // namespace soralog -#undef likely_if +#undef LIKELY_IF diff --git a/src/impl/sink_to_console.cpp b/src/impl/sink_to_console.cpp index 60bce98..7e7b048 100644 --- a/src/impl/sink_to_console.cpp +++ b/src/impl/sink_to_console.cpp @@ -171,9 +171,7 @@ namespace soralog { } void SinkToConsole::flush() noexcept { - bool false_v = false; - if (not flush_in_progress_.compare_exchange_strong( - false_v, true, std::memory_order_acq_rel)) { + if (flush_in_progress_.test_and_set()) { return; } @@ -305,7 +303,7 @@ namespace soralog { break; } - flush_in_progress_.store(false, std::memory_order_release); + flush_in_progress_.clear(); } void SinkToConsole::run() { diff --git a/src/impl/sink_to_file.cpp b/src/impl/sink_to_file.cpp index 3eebda2..84e5ecd 100644 --- a/src/impl/sink_to_file.cpp +++ b/src/impl/sink_to_file.cpp @@ -111,9 +111,7 @@ namespace soralog { } void SinkToFile::flush() noexcept { - bool false_v = false; - if (not flush_in_progress_.compare_exchange_strong( - false_v, true, std::memory_order_acq_rel)) { + if (flush_in_progress_.test_and_set()) { return; } @@ -228,7 +226,7 @@ namespace soralog { } } - flush_in_progress_.store(false, std::memory_order_release); + flush_in_progress_.clear(); } void SinkToFile::rotate() noexcept { diff --git a/src/impl/sink_to_syslog.cpp b/src/impl/sink_to_syslog.cpp index 86fc78c..3f1afaa 100644 --- a/src/impl/sink_to_syslog.cpp +++ b/src/impl/sink_to_syslog.cpp @@ -119,9 +119,7 @@ namespace soralog { } void SinkToSyslog::flush() noexcept { - bool false_v = false; - if (not flush_in_progress_.compare_exchange_strong( - false_v, true, std::memory_order_acq_rel)) { + if (flush_in_progress_.test_and_set()) { return; } @@ -239,7 +237,7 @@ namespace soralog { } } - flush_in_progress_.store(false, std::memory_order_release); + flush_in_progress_.clear(); } void SinkToSyslog::run() { diff --git a/test/unit/circular_buffer_test.cpp b/test/unit/circular_buffer_test.cpp index 397980d..954d6cd 100644 --- a/test/unit/circular_buffer_test.cpp +++ b/test/unit/circular_buffer_test.cpp @@ -7,8 +7,8 @@ #include -#if __cplusplus > 201703L -#include +#if __cplusplus >= 202002L +#include #endif #include "soralog/impl/sink_to_file.hpp" @@ -159,13 +159,13 @@ TEST_F(CircularBufferTest, PutGetMt) { std::atomic_size_t i = 0; std::atomic_size_t n = 100; -#if __cplusplus > 201703L - std::barrier barrier(3); +#if __cplusplus >= 202002L + std::latch latch(3); #endif std::thread prod([&] { -#if __cplusplus > 201703L - barrier.arrive_and_wait(); +#if __cplusplus >= 202002L + latch.arrive_and_wait(); #endif while (i < n) { std::cout << "w" << i << std::endl; @@ -181,8 +181,8 @@ TEST_F(CircularBufferTest, PutGetMt) { }); std::thread cons([&] { -#if __cplusplus > 201703L - barrier.arrive_and_wait(); +#if __cplusplus >= 202002L + latch.arrive_and_wait(); #endif while (i < n) { std::cout << "r" << std::endl; @@ -195,8 +195,8 @@ TEST_F(CircularBufferTest, PutGetMt) { } }); -#if __cplusplus > 201703L - barrier.arrive_and_wait(); +#if __cplusplus >= 202002L + latch.arrive_and_wait(); #endif prod.join(); cons.join(); @@ -207,13 +207,13 @@ TEST_F(CircularBufferTest, Mutual) { CircularBuffer testee(capacity); -#if __cplusplus > 201703L - std::barrier barrier(3); +#if __cplusplus >= 202002L + std::latch latch(3); #endif std::thread prod([&] { -#if __cplusplus > 201703L - barrier.arrive_and_wait(); +#if __cplusplus >= 202002L + latch.arrive_and_wait(); #endif if (auto ref = testee.put('*')) { std::cout << "put " << ref->c() // @@ -224,8 +224,8 @@ TEST_F(CircularBufferTest, Mutual) { }); std::thread cons([&] { -#if __cplusplus > 201703L - barrier.arrive_and_wait(); +#if __cplusplus >= 202002L + latch.arrive_and_wait(); #endif std::this_thread::sleep_for(std::chrono::milliseconds(50)); if (auto ref = testee.get()) { @@ -235,8 +235,8 @@ TEST_F(CircularBufferTest, Mutual) { } }); -#if __cplusplus > 201703L - barrier.arrive_and_wait(); +#if __cplusplus >= 202002L + latch.arrive_and_wait(); #endif prod.join(); cons.join(); diff --git a/test/unit/macros_test.cpp b/test/unit/macros_test.cpp index 781ffbf..7a109a2 100644 --- a/test/unit/macros_test.cpp +++ b/test/unit/macros_test.cpp @@ -21,7 +21,7 @@ class MacrosTest : public ::testing::Test { void log(Level lvl, const Format &format, Args &&...args) { last_level = lvl; size_t len = - fmt::vformat_to_n( + ::fmt::vformat_to_n( message_buf.begin(), message_buf.size(), ::fmt::detail_exported::compile_string_to_view(format), ::fmt::make_format_args(args...)) diff --git a/test/unit/sink_to_console_test.cpp b/test/unit/sink_to_console_test.cpp index 74440b8..1ed3e9f 100644 --- a/test/unit/sink_to_console_test.cpp +++ b/test/unit/sink_to_console_test.cpp @@ -9,6 +9,10 @@ #include "soralog/impl/sink_to_console.hpp" +#if __cplusplus >= 202002L +#include +#endif + using namespace soralog; using namespace testing; using namespace std::chrono_literals; @@ -37,7 +41,7 @@ class SinkToConsoleTest : public ::testing::Test { "console", SinkToConsole::Stream::STDOUT, // standard output stream false, // no color - Sink::ThreadInfoType::NONE, // ignore thread info + Sink::ThreadInfoType::ID, // ignore thread info 4, // capacity: 4 events 64, // max message length: 64 byte 16384, // buffers size: 16 Kb @@ -101,3 +105,52 @@ TEST_F(SinkToConsoleTest, ZeroLatencyLogging) { std::this_thread::sleep_for(delay); logger->flush(); } + +/** + * @given Sink with zero-latency + * @when Push four message to log every half-second + * @then Messages will be written separately + * @note Test is disabled, because it should be started and watched manually + */ +TEST_F(SinkToConsoleTest, MultithreadLogging) { + auto logger = createLogger(40ms); + + size_t treads_n = 10; + size_t iters_n = 100; + +#if __cplusplus >= 202002L + std::latch latch(treads_n); +#endif + + auto task = [&] { +#if __cplusplus >= 202002L + latch.arrive_and_wait(); +#endif + std::mutex m; + for (auto i = 0; i < iters_n; ++i) { + logger->debug("iteration {}.1", i); + logger->debug("iteration {}.2", i); + logger->debug("iteration {}.3", i); + logger->debug("iteration {}.4", i); + logger->debug("iteration {}.5", i); + logger->debug("iteration {}.6", i); + logger->debug("iteration {}.7", i); + std::unique_lock l(m); + logger->debug("iteration {}.8", i); + logger->debug("iteration {}.9", i); + logger->debug("iteration {}.0", i); + } + }; + + std::vector threads; + for (auto i = 0; i < treads_n; ++i) { + threads.emplace_back([&] { + // soralog::util::setThreadName(fmt::format("{}", i)); + task(); + }); + } + + for (auto &t : threads) { + t.join(); + } +}