From 315a28b39f697bdeb95727c12b1e751fe0330dd8 Mon Sep 17 00:00:00 2001 From: Ryan Steel Date: Mon, 31 Aug 2026 09:36:57 +0100 Subject: [PATCH] update ts_client doxygen and dd --- .../ts_client/docs/detailed_design/index.rst | 56 +++---------------- score/ts_client/src/gptp_ipc.h | 3 + score/ts_client/src/gptp_ipc_data.h | 16 +++--- score/ts_client/src/gptp_ipc_publisher.h | 39 +++++++++++-- score/ts_client/src/gptp_ipc_receiver.h | 37 ++++++++++-- score/ts_client/src/gptp_ipc_test_utils.h | 17 +++++- 6 files changed, 96 insertions(+), 72 deletions(-) diff --git a/score/ts_client/docs/detailed_design/index.rst b/score/ts_client/docs/detailed_design/index.rst index 67a35cc5..9889e429 100644 --- a/score/ts_client/docs/detailed_design/index.rst +++ b/score/ts_client/docs/detailed_design/index.rst @@ -67,56 +67,16 @@ Main classes and their relationships: -Units within Time Sync Client ------------------------------- - -GptpIpcPublisher Unit -~~~~~~~~~~~~~~~~~~~~~ - -The ``GptpIpcPublisher`` component creates and manages the POSIX shared memory segment and writes ``GptpIpcData`` using the seqlock protocol. - -Implementation Requirements -''''''''''''''''''''''''''' - -The ``GptpIpcPublisher`` has the following requirements: - -- The ``GptpIpcPublisher`` shall create a POSIX shared memory segment via ``shm_open()`` with ``O_CREAT`` flag -- The ``GptpIpcPublisher`` shall map the shared memory region as ``GptpIpcRegion`` aligned to 64 bytes -- The ``GptpIpcPublisher`` shall initialize the magic number field to ``0x47505450`` ('GPTP') -- The ``GptpIpcPublisher`` shall write ``GptpIpcData`` using the seqlock protocol: - - 1. Increment ``seq`` (becomes odd — signals write in progress) - 2. Apply a release memory fence - 3. ``memcpy`` the ``GptpIpcData`` payload - 4. Store ``seq_confirm = seq + 1`` - 5. Increment ``seq`` (both ``seq`` and ``seq_confirm`` become even — signals write complete) - -- The ``GptpIpcPublisher`` shall use the default shared memory name ``/gptp_ptp_info`` unless overridden -- The ``GptpIpcPublisher`` shall support ``Destroy()`` to unmap and unlink the shared memory segment - -GptpIpcReceiver Unit -~~~~~~~~~~~~~~~~~~~~ - -The ``GptpIpcReceiver`` component opens the shared memory segment read-only and reads ``GptpIpcData`` with bounded retry on torn reads. - -Implementation Requirements -''''''''''''''''''''''''''' - -The ``GptpIpcReceiver`` has the following requirements: - -- The ``GptpIpcReceiver`` shall open the POSIX shared memory segment via ``shm_open()`` with ``O_RDONLY`` flag -- The ``GptpIpcReceiver`` shall map the shared memory region as read-only (``PROT_READ``) -- The ``GptpIpcReceiver`` shall validate the magic number (``0x47505450``) on ``Init()`` -- The ``GptpIpcReceiver`` shall read ``GptpIpcData`` using the seqlock protocol with up to 20 retries: +Units within the Component +-------------------------- - 1. Read ``seq1`` with acquire ordering (must be even, otherwise retry) - 2. ``memcpy`` the ``GptpIpcData`` payload - 3. Apply an acquire-release fence - 4. Read ``seq_confirm`` as ``seq2`` and re-read ``seq`` as ``seq3`` - 5. If ``seq1 == seq2 == seq3``, the read is consistent; otherwise retry +The relationship between a unit and its parent component is established implicitly +through the file path. Each component has its own directory, and units residing +within that directory belong to it. The unit's attributes and behaviour are documented +in the source code itself. -- The ``GptpIpcReceiver`` shall return ``std::optional`` (empty if all retries exhausted) -- The ``GptpIpcReceiver`` shall support ``Close()`` to unmap the shared memory region +- **GptpIpcPublisher**: Creates and writes to shared memory using seqlock protocol (see ``src/gptp_ipc_publisher.h``) +- **GptpIpcReceiver**: Reads from shared memory with bounded retry on torn reads (see ``src/gptp_ipc_receiver.h``) Seqlock Protocol Workflow ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/score/ts_client/src/gptp_ipc.h b/score/ts_client/src/gptp_ipc.h index 87fe0ab4..86407239 100644 --- a/score/ts_client/src/gptp_ipc.h +++ b/score/ts_client/src/gptp_ipc.h @@ -13,6 +13,9 @@ #ifndef SCORE_TS_CLIENT_SRC_GPTP_IPC_H #define SCORE_TS_CLIENT_SRC_GPTP_IPC_H +/// @file +/// @brief Umbrella include for gPTP IPC channel, publisher, and receiver types. + #include "score/ts_client/src/gptp_ipc_channel.h" #include "score/ts_client/src/gptp_ipc_publisher.h" #include "score/ts_client/src/gptp_ipc_receiver.h" diff --git a/score/ts_client/src/gptp_ipc_data.h b/score/ts_client/src/gptp_ipc_data.h index 3b2030f5..2c3c4067 100644 --- a/score/ts_client/src/gptp_ipc_data.h +++ b/score/ts_client/src/gptp_ipc_data.h @@ -21,9 +21,7 @@ namespace score namespace ts { -/** - * @brief IPC-layer status flags transmitted from TimeSlave to TimeDaemon. - */ +/// @brief IPC-layer status flags transmitted from TimeSlave to TimeDaemon. struct GptpIpcStatus { bool is_synchronized; @@ -33,6 +31,7 @@ struct GptpIpcStatus bool is_correct; }; +/// @brief Sync/FollowUp snapshot from latest received gPTP sync cycle. struct GptpIpcSyncFupData { std::uint64_t precise_origin_timestamp; @@ -46,6 +45,7 @@ struct GptpIpcSyncFupData std::uint64_t clock_identity; }; +/// @brief Peer-delay measurement snapshot from latest completed pDelay cycle. struct GptpIpcPDelayData { std::uint64_t request_origin_timestamp; @@ -62,12 +62,10 @@ struct GptpIpcPDelayData std::uint64_t resp_clock_identity; }; -/** - * @brief IPC data snapshot written by TimeSlave and read by TimeDaemon. - * - * This type is internal to ts_client and intentionally decoupled from - * score::td::PtpTimeInfo. Callers are responsible for mapping between the two. - */ +/// @brief IPC data snapshot written by TimeSlave and read by TimeDaemon. +/// +/// This type is internal to ts_client and intentionally decoupled from +/// score::td::PtpTimeInfo. Callers are responsible for mapping between the two. struct GptpIpcData { std::chrono::nanoseconds ptp_assumed_time; diff --git a/score/ts_client/src/gptp_ipc_publisher.h b/score/ts_client/src/gptp_ipc_publisher.h index f13a2a23..18e98522 100644 --- a/score/ts_client/src/gptp_ipc_publisher.h +++ b/score/ts_client/src/gptp_ipc_publisher.h @@ -26,23 +26,50 @@ namespace ts namespace details { -/** - * @brief Single-writer publisher for the gPTP IPC channel. - * - * Creates the POSIX shared memory segment and writes PtpTimeInfo using - * the seqlock protocol. Used by TimeSlave. - */ +/// @brief Single-writer publisher for the gPTP IPC shared memory channel. +/// +/// Creates the POSIX shared memory segment and writes @c GptpIpcData using +/// the seqlock protocol. Used by TimeSlave to publish time synchronisation +/// snapshots to applications (consumed by @c ShmPTPEngine / @c GptpIpcReceiver +/// in TimeDaemon). +/// +/// @see @c GptpIpcReceiver Multi-reader counterpart. class GptpIpcPublisher final { public: + /// @brief Constructs unopened publisher instance. GptpIpcPublisher() = default; + + /// @brief Ensures shared memory cleanup via @c Close(). ~GptpIpcPublisher(); GptpIpcPublisher(const GptpIpcPublisher&) = delete; GptpIpcPublisher& operator=(const GptpIpcPublisher&) = delete; + /// @brief Creates and maps the POSIX shared memory segment. + /// + /// Opens the segment via @c shm_open() with @c O_CREAT, maps it as a + /// @c GptpIpcRegion, and initialises the magic number to @c 0x47505450. + /// + /// @param ipc_name Shared memory name. Default: @c kGptpIpcName (@c /gptp_ptp_info). + /// @return true if successful. bool Open(const std::string& ipc_name = kGptpIpcName); + + /// @brief Writes @c GptpIpcData using the 5-step seqlock write protocol. + /// + /// 1. @c seq++ (becomes odd — signals write in progress to readers). + /// 2. Release memory fence. + /// 3. @c memcpy of payload. + /// 4. @c seq_confirm = seq + 1. + /// 5. @c seq++ (both counters even — write complete). + /// + /// Dual seq counters detect torn reads: reader checks seq1 == seq2 == seq3 + /// after memcpy proves no concurrent write. + /// + /// @param data The @c GptpIpcData snapshot to publish. void Publish(const score::ts::GptpIpcData& data); + + /// @brief Unmaps and unlinks the shared memory segment. void Close(); private: diff --git a/score/ts_client/src/gptp_ipc_receiver.h b/score/ts_client/src/gptp_ipc_receiver.h index 5e4da42a..39e588f7 100644 --- a/score/ts_client/src/gptp_ipc_receiver.h +++ b/score/ts_client/src/gptp_ipc_receiver.h @@ -27,23 +27,48 @@ namespace ts namespace details { -/** - * @brief Multi-reader receiver for the gPTP IPC channel. - * - * Opens an existing POSIX shared memory segment (read-only) and reads - * PtpTimeInfo using the seqlock protocol. Used by ShmPTPEngine. - */ +/// @brief Multi-reader receiver for the gPTP IPC shared memory channel. +/// +/// Opens an existing POSIX shared memory segment read-only and reads +/// @c GptpIpcData using the seqlock protocol with bounded retry on torn reads. +/// Used by @c ShmPTPEngine in TimeDaemon. +/// +/// @see GptpIpcPublisher Single-writer counterpart. class GptpIpcReceiver final { public: + /// @brief Constructs unopened receiver instance. GptpIpcReceiver() = default; + + /// @brief Ensures mapping teardown via @c Close(). ~GptpIpcReceiver(); GptpIpcReceiver(const GptpIpcReceiver&) = delete; GptpIpcReceiver& operator=(const GptpIpcReceiver&) = delete; + /// @brief Opens the existing POSIX shared memory segment read-only. + /// + /// Opens via @c shm_open() with @c O_RDONLY, maps as read-only, and + /// validates the magic number (@c 0x47505450). + /// + /// @param ipc_name Shared memory name. Default: @c kGptpIpcName (@c /gptp_ptp_info). + /// @return true if successful. bool Open(const std::string& ipc_name = kGptpIpcName); + + /// @brief Reads @c GptpIpcData using the seqlock read protocol (up to 20 retries). + /// + /// Read sequence per attempt: + /// 1. Read @c seq1 (acquire — must be even; odd means write in progress, skip). + /// 2. @c memcpy of payload. + /// 3. Acquire-release fence. + /// 4. Read @c seq_confirm as @c seq2, re-read @c seq as @c seq3. + /// 5. Accept only if @c seq1 == @c seq2 == @c seq3; otherwise retry. + /// + /// @return @c GptpIpcData if a consistent read was achieved within the retry + /// budget, @c std::nullopt if all retries detected a torn read. std::optional Receive(); + + /// @brief Unmaps the shared memory region. void Close(); private: diff --git a/score/ts_client/src/gptp_ipc_test_utils.h b/score/ts_client/src/gptp_ipc_test_utils.h index 19a97ff1..e138ce43 100644 --- a/score/ts_client/src/gptp_ipc_test_utils.h +++ b/score/ts_client/src/gptp_ipc_test_utils.h @@ -28,6 +28,9 @@ namespace ts namespace details { +/// @brief Creates process-local unique shared-memory name for tests. +/// +/// Name format: @c /gptp_ipc_ut__. inline std::string UniqueShmName() { static std::atomic counter{0}; @@ -35,15 +38,19 @@ inline std::string UniqueShmName() std::to_string(counter.fetch_add(1, std::memory_order_relaxed)); } -/// RAII helper: creates SHM via SharedMemoryFactory (same layout as GptpIpcPublisher) -/// so that GptpIpcReceiver can open it. Gives direct access to the region for -/// edge-case tests that need to corrupt seq/magic. +/// @brief RAII helper for manual shared-memory setup in tests. +/// +/// Creates SHM via @c SharedMemoryFactory with @c GptpIpcRegion layout so +/// @c GptpIpcReceiver can open same region. Exposes raw region pointer for +/// edge-case tests that intentionally corrupt seqlock state or magic. struct ManualShm { std::shared_ptr resource_; GptpIpcRegion* region_{nullptr}; std::string name_; + /// @brief Creates shared memory region with provided name. + /// @param n POSIX shared-memory object name. explicit ManualShm(const std::string& n) : name_{n} { score::memory::shared::SharedMemoryFactory::Remove(n); @@ -57,16 +64,20 @@ struct ManualShm score::memory::shared::permission::WorldWritable{}); } + /// @brief Releases shared memory resource and removes object. ~ManualShm() { resource_.reset(); score::memory::shared::SharedMemoryFactory::Remove(name_); } + /// @brief Returns true when region and resource are initialized. bool Valid() const { return resource_ != nullptr && region_ != nullptr; } + + /// @brief Returns mutable pointer to mapped IPC region. GptpIpcRegion* Region() { return region_;