diff --git a/score/ts_client/docs/detailed_design/_assets/ipc_channel.puml b/score/ts_client/docs/detailed_design/_assets/ipc_channel.puml new file mode 100644 index 00000000..ed7221aa --- /dev/null +++ b/score/ts_client/docs/detailed_design/_assets/ipc_channel.puml @@ -0,0 +1,84 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +@startuml ts_client_ipc_channel +!theme plain + +title libTSClient Shared Memory IPC + +legend top left + |= Color |= Description | + | <#LightPink> | IPC components | + | <#LightCyan> | Shared memory region | + | <#LightSalmon> | TimeDaemon adapter | +endlegend + +package "TimeSlave Process" { + class GptpIpcPublisher #LightPink { + - region_ : GptpIpcRegion* + - shm_resource_ : shared_ptr + - ipc_name_ : string + + Init(name) : bool + + Publish(data : GptpIpcData) : void + + Destroy() : void + } +} + +package "Shared Memory" { + class GptpIpcRegion <> #LightCyan { + + magic : atomic = 0x47505450 + + seq : atomic + + data : GptpIpcData + + seq_confirm : atomic + -- + 64-byte aligned for\ncache line efficiency + } +} + +package "TimeDaemon Process" { + class GptpIpcReceiver #LightPink { + - region_ : const GptpIpcRegion* + - shm_resource_ : shared_ptr + + Init(name) : bool + + Receive() : std::optional + + Close() : void + } + + class ShmPTPEngine #LightSalmon { + - receiver_ : GptpIpcReceiver + - ipc_name_ : string + + Initialize() : bool + + Deinitialize() : bool + + ReadPTPSnapshot(info : PtpTimeInfo&) : bool + } +} + +GptpIpcPublisher --> GptpIpcRegion : "shm_open(O_CREAT)\nmmap(PROT_WRITE)" +GptpIpcReceiver --> GptpIpcRegion : "shm_open(O_RDONLY)\nmmap(PROT_READ)" +ShmPTPEngine *-- GptpIpcReceiver +ShmPTPEngine ..> "PtpTimeInfo" : converts to + +note right of GptpIpcRegion + **Seqlock Protocol:** + Writer: seq++ (odd) → fence → memcpy → seq_confirm++, seq++ (even) + Reader: read seq1 (even) → memcpy → fence → read seq2, seq3 + retry if seq1 != seq2 or seq1 != seq3 + Retry up to 20 times on torn read +end note + +note bottom of ShmPTPEngine + Maps GptpIpcData fields to PtpTimeInfo. + Instantiated as GPTPShmMachine via CreateGPTPShmMachine(). +end note + +@enduml diff --git a/score/ts_client/docs/detailed_design/_assets/ipc_sequence.puml b/score/ts_client/docs/detailed_design/_assets/ipc_sequence.puml new file mode 100644 index 00000000..6e11ac0c --- /dev/null +++ b/score/ts_client/docs/detailed_design/_assets/ipc_sequence.puml @@ -0,0 +1,65 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +@startuml ts_client_ipc_sequence +!theme plain + +title libTSClient Seqlock IPC Protocol + +participant "TimeSlave\n(GptpIpcPublisher)" as PUB #LightPink +participant "SharedMemory\n(GptpIpcRegion)" as SHM #LightCyan +participant "TimeDaemon\n(GptpIpcReceiver)" as RCV #LightPink + +== Initialization == + +PUB -> SHM : shm_open("/gptp_ptp_info", O_CREAT | O_RDWR) +PUB -> SHM : ftruncate(sizeof(GptpIpcRegion)) +PUB -> SHM : mmap(PROT_READ | PROT_WRITE) +PUB -> SHM : write magic = 0x47505450 ('GPTP') + +... + +RCV -> SHM : shm_open("/gptp_ptp_info", O_RDONLY) +RCV -> SHM : mmap(PROT_READ) +RCV -> SHM : verify magic == 0x47505450 + +== Publish (Writer Side) == + +PUB -> SHM : seq.fetch_add(1, relaxed) // seq becomes odd (write in progress) +PUB -> SHM : atomic_thread_fence(release) +PUB -> SHM : memcpy(&data, &src, sizeof(GptpIpcData)) +PUB -> SHM : seq_confirm.store(seq+1, release) // seq_confirm becomes even +PUB -> SHM : seq.store(seq+1, release) // seq becomes even (write done) + +== Receive (Reader Side) == + +loop up to 20 retries + RCV -> SHM : seq1 = seq.load(acquire) + alt seq1 is odd (write in progress) + RCV -> RCV : retry + else seq1 is even + RCV -> SHM : memcpy(&local, &data, sizeof(GptpIpcData)) + RCV -> SHM : atomic_thread_fence(acq_rel) + RCV -> SHM : seq2 = seq_confirm.load(acquire) + RCV -> SHM : seq3 = seq.load(acquire) + alt seq1 == seq2 && seq1 == seq3 + RCV --> RCV : return GptpIpcData (consistent) + else torn read (new write started) + RCV -> RCV : retry + end + end +end + +RCV --> RCV : return std::nullopt (exhausted retries) + +@enduml diff --git a/score/ts_client/docs/detailed_design/detailed_design.rst b/score/ts_client/docs/detailed_design/detailed_design.rst deleted file mode 100644 index 1d7b6b30..00000000 --- a/score/ts_client/docs/detailed_design/detailed_design.rst +++ /dev/null @@ -1,96 +0,0 @@ -.. - # ******************************************************************************* - # Copyright (c) 2026 Contributors to the Eclipse Foundation - # - # See the NOTICE file(s) distributed with this work for additional - # information regarding copyright ownership. - # - # This program and the accompanying materials are made available under the - # terms of the Apache License Version 2.0 which is available at - # https://www.apache.org/licenses/LICENSE-2.0 - # - # SPDX-License-Identifier: Apache-2.0 - # ******************************************************************************* - -.. _ts_client_detailed_design: - -Time Sync Client Detailed Design -================================= - -.. document:: Time Sync Client Detailed Design - :id: doc__ts_client_detailed_design - :status: draft - :version: 1 - :safety: ASIL_B - :security: NO - :realizes: wp__sw_implementation - :tags: ts_client - -.. note:: - Work in progress: structure, titles, and needs IDs only. Content and req/comp/feat traceability links to follow in later PRs. - -.. attention:: - The above directive must be updated according to your Component. - - - Adjust ``status`` to be ``valid`` - - Adjust ``safety`` and ``tags`` according to your needs - -Detailed Design for Time Sync Client -===================================== - -Description ------------ - -| Design Decisions - For the documentation of the decision the :need:`gd_temp__change_decision_record` can be used. -| Design Constraints - -Example: - - - component is split into two units unit1 and unit2 based on single responsibility principle. - - unit2 is injected to unit1 one via dependency injection for testability. - -Rationale Behind Decomposition into Units -****************************************** -| mandatory: a motivation for the decomposition into one or more units. - -.. note:: Reason for split into multiple units could be- - - Based on design principles like SOLID,DRY etc - - Based on design pattern's etc. - -Static Diagrams for Unit Interactions -------------------------------------- - -A static view provides an overview of the units and their relationships using -UML 2.0 notations (e.g. class diagrams, component diagrams). Use ``.. uml::`` -or ``.. image::`` directives to include the diagram. - -.. .. uml:: dd_example_ex_sta.puml - -Dynamic Diagrams for Unit Interactions (optional) --------------------------------------------------- - -A dynamic view illustrates how the units within a component interact over their -interfaces to fulfill a specific use case or functionality. It is optional when the -component's behaviour is straightforward and can be understood from the static view -and interface documentation alone. - -Use standard UML behavioural diagrams (sequence diagrams, state machine diagrams) -with ``.. uml::`` or ``.. image::`` directives. - -.. .. uml:: dd_example_ex_dyn.puml - -Units within the Component --------------------------- - -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. A separate static diagram per unit is not required. - -Interface documentation of a software unit is part of the source code (e.g. public -API headers, trait definitions, or documented function signatures). - -Example: - -- unit1: implements the main logic (see source code for details) -- unit2: injected into unit1 via dependency injection for testability diff --git a/score/ts_client/docs/detailed_design/index.rst b/score/ts_client/docs/detailed_design/index.rst index a1cbdb6c..67a35cc5 100644 --- a/score/ts_client/docs/detailed_design/index.rst +++ b/score/ts_client/docs/detailed_design/index.rst @@ -12,28 +12,152 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -.. _ts_client_detailed_design_index: +.. _ts_client_detailed_design: -Detailed Design -############### +Time Sync Client Detailed Design +================================= -.. note:: - Work in progress: page structure only, content to follow in later PRs. +.. document:: Time Sync Client Detailed Design + :id: doc__ts_client_detailed_design + :status: draft + :version: 1 + :safety: ASIL_B + :security: NO + :realizes: wp__sw_implementation + :tags: ts_client -.. attention:: +Description +----------- - The detailed design document is optional and should be created if the design of the component is complex and cannot be easily understood from the architecture documentation and interface documentation alone. - But the inspection checklist for the implementation is mandatory. +The ``ts_client`` component provides shared memory-based IPC for gPTP time synchronization data exchange between TimeSlave and TimeDaemon processes. It implements a lock-free, single-writer/multi-reader communication channel using the seqlock protocol over POSIX shared memory. +Use Cases +~~~~~~~~~ -Detail design example ---------------------- +1. Publishing time synchronization snapshots from TimeSlave to shared memory +2. Reading time synchronization snapshots from TimeDaemon +3. Lock-free concurrent access with bounded retry on torn reads -An example of documenting detailed design can be found in: +Rationale Behind Decomposition into Units +------------------------------------------ - .. toctree:: +The ``ts_client`` component is decomposed into two implementation units: - detailed_design +1. **GptpIpcPublisher** — Creates and writes to the shared memory segment (TimeSlave side) +2. **GptpIpcReceiver** — Opens and reads from the shared memory segment (TimeDaemon side) + +This separation enables independent deployment in different processes while maintaining a consistent IPC protocol. + +Static Diagrams for Unit Interactions +-------------------------------------- + +Class View +~~~~~~~~~~ + +Main classes and their relationships: + +.. raw:: html + +
+ +.. uml:: _assets/ipc_channel.puml + :alt: Class View + +.. raw:: html + +
+ +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: + + 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 ``GptpIpcReceiver`` shall return ``std::optional`` (empty if all retries exhausted) +- The ``GptpIpcReceiver`` shall support ``Close()`` to unmap the shared memory region + +Seqlock Protocol Workflow +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The seqlock protocol ensures lock-free communication between publisher and receiver: + +.. raw:: html + +
+ +.. uml:: _assets/ipc_sequence.puml + :alt: Seqlock Protocol + +.. raw:: html + +
+ +Shared Memory Layout +~~~~~~~~~~~~~~~~~~~~ + +The ``GptpIpcRegion`` structure defines the shared memory layout: + +.. code-block:: cpp + + struct alignas(64) GptpIpcRegion + { + std::atomic magic{0x47505450}; // 'GPTP' + std::atomic seq{0}; + score::ts::GptpIpcData data{}; + std::atomic seq_confirm{1}; + }; + +- Aligned to 64 bytes (cache line size) to prevent false sharing +- ``magic`` field validates the segment on reader initialization +- ``seq`` and ``seq_confirm`` implement the seqlock protocol +- ``data`` contains the ``GptpIpcData`` payload + +Using in Test Environment +-------------------------- + +The ``GptpIpcPublisher`` and ``GptpIpcReceiver`` rely on POSIX shared memory (``shm_open``), which works on any Linux host. Component tests can run end-to-end using real IPC without platform-specific mocks. Inspection Checklist --------------------