diff --git a/CMakeLists.txt b/CMakeLists.txt index 58fa73f..7e713e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") -Wpedantic -Wconversion -Wshadow + -fno-rtti ) endif() @@ -35,11 +36,12 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) # Add Chapters # ------------------------------------------------------------ +add_subdirectory(SetupChapter) add_subdirectory(Common) -add_subdirectory(Chapter4-TypeErasure) -add_subdirectory(Chapter8-SensorPipelines) -add_subdirectory(Chapter9-SystemController) -add_subdirectory(Chapter10-Reactor) -add_subdirectory(Chapter11-FusionTiming) -add_subdirectory(Chapter12-DeterministicFusionAndOutput) +add_subdirectory(Chapter3-TypeErasure) +add_subdirectory(Chapter7-SensorPipelines) +add_subdirectory(Chapter8-SystemCompositionAndControl) +add_subdirectory(Chapter9-IntegratingExternalInputs) +add_subdirectory(Chapter10-FusionTiming) +add_subdirectory(Chapter11-DeterministicFusionAndOutput) add_subdirectory(ExternalDataSource) diff --git a/Chapter7-SensorPipelines/CMakeLists.txt b/Chapter7-SensorPipelines/CMakeLists.txt index 6846e3c..42298de 100644 --- a/Chapter7-SensorPipelines/CMakeLists.txt +++ b/Chapter7-SensorPipelines/CMakeLists.txt @@ -10,6 +10,8 @@ add_executable(SensorPipelineDemo include/PositionProducer.h include/PositionConsumer.h include/SensorPipeline.h + include/BurstySensorPipelineIntervals.h + include/DefaultSensorPipelineIntervals.h src/SensorPipeline.cpp src/PositionConsumer.cpp src/PositionProducer.cpp diff --git a/Chapter7-SensorPipelines/README.md b/Chapter7-SensorPipelines/README.md index 00891d1..2c9f820 100644 --- a/Chapter7-SensorPipelines/README.md +++ b/Chapter7-SensorPipelines/README.md @@ -1 +1 @@ -This repository accompanies Chapter 8 of _Designing Deterministic Systems with Modern C++_. +This repository accompanies Chapter 7 of _Designing Deterministic Systems with Modern C++_. diff --git a/Chapter7-SensorPipelines/include/BurstySensorPipelineIntervals.h b/Chapter7-SensorPipelines/include/BurstySensorPipelineIntervals.h new file mode 100644 index 0000000..838baca --- /dev/null +++ b/Chapter7-SensorPipelines/include/BurstySensorPipelineIntervals.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "SensorPipeline.h" + +// A test scaffold for the producer loop that allows for bursts of measurements, +// the idea is to _force_, from time to time, the producer to block on emplacing +// a measurement to a full queue +static +SensorPipelineIntervals& getBurstyIntervals() +{ + static constexpr long burstInterval = 1000; + static constexpr long burstOf = 300; + static long count {0}; + + struct Bursty : public SensorPipelineIntervals + { + std::chrono::nanoseconds pollingInterval() const + { + return std::chrono::microseconds(count++ % burstInterval <= burstOf); + } + + std::chrono::nanoseconds blockedWaitingInterval() const + { + return std::chrono::microseconds(50); + } + }; + + static Bursty bursty {}; + return bursty; +} diff --git a/Chapter7-SensorPipelines/include/DefaultSensorPipelineIntervals.h b/Chapter7-SensorPipelines/include/DefaultSensorPipelineIntervals.h new file mode 100644 index 0000000..c3c196e --- /dev/null +++ b/Chapter7-SensorPipelines/include/DefaultSensorPipelineIntervals.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include "SensorPipeline.h" + + +static +SensorPipelineIntervals& getDefaultIntervals() +{ + struct Defaults : public SensorPipelineIntervals + { + std::chrono::nanoseconds pollingInterval() const { return std::chrono::microseconds(1000); } + std::chrono::nanoseconds blockedWaitingInterval() const { return std::chrono::microseconds(50); } + }; + + static Defaults defaults {}; + return defaults; +} diff --git a/Chapter7-SensorPipelines/include/GpsMeasurement.h b/Chapter7-SensorPipelines/include/GpsMeasurement.h deleted file mode 100644 index e69de29..0000000 diff --git a/Chapter7-SensorPipelines/include/SensorPipeline.h b/Chapter7-SensorPipelines/include/SensorPipeline.h index 5fd18b1..fbf7c91 100644 --- a/Chapter7-SensorPipelines/include/SensorPipeline.h +++ b/Chapter7-SensorPipelines/include/SensorPipeline.h @@ -1,13 +1,17 @@ #pragma once #include +#include #include #include #include "readerwriterqueue.h" +#include "Counters.h" #include "AnyMeasurement.h" +class SensorPipelineIntervals; + // // A simple, fixed-topology pipeline: // Producer thread -> SPSC queue -> Consumer thread @@ -15,15 +19,21 @@ class SensorPipeline { public: - explicit SensorPipeline(std::size_t capacity); + explicit SensorPipeline(std::size_t capacity); // creates this "holder" but doesn't start work + explicit SensorPipeline(std::size_t capacity, + const SensorPipelineIntervals& producerIntervals, + const SensorPipelineIntervals& consumerIntervals); ~SensorPipeline(); SensorPipeline(const SensorPipeline&) = delete; SensorPipeline& operator=(const SensorPipeline&) = delete; - void start(); - void stop(); - void join(); + void start(); // create pipeline (including its threads) and start flow + void stop(); // _request_ work to end (does not block) + void join(); // wait for completion of pipeline (blocks) and end threads (and resources) + // ...now SensorPipeline is safe to destruct + + void status(std::ostream& os); private: void producerLoop(); @@ -34,4 +44,20 @@ class SensorPipeline std::thread m_producerThread; std::thread m_consumerThread; std::atomic m_running { false }; + + const SensorPipelineIntervals& m_producerIntervals; + const SensorPipelineIntervals& m_consumerIntervals; + + Counter producer_enqueued; + Counter producer_blocked; + Counter consumer_blocked; + Counter consumer_dequeued; +}; + +struct SensorPipelineIntervals +{ + virtual std::chrono::nanoseconds pollingInterval() const = 0; + virtual std::chrono::nanoseconds blockedWaitingInterval() const = 0; + + virtual ~SensorPipelineIntervals() = default; }; diff --git a/Chapter7-SensorPipelines/src/SensorPipeline.cpp b/Chapter7-SensorPipelines/src/SensorPipeline.cpp index eb1f467..fca49c4 100644 --- a/Chapter7-SensorPipelines/src/SensorPipeline.cpp +++ b/Chapter7-SensorPipelines/src/SensorPipeline.cpp @@ -12,6 +12,7 @@ #include "MeasurementTypes.h" // weather::Position, MeasurementHeaderV1, SourceId #include "PositionProducer.h" #include "PositionConsumer.h" +#include "DefaultSensorPipelineIntervals.h" namespace { @@ -23,13 +24,24 @@ std::uint64_t nowNs() noexcept std::chrono::duration_cast( Clock::now().time_since_epoch()).count()); } + } // namespace SensorPipeline::SensorPipeline(std::size_t capacity) - : m_queue(capacity) + : SensorPipeline(capacity, getDefaultIntervals(), getDefaultIntervals()) +{ +} + +SensorPipeline::SensorPipeline(std::size_t capacity, + const SensorPipelineIntervals& producerIntervals, + const SensorPipelineIntervals& consumerIntervals) + : m_queue(capacity) + , m_producerIntervals(producerIntervals) + , m_consumerIntervals(consumerIntervals) { } + SensorPipeline::~SensorPipeline() { stop(); @@ -64,10 +76,19 @@ void SensorPipeline::join() } } +void SensorPipeline::status(std::ostream& os) +{ + os << "Producer enqueued: " << producer_enqueued << "\n" + << " blocked: " << producer_blocked << "\n" + << "Consumer blocked: " << consumer_blocked << "\n" + << " dequeued: " << consumer_dequeued << "\n"; +} + void SensorPipeline::producerLoop() { PositionProducer producer; + // Loop getting measurements until done (stopped) while (m_running.load()) { const weather::Position pos = producer.nextPosition(); @@ -81,12 +102,20 @@ void SensorPipeline::producerLoop() weather::AnyMeasurement msg(header, pos); - while (m_running.load() && !m_queue.try_enqueue(msg)) + // busy loop until measurement successfully enqueued + while (m_running.load()) { - std::this_thread::sleep_for(std::chrono::microseconds(50)); + if (m_queue.try_enqueue(msg)) + { + producer_enqueued++; + break; + } else { + producer_blocked++; + std::this_thread::sleep_for(m_producerIntervals.blockedWaitingInterval()); + } } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::this_thread::sleep_for(m_producerIntervals.pollingInterval()); } } @@ -107,9 +136,11 @@ void SensorPipeline::consumerLoop() { if (!m_queue.try_dequeue(msg)) { - std::this_thread::sleep_for(std::chrono::microseconds(50)); + consumer_blocked++; + std::this_thread::sleep_for(m_consumerIntervals.blockedWaitingInterval()); continue; } + consumer_dequeued++; const weather::Position* pos = msg.try_get(); if (pos != nullptr) @@ -122,13 +153,20 @@ void SensorPipeline::consumerLoop() } } - // Optional drain after stop (keeps output tidy) - while (m_queue.try_dequeue(msg)) + // Drain after stop to ensure all measurements are used and + // pipeline is emptied) + while (true) { - const weather::Position* pos = msg.try_get(); - if (pos != nullptr) + if (m_queue.try_dequeue(msg)) { - consumer.consume(*pos); + consumer_dequeued++; + const weather::Position* pos = msg.try_get(); + if (pos != nullptr) + { + consumer.consume(*pos); + } + } else { + break; } } } diff --git a/Chapter7-SensorPipelines/src/main.cpp b/Chapter7-SensorPipelines/src/main.cpp index 6f8efb8..cfd48cc 100644 --- a/Chapter7-SensorPipelines/src/main.cpp +++ b/Chapter7-SensorPipelines/src/main.cpp @@ -1,13 +1,19 @@ +#include +#include #include #include "SensorPipeline.h" +#include "BurstySensorPipelineIntervals.h" +#include "DefaultSensorPipelineIntervals.h" using namespace weather; int main() { // "Capacity" is an initial sizing parameter for the SPSC queue. - SensorPipeline pipeline(/*capacity*/ 128); + SensorPipeline pipeline(128 /* capacity */, + getBurstyIntervals() /* producer intervals */, + getDefaultIntervals() /* consumer intervals */); pipeline.start(); @@ -17,6 +23,7 @@ int main() pipeline.stop(); pipeline.join(); + pipeline.status(std::cerr); + return 0; } - diff --git a/Chapter8-SytemCompositionAndControl/CMakeLists.txt b/Chapter8-SystemCompositionAndControl/CMakeLists.txt similarity index 58% rename from Chapter8-SytemCompositionAndControl/CMakeLists.txt rename to Chapter8-SystemCompositionAndControl/CMakeLists.txt index c258c3f..4881203 100644 --- a/Chapter8-SytemCompositionAndControl/CMakeLists.txt +++ b/Chapter8-SystemCompositionAndControl/CMakeLists.txt @@ -1,21 +1,20 @@ cmake_minimum_required(VERSION 3.16) -project(chapter9_system_demo LANGUAGES CXX) +project(chapter8_system_demo LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -add_executable(chapter9_demo +add_executable(chapter8_demo src/main.cpp src/WeatherSystem.cpp src/WeatherSystemBuilder.cpp - src/Chapter9RunLoops.cpp + src/Chapter8RunLoops.cpp include/WeatherSystem.h include/WeatherSystemBuilder.h - include/Chapter9MeasurementFactory.h - include/Chapter9RunLoops.h + include/Chapter8RunLoops.h ) -target_include_directories(chapter9_demo PRIVATE +target_include_directories(chapter8_demo PRIVATE include ../Common/include ../ThirdParty/readerwriterqueue diff --git a/Chapter8-SytemCompositionAndControl/README.md b/Chapter8-SystemCompositionAndControl/README.md similarity index 73% rename from Chapter8-SytemCompositionAndControl/README.md rename to Chapter8-SystemCompositionAndControl/README.md index d574a23..3abbbd2 100644 --- a/Chapter8-SytemCompositionAndControl/README.md +++ b/Chapter8-SystemCompositionAndControl/README.md @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2025 Autumnal Software -# Chapter 9 - System Controller (Snapshot) +# Chapter 8 - System Composition And Control (Snapshot) -This repository accompanies Chapter 9 of _Designing Deterministic Systems with Modern C++_. +This repository accompanies Chapter 8 of _Designing Deterministic Systems with Modern C++_. -This Chapter 9 demo is about system composition and control. +This Chapter 8 demo is about system composition and control. - Uses `std::thread` - No Boost.Asio @@ -18,7 +18,7 @@ This Chapter 9 demo is about system composition and control. This snapshot assumes the repository already provides: - `ThirdParty/readerwriterqueue` -- `ThirdParty/boost_asio_1_36_0` (not used in Chapter 9, but present for later chapters) +- `ThirdParty/boost_asio_1_36_0` (not used in Chapter 8, but present for later chapters) - `../Chapter7-ImprovedAnyMeasurement/include` containing: - `MeasurementTypes.h` - `AnyMeasurement.h` diff --git a/Chapter8-SytemCompositionAndControl/include/BuildStatus.h b/Chapter8-SystemCompositionAndControl/include/BuildStatus.h similarity index 100% rename from Chapter8-SytemCompositionAndControl/include/BuildStatus.h rename to Chapter8-SystemCompositionAndControl/include/BuildStatus.h diff --git a/Chapter8-SytemCompositionAndControl/include/Chapter9RunLoops.h b/Chapter8-SystemCompositionAndControl/include/Chapter8RunLoops.h similarity index 87% rename from Chapter8-SytemCompositionAndControl/include/Chapter9RunLoops.h rename to Chapter8-SystemCompositionAndControl/include/Chapter8RunLoops.h index 9904efe..31e8ec1 100644 --- a/Chapter8-SytemCompositionAndControl/include/Chapter9RunLoops.h +++ b/Chapter8-SystemCompositionAndControl/include/Chapter8RunLoops.h @@ -9,7 +9,7 @@ #include "LogEvent.h" #include "readerwriterqueue.h" -struct Chapter9Stats +struct Chapter8Stats { std::atomic enqTempOk{0}; std::atomic enqPosOk{0}; @@ -27,10 +27,10 @@ struct Chapter9Stats -class Chapter9RunLoops +class Chapter8RunLoops { public: - Chapter9RunLoops(moodycamel::ReaderWriterQueue& inQ, + Chapter8RunLoops(moodycamel::ReaderWriterQueue& inQ, moodycamel::ReaderWriterQueue& logQ); void producer(const std::atomic& stop); @@ -41,5 +41,5 @@ class Chapter9RunLoops moodycamel::ReaderWriterQueue& m_inQ; moodycamel::ReaderWriterQueue& m_logQ; - Chapter9Stats m_stats; + Chapter8Stats m_stats; }; diff --git a/Chapter8-SytemCompositionAndControl/include/LogEvent.h b/Chapter8-SystemCompositionAndControl/include/LogEvent.h similarity index 100% rename from Chapter8-SytemCompositionAndControl/include/LogEvent.h rename to Chapter8-SystemCompositionAndControl/include/LogEvent.h diff --git a/Chapter8-SytemCompositionAndControl/include/WeatherSystem.h b/Chapter8-SystemCompositionAndControl/include/WeatherSystem.h similarity index 91% rename from Chapter8-SytemCompositionAndControl/include/WeatherSystem.h rename to Chapter8-SystemCompositionAndControl/include/WeatherSystem.h index ddd12fe..8850f0a 100644 --- a/Chapter8-SytemCompositionAndControl/include/WeatherSystem.h +++ b/Chapter8-SystemCompositionAndControl/include/WeatherSystem.h @@ -12,7 +12,7 @@ #include "LogEvent.h" #include "readerwriterqueue.h" -class Chapter9RunLoops; +class Chapter8RunLoops; class WeatherSystem { @@ -35,13 +35,13 @@ class WeatherSystem moodycamel::ReaderWriterQueue& inQueue() noexcept; moodycamel::ReaderWriterQueue& logQueue() noexcept; - Chapter9RunLoops& runLoops() noexcept; + Chapter8RunLoops& runLoops() noexcept; private: moodycamel::ReaderWriterQueue m_inQ; moodycamel::ReaderWriterQueue m_logQ; - Chapter9RunLoops* m_runLoops; + Chapter8RunLoops* m_runLoops; std::array&)>, ThreadCount> m_entries {}; std::array m_threads {}; diff --git a/Chapter8-SytemCompositionAndControl/include/WeatherSystemBuilder.h b/Chapter8-SystemCompositionAndControl/include/WeatherSystemBuilder.h similarity index 100% rename from Chapter8-SytemCompositionAndControl/include/WeatherSystemBuilder.h rename to Chapter8-SystemCompositionAndControl/include/WeatherSystemBuilder.h diff --git a/Chapter8-SytemCompositionAndControl/src/Chapter9RunLoops.cpp b/Chapter8-SystemCompositionAndControl/src/Chapter8RunLoops.cpp similarity index 95% rename from Chapter8-SytemCompositionAndControl/src/Chapter9RunLoops.cpp rename to Chapter8-SystemCompositionAndControl/src/Chapter8RunLoops.cpp index 7c5e745..8d3b18a 100644 --- a/Chapter8-SytemCompositionAndControl/src/Chapter9RunLoops.cpp +++ b/Chapter8-SystemCompositionAndControl/src/Chapter8RunLoops.cpp @@ -7,7 +7,7 @@ #include #include -#include "Chapter9RunLoops.h" +#include "Chapter8RunLoops.h" #include "MeasurementTypes.h" // @@ -39,7 +39,7 @@ static void updateMax(std::atomic& maxVal, } } -Chapter9RunLoops::Chapter9RunLoops( +Chapter8RunLoops::Chapter8RunLoops( moodycamel::ReaderWriterQueue& inQ, moodycamel::ReaderWriterQueue& logQ) : m_inQ(inQ) @@ -47,7 +47,7 @@ Chapter9RunLoops::Chapter9RunLoops( { } -void Chapter9RunLoops::producer(const std::atomic& stop) +void Chapter8RunLoops::producer(const std::atomic& stop) { using namespace std::chrono; @@ -112,7 +112,7 @@ void Chapter9RunLoops::producer(const std::atomic& stop) } } -void Chapter9RunLoops::consumer(const std::atomic& stop) +void Chapter8RunLoops::consumer(const std::atomic& stop) { using namespace std::chrono; @@ -150,7 +150,7 @@ void Chapter9RunLoops::consumer(const std::atomic& stop) updateMax(m_stats.latencyMaxNs, nowNs - rxNs); } - // No per-message logging in Chapter 9. + // No per-message logging in Chapter 8. } else { @@ -160,7 +160,7 @@ void Chapter9RunLoops::consumer(const std::atomic& stop) } -void Chapter9RunLoops::logger(const std::atomic& stop) +void Chapter8RunLoops::logger(const std::atomic& stop) { using namespace std::chrono; diff --git a/Chapter8-SytemCompositionAndControl/src/WeatherSystem.cpp b/Chapter8-SystemCompositionAndControl/src/WeatherSystem.cpp similarity index 89% rename from Chapter8-SytemCompositionAndControl/src/WeatherSystem.cpp rename to Chapter8-SystemCompositionAndControl/src/WeatherSystem.cpp index 185e804..3730858 100644 --- a/Chapter8-SytemCompositionAndControl/src/WeatherSystem.cpp +++ b/Chapter8-SystemCompositionAndControl/src/WeatherSystem.cpp @@ -2,12 +2,12 @@ // Copyright (c) 2025 Autumnal Software #include "WeatherSystem.h" -#include "Chapter9RunLoops.h" +#include "Chapter8RunLoops.h" WeatherSystem::WeatherSystem() : m_inQ(256) , m_logQ(64) - , m_runLoops(new Chapter9RunLoops(m_inQ, m_logQ)) + , m_runLoops(new Chapter8RunLoops(m_inQ, m_logQ)) { } @@ -59,7 +59,7 @@ moodycamel::ReaderWriterQueue& WeatherSystem::logQueue() noexcept return m_logQ; } -Chapter9RunLoops& WeatherSystem::runLoops() noexcept +Chapter8RunLoops& WeatherSystem::runLoops() noexcept { return *m_runLoops; } diff --git a/Chapter8-SytemCompositionAndControl/src/WeatherSystemBuilder.cpp b/Chapter8-SystemCompositionAndControl/src/WeatherSystemBuilder.cpp similarity index 95% rename from Chapter8-SytemCompositionAndControl/src/WeatherSystemBuilder.cpp rename to Chapter8-SystemCompositionAndControl/src/WeatherSystemBuilder.cpp index 56424f3..0463f28 100644 --- a/Chapter8-SytemCompositionAndControl/src/WeatherSystemBuilder.cpp +++ b/Chapter8-SystemCompositionAndControl/src/WeatherSystemBuilder.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2025 Autumnal Software #include "WeatherSystemBuilder.h" -#include "Chapter9RunLoops.h" +#include "Chapter8RunLoops.h" BuildStatus WeatherSystemBuilder::build(WeatherSystem& system) const { diff --git a/Chapter8-SytemCompositionAndControl/src/main.cpp b/Chapter8-SystemCompositionAndControl/src/main.cpp similarity index 90% rename from Chapter8-SytemCompositionAndControl/src/main.cpp rename to Chapter8-SystemCompositionAndControl/src/main.cpp index 7b2c738..840369d 100644 --- a/Chapter8-SytemCompositionAndControl/src/main.cpp +++ b/Chapter8-SystemCompositionAndControl/src/main.cpp @@ -46,8 +46,12 @@ int main(int argc, char** argv) lock_memory(); // After construction, before execution } + + const auto deviceRunLength{std::chrono::seconds(15)}; + + system.start(); - std::this_thread::sleep_for(std::chrono::seconds(5)); + std::this_thread::sleep_for(deviceRunLength); system.stop(); system.join(); diff --git a/Chapter8-SytemCompositionAndControl/include/Chapter9MeasurementFactory.h b/Chapter8-SytemCompositionAndControl/include/Chapter9MeasurementFactory.h deleted file mode 100644 index 3fb4c8b..0000000 --- a/Chapter8-SytemCompositionAndControl/include/Chapter9MeasurementFactory.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2025 Autumnal Software - -#pragma once - -#include -#include - -#include "AnyMeasurement.h" -#include "MeasurementTypes.h" - -namespace chapter9 -{ - using Clock = std::chrono::steady_clock; - - // Convert steady_clock time_point to a monotonic nanosecond tick count. - std::uint64_t to_ns(Clock::time_point t) noexcept; - - weather::AnyMeasurement make_temperature(double seed, Clock::time_point eventTime, Clock::time_point rxTime); - weather::AnyMeasurement make_position(double seed, Clock::time_point eventTime, Clock::time_point rxTime); -} diff --git a/Chapter8-SytemCompositionAndControl/src/Chapter9MeasurementFactory.cpp b/Chapter8-SytemCompositionAndControl/src/Chapter9MeasurementFactory.cpp deleted file mode 100644 index fb072be..0000000 --- a/Chapter8-SytemCompositionAndControl/src/Chapter9MeasurementFactory.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2025 Autumnal Software - -#include "Chapter9MeasurementFactory.h" - -namespace chapter9 -{ - std::uint64_t to_ns(Clock::time_point t) noexcept - { - return static_cast( - std::chrono::duration_cast(t.time_since_epoch()).count()); - } - - weather::AnyMeasurement make_temperature(double seed, Clock::time_point eventTime, Clock::time_point rxTime) - { - weather::MeasurementHeaderV1 h; - h.eventTime = to_ns(eventTime); - h.rxTime = to_ns(rxTime); - h.source = weather::SourceId::Unknown; - h.flags = 0; - // kind is set by AnyMeasurement via MeasurementKindOf - - weather::Temperature t; - t.value = 20.0 + seed; - - return weather::AnyMeasurement(h, t); - } - - weather::AnyMeasurement make_position(double seed, Clock::time_point eventTime, Clock::time_point rxTime) - { - weather::MeasurementHeaderV1 h; - h.eventTime = to_ns(eventTime); - h.rxTime = to_ns(rxTime); - h.source = weather::SourceId::Unknown; - h.flags = 0; - - weather::Position p; - p.lat = 43.0 + seed * 0.0001; - p.lon = -77.0 + seed * 0.0001; - p.alt = 150.0; - - return weather::AnyMeasurement(h, p); - } -} diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt index fae86d6..0c4022b 100644 --- a/Common/CMakeLists.txt +++ b/Common/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(Common STATIC # Public headers (explicit) include/ImmutableByteView.h include/MutableByteView.h + include/Counters.h include/Status.h diff --git a/Common/include/Counters.h b/Common/include/Counters.h new file mode 100644 index 0000000..bca08e5 --- /dev/null +++ b/Common/include/Counters.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + + +// A counter that is isolated on a cache line. Thread-safe plus isolated on a single cache line +// so incrementing it won't have interference penalties. +struct Counter +{ + void incr() { ++value; } + void operator++() { incr(); } + void operator++(int) { incr(); } + + operator unsigned long() const { return value.load(); } + +private: +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winterference-size" + static constexpr auto cache_line_size = std::hardware_destructive_interference_size; +#pragma GCC diagnostic pop + alignas(cache_line_size) std::atomic value {0}; + char _padding_[cache_line_size - sizeof(long)]; +}; diff --git a/SetupChapter/jitter_test.cpp b/SetupChapter/jitter_test.cpp index f8a3587..da980d2 100644 --- a/SetupChapter/jitter_test.cpp +++ b/SetupChapter/jitter_test.cpp @@ -80,9 +80,8 @@ int main(int argc, char* argv[]) const long double avg_abs = sum_abs / static_cast(abs_jitter.size()); std::sort(abs_jitter.begin(), abs_jitter.end()); - const std::size_t idx_99 = static_cast( - (abs_jitter.size() - 1) * 0.99 - ); + const std::size_t idx_99 = static_cast(static_cast(abs_jitter.size() - 1) * 0.99); + const long long p99_abs = abs_jitter[idx_99]; std::cout << "Jitter statistics (nanoseconds)\n"; @@ -94,4 +93,3 @@ int main(int argc, char* argv[]) return 0; } - diff --git a/global b/global deleted file mode 100644 index e69de29..0000000