VLink  2.0.0
A high-performance communication middleware
calculate_sample.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file calculate_sample.h
26  * @brief Per-publisher gap detection used to expose sample loss statistics to subscribers.
27  *
28  * @details
29  * This is an internal implementation header used by the public subscriber / getter
30  * templates and their backend @c NodeImpl classes; it is not part of the user API.
31  * @c CalculateSample inspects the monotonic sequence numbers attached to incoming
32  * frames and accumulates how many samples have been expected and how many have
33  * been skipped, on a per-sender basis.
34  *
35  * @par Role
36  * | Caller | What it does with @c CalculateSample |
37  * | ---------------------------------------- | ---------------------------------------------- |
38  * | @c SubscriberImpl / @c GetterImpl | Owns one instance when latency tracking is on. |
39  * | Transport backend receive thread | Calls @c update(seq, guid) for every frame. |
40  * | @c get_lost() / proxy / discovery layer | Reads cumulative counters for reporting. |
41  *
42  * @par Sizing summary
43  * Counters are stored per GUID in a hash map, so memory grows linearly with the
44  * number of observed senders. Each entry contains three @c uint64_t values, and
45  * the table is guarded by a @c std::shared_mutex so reads from multiple reader
46  * threads do not contend with each other.
47  *
48  * @par Algorithm
49  * - The first call for a GUID seeds @c first with the observed sequence number
50  * and @c expected with @c seq + 1, and records zero loss.
51  * - Subsequent in-order frames increment @c expected by one.
52  * - When @c seq is ahead of @c expected by at most @c UINT32_MAX, the gap is
53  * added to @c lost and @c expected jumps to @c seq + 1.
54  * - A gap strictly greater than @c UINT32_MAX is treated as a sender restart
55  * and the state is re-seeded with no extra loss recorded.
56  *
57  * @note @c get_total() returns the sum of @c (expected - first) across all
58  * GUIDs; @c get_lost() returns the sum of @c lost counters.
59  */
60 
61 #pragma once
62 
63 #include <cstdint>
64 #include <shared_mutex>
65 #include <unordered_map>
66 
67 #include "../base/macros.h"
68 
69 namespace vlink {
70 
71 /**
72  * @class CalculateSample
73  * @brief Tracks expected and lost sample counts grouped by sender GUID.
74  *
75  * @details
76  * One instance is created per subscriber / getter when latency-and-loss tracking
77  * is enabled. The @c guid parameter of @c update() lets a single receiver
78  * account for multiple senders without their streams interfering.
79  */
81  public:
82  /**
83  * @brief Constructs an empty counter.
84  */
85  CalculateSample() noexcept;
86 
87  /**
88  * @brief Releases all per-sender state.
89  */
90  ~CalculateSample() noexcept;
91 
92  /**
93  * @brief Records a sequence number received from @p guid.
94  *
95  * @details
96  * Updates the per-sender counters according to the algorithm described at file
97  * scope. Acquires the exclusive side of the shared mutex.
98  *
99  * @param seq Sequence number reported by the transport.
100  * @param guid Sender identifier; pass @c 0 when there is exactly one source.
101  */
102  void update(uint64_t seq, uint64_t guid = 0) noexcept;
103 
104  /**
105  * @brief Returns the total number of expected samples across all senders.
106  *
107  * @details
108  * Computed as the sum of @c (expected - first) over every tracked GUID. The
109  * value includes both received and lost samples.
110  *
111  * @return Number of expected samples; @c 0 when no data has been observed yet.
112  */
113  [[nodiscard]] uint64_t get_total() const noexcept;
114 
115  /**
116  * @brief Returns the cumulative number of lost samples across all senders.
117  *
118  * @return Aggregate loss count since construction.
119  */
120  [[nodiscard]] uint64_t get_lost() const noexcept;
121 
122  private:
123  struct Number final {
124  uint64_t first{0};
125  uint64_t expected{0};
126  uint64_t lost{0};
127  };
128 
129  std::unordered_map<uint64_t, Number> number_map_;
130  mutable std::shared_mutex mtx_;
131 
133 };
134 
135 } // namespace vlink
#define VLINK_EXPORT
Definition: macros.h:81
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition: macros.h:174