Flow-IPC 3.0.0
Flow-IPC project: Full implementation reference.
shm_stats.hpp
Go to the documentation of this file.
1/* Flow-IPC: Shared Memory
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
21#include <flow/util/stat/stat_set.hpp>
22#include <cstdint>
23#include <string>
24#include <atomic>
25
26namespace ipc::shm::stat
27{
28
29// Types.
30
31/**
32 * Flow-IPC SHM-module: SHM-provider-agnostic stats about an *arena object* if viewed as solely a memory-manager or
33 * heap, at the `construct()`ed a/k/a *first-class* object level. Assume `arena` is the actual C++ object
34 * (e.g., SHM-classic classic::Pool_arena; SHM-jemalloc arena_lend::jemalloc::Ipc_arena).
35 * - "First-class object level": counts things about objects that were `arena.construct()`ed, returning a `shared_ptr`
36 * *handle*: not all individual `arena.allocate()`ed buffers. (E.g., one can `construct<vector<char, ...>>()` --
37 * that is one first-class object, accessed via `shared_ptr<vector<char, ...>>`. Such guys are tracked here.
38 * Cf.: the `sizeof(vector)`-sized buffer + any subsequently allocated `vector::capacity()`-sized buffer -- both
39 * in SHM. These are not tracked here.)
40 * - "Solely a... heap": An *arena* can `construct()`, `allocate()`, `deallocate()` -- basically similar to
41 * the `malloc()`ing heap, spiritually. We track this here.
42 * Cf.: In Flow-IPC SHM-land, however, a `construct()`ed handle can then be *shared* (or *lent*) to potentially
43 * other process(es). This is not tracked here.
44 * - "SHM-provider-agnostic": Let's take the first two historically available SHM-providers as representative
45 * examples: SHM-classic (arena-sharing type), SHM-jemalloc (arena-lending type). This set of stats applies
46 * to both of them and all currently conceived SHM-providers. In particular all are expected to
47 * `construct<T>()`, with a custom disposer on the returned `shared_ptr<T>`, among other expected behaviors.
48 *
49 * Attention: These stats apply to an arena *object*; not necessarily to the arena in SHM which is accessible
50 * from multiple processes (in various ways depending on SHM-provider type). For example see #m_construct_count
51 * doc header.
52 *
53 * @see ipc::shm doc header explains some core ideas, notably the arena-sharing versus arena-lending SHM-provider
54 * classification.
55 * @see Lender_obj_stats: That concerns sharing `construct()`ed handles with (typically) other process(es).
56 *
57 * ### Convention ###
58 * Any SHM-provider will have one of these per-arena, and it will be arguably of first-glance interest.
59 * A suggested/followed convention is, if possible, to therefore place this particular `struct` at the top
60 * of any top-level bundling of SHM-related `Stat_set`s. For this reason we place the quasi-diagnostic
61 * stat-member #m_n_shards in this `struct` -- at its top at that; so it'll be immediately and easily visible --
62 * and it is likely to apply to all or most of the sharded parts of the containing bundling-`struct`.
63 *
64 * ### Mechanics ###
65 * It is a `Stat_set` as usable with `flow::util::stat` stats-wrangling infrastructure. That constrains its basic
66 * shape; in short, basically, it is just a `struct` with a member per stat -- plus declare_stats() declaring
67 * and classifying those members as ACCUMULATORs, GAUGEs, HI_WMARKs. Various properties of
68 * the ways in which these `struct`s will be updated (stat-keeping) in practice constrain further aspects of
69 * these stat-members. To wit:
70 *
71 * It tracks an `Arena` object's (e.g.: classic::Pool_arena, arena_lend::jemalloc::Ipc_arena) activities, and
72 * these activities, to do with at least allocation/deallocation, shall execute from potentially multiple threads
73 * concurrently, most likely without (applicable) synchronization. Therefore we use `atomic<>`s
74 * (and `Histogram_counter`s which internally keep `atomic<>`s as well).
75 *
76 * At least some SHM-providers (as of this writing at least SHM-jemalloc) track stats in thread-locally (TL-) sharded
77 * fashion. (See `flow::util::stat` doc header for discussion of multi-threaded stat-keeping approaches. This is
78 * required background for the following.) GAUGEs within the shard `struct`s may in fact become negative, despite
79 * the recomposed-from-shards (i.e., summed) final values -- at stat-consumption time -- being non-negative.
80 * Therefore (as explained in the aforementioned doc header-contained TL-sharding discussion) we use *signed* types
81 * for these particular (GAUGE) `atomic`s.
82 *
83 * @note To be clear(er) this does not apply to *every* TL-sharded GAUGE but only ones where in a given thread's
84 * shard the stat-member's value can go negative (obviously); the trick is to know when that is the case.
85 * It's usually straightforward given a specific example; but essentially: Typically a `++` is eventually
86 * balanced with a `--`; and the goes-negative situation is avoided if the `--` always occurs from the
87 * same thread as the `++` it is balancing. If T1 does `++`, then T2 does the corresponding `--` later,
88 * T2's shard `Stat_set` can easily go negative.
89 *
90 * ### Rationale: Why no byte counts? ###
91 * You may notice that Owner_obj_stats, and all the other `*_obj_stats` nearby, count first-class objects
92 * or events across them. It would also have been possible, in many cases, to provides byte-total counterparts.
93 * For example, #m_construct_count could have `m_construct_bytes` counterpart. Sounds useful! It is however
94 * less useful than it appears. Effectively such things would basically add `sizeof(T)` for each `_bytes`
95 * counterpart, when the corresponding object-count is incremented by 1. Thing is, in practice, that would
96 * potentially only count a small fraction of what the particular `T` is actually using in RAM. If `T` is
97 * plain-old-data -- e.g., a `struct` of `int`s and the like -- the fraction is 100%; great. If `T` is an
98 * STL-compliant container with a proper allocator (we recommend shm::stl::Stateless_allocator), though, then
99 * the `T()` ctor alone could be allocating (e.g.) megabytes; not to mention potential subsequent allocs
100 * (and deallocs) through the `T` allocator.
101 *
102 * Granted: this means sometimes recording `sizeof(T)` would be interesting; and sometimes it would be silly
103 * and only a small % of the true RAM use. It is realistically an unknown mix; so tracking this would be
104 * an unknown mix of useful and useless. That said it would not hurt (other than taking processor cycles --
105 * which is not nothing) to have it, as long as the user is made ultra-aware of the allocator caveat (but it's not
106 * a caveat; it's a serious flaw). Currently we don't view it as a to-do, as both taking non-trivial cycles
107 * and being kinda-sorta-useful-but-often-misleading means, let's not, unless feedback from the field indicates
108 * otherwise.
109 *
110 * @internal
111 * Would it be possible to automatically capture all the allocator-driven RAM "charged" to a given
112 * `construct()`ed first-class object? Answer: kind of but not really, and it would be potentially
113 * perf-costly to the extent that it is possible. Avoiding all details of a potential impl of such a
114 * thing: Even supposing it were doable, it would directly imply stat-accounting in potentially *all*
115 * `allocate()` calls at least. classic::Pool_arena doc header, under "Impl notes," discusses the possibility
116 * of tracking stats in each `[de]allocate()`. However, even supposing one decides to impl this (and that's
117 * a big if), it's arguably not suitable for `*_obj_stats`.
118 */
120{
121 // Data.
122
123 /**
124 * (Diagnostic gauge) Number of shards used in the aggregated computation of this `struct`'s values (and quite
125 * possibly co-bundled ones' values). This stat-member shall equal 1 in the following situations:
126 * - TL-sharding (sharded aggregation) not used. (E.g., SHM-classic does not use TL-sharding; SHM-jemalloc
127 * does albeit not for everything... but definitely for Owner_obj_stats and several others it does.)
128 * - TL-sharding is used, but `*this` *is* a shard, not the aggregated result of combining shards.
129 * (Typically this would be only seen internally, not as a result seen by the user via stat-consumption API.)
130 * - TL-sharding is used, and there happened to be only 1 component shard (1 contributing thread).
131 */
132 unsigned int m_n_shards = 1;
133
134 /**
135 * Max of #m_n_shards observed so far; ignore if sharding is not used to collect `*this`.
136 *
137 * @internal
138 * The default is zero, instead of ` == m_n_shards` (which is `1`) due to a certain subtlety;
139 * see explanation of `fresh_stats_from_0_shards` arg in `flow::util::stats_aggregate_shards()` doc header.
140 */
141 unsigned int m_n_shards_hi_wmark = 0;
142
143 /**
144 * Count of the arena object's `.construct<T>()` calls by user. Each returns a `shared_ptr<T>` with a custom
145 * disposer.
146 *
147 * A subtlety applies depending on SHM-provider type: *arena-sharing* (SHM-classic: `arena` is
148 * classic::Pool_arena) or *arena-lending* (SHM-jemalloc: `arena` is jemalloc::Ipc_arena).
149 * - Arena-sharing: `arena` and `arena2` can (and commonly do) refer to the same arena-in-SHM.
150 * #m_construct_count counts only the `.construct<T>()` calls on the tracked `arena`.
151 * - Arena-lending: By the nature of this design, only one `arena` can refer to a particular arena-in-SHM.
152 * Therefore #m_construct_count counts all `.construct<T>()` calls relevant to the arena-in-SHM.
153 */
154 std::atomic<uint64_t> m_construct_count = 0;
155
156 /**
157 * Of the `shared_ptr` groups counted in #m_construct_count, count of the ones for which the disposer has
158 * executed. Recall the disposer executes once a ref-counted-pointer group's ref-count reaches zero.
159 *
160 * @note As tracked in Lender_obj_stats, a `construct()`ed (first-class) object can be lent to (shared with)
161 * other process(es). The object shall not be destroyed (garbage-collected: GCed) until all the resulting
162 * (*borrowed*) `shared_ptr<T>` groups *also* reach ref-count zero. An object counted in #m_disposer_count
163 * may well still be live due to lending. This type of disposer applies only to the original
164 * first-class handle as constructed via the arena.
165 */
166 std::atomic<uint64_t> m_disposer_count = 0;
167
168 /**
169 * Gauge: How many groups counted in #m_construct_count have not yet had their disposer execute (as counted
170 * in #m_disposer_count). This should ~equal the latter subtracted from the former (modulo the usual minor
171 * concurrency artifacts).
172 *
173 * ### Rationale ###
174 * We've considered eliminating #m_live_handle_groups, given that it represents
175 * `m_construct_count - m_disposer_count`, and those two are right there. We've kept it for the sake
176 * of the derived #m_live_handle_groups_hi_wmark however. The perf cost of maintaining it is negligible.
177 */
178 std::atomic<int64_t> m_live_handle_groups = 0;
179
180 /// Max of #m_live_handle_groups observed so far.
181 std::atomic<int64_t> m_live_handle_groups_hi_wmark = 0;
182
183 /**
184 * Number of times the `arena` object has *destroyed* a first-class (`construct()`ed) object stored in the
185 * underlying SHM-arena -- not necessarily originally `arena.construct()`ed by that same arena object.
186 *
187 * It is best to more specifically show what event this counts depending on SHM-provider type:
188 * *arena-sharing* (SHM-classic: `arena` is classic::Pool_arena) or *arena-lending* (SHM-jemalloc: `arena` is
189 * jemalloc::Ipc_arena).
190 * - Arena-sharing: `arena` and `arena2` can (and commonly do) refer to the same arena-in-SHM.
191 * Suppose we do `obj = arena.construct()` and then lend it/borrow it via
192 * `obj2 = arena2.borrow_object()`. Both refer to the same object. Eventually each of `obj` and `obj2`
193 * `shared_ptr` groups reaches ref-count=zero; that disposer runs. If `obj`'s disposer is last to run,
194 * then `arena` will *destroy*. If `obj2`'s disposer is last to run, then `arena2` will *destroy*.
195 * - Outcome: Owner_obj_stats::m_destroy_count for `arena` shall increment <=> `obj` disposer is last to run.
196 * - Outcome: Owner_obj_stats::m_destroy_count for `arena2` shall increment <=> `obj2` disposer is last to run.
197 * - Corollary: #m_destroy_count may well exceed #m_construct_count at steady state.
198 * In arena-sharing SHM-providers it is possible/normal for an `arena` object to destroy an object
199 * `construct()`ed by `arena2`, typically with a process boundary between them. It depends only on the
200 * order of disposer-invoked events (typically across processes).
201 * - Arena-lending: By the nature of this design, only one `arena` can refer to a particular arena-in-SHM.
202 * Only it can destroy `obj`s constructed therein. Therefore, #m_destroy_count is at most equal to
203 * #m_construct_count (modulo concurrency artifacts).
204 *
205 * @see #m_non_owner_destroy_count: Additional info about the more-complex *arena-sharing* case above.
206 */
207 std::atomic<uint64_t> m_destroy_count = 0;
208
209 /**
210 * Number of times the `arena` object has *destroyed* a first-class (`construct()`ed) object stored in the
211 * underlying SHM-arena -- but originally `arena.construct()`ed by a *different process*.
212 *
213 * @see m_destroy_count counts similar events but does not discriminate by constructing process.
214 * Its doc header provides necessary background for further discussion.
215 *
216 * The meaning, again, is best described by SHM-provider type first of all:
217 *
218 * If arena-lending (SHM-jemalloc): This value is always zero. `arena` can destroy only its own
219 * `arena.construct()`ed (first-class) objects. You can stop reading in that case. Otherwise let's assume
220 * we're dealing with arena-sharing (SHM-classic) provider. Then:
221 *
222 * The meaning is as written above; when destroying an object, if the internally-recorded process ID does *not*
223 * match the current process ID, #m_non_owner_destroy_count is incremented; otherwise it is not.
224 *
225 * @note While commonly -- in production scenarios -- `arena` and `arena2` that refer to the same shared SHM-arena
226 * will reside in different processes, in general this is not a requirement. This stat does not check
227 * which arena-object originally `construct()`ed an object -- only the process. The dichotomy described
228 * in #m_destroy_count doc header is *typically*, but not necessarily (especially in test scenarios),
229 * the same one resolved by #m_non_owner_destroy_count.
230 *
231 * @internal
232 *
233 * @todo For SHM-jemalloc specifically, a particular spiritually similar-to-`m_[non_owner_]destroy_count`
234 * stat-pair could be implemented: count how many times a borrowed-`obj`'s disposer was the last disposer (for
235 * the underlying object) to execute, versus how many times it was the original constructed-`obj`'s disposer.
236 * This would require storing an `atomic` count in SHM, almost certainly in
237 * arena_lend::detail::Lend_tracker_pool, similarly to Lend_tracker_pool::Metadata::m_n_unused (summed across
238 * threads at stat-consumption time). This has an impact on cross-thread perf and is not wholly
239 * trivial to code; so we should approach this only if there is demand in the field. All else being equal,
240 * though, it would be an interesting signal.
241 */
242 std::atomic<uint64_t> m_non_owner_destroy_count = 0;
243}; // struct Owner_obj_stats
244
245/**
246 * Flow-IPC SHM-module: SHM-provider-agnostic stats about an *arena* if viewed as being capable or *lending* to
247 * (sharing with) other process(es); otherwise like Owner_obj_stats.
248 *
249 * @see Owner_obj_stats doc header. We follow that same subject matter (obviously excluding what's already tracked
250 * in it) but now covering an arena's ability to *share* (*lend*) first-class objects, by handle, to
251 * other process(es).
252 *
253 * Notes in Owner_obj_stats doc header, except the aforementioned difference in subject matter, generally
254 * apply here.
255 */
257{
258 // Data.
259
260 /**
261 * Count of `.lend_object(obj)` calls, where `shared_ptr<T> obj` was obtained as `arena.construct<T>(...)` from
262 * the tracked arena.
263 *
264 * Context: We did not state above that `.lend_object()` was called as `arena.lend_object()`. That is because
265 * this aspect depends on the SHM-provider: *arena-sharing* (SHM-classic) or *arena-lending* (SHM-jemalloc).
266 * - Arena-sharing: The arena itself handles lending things from itself: `arena.lend_object()`.
267 * E.g.: see classic::Pool_arena::lend_object().
268 * - Arena-lending: A dedicated SHM-session object handles lending things: `shm_session.lend_object(obj)`.
269 * Nevertheless the `shm_session` knows from which arena the handle `obj` comes:
270 * `arena` itself had to first be *lent* to the process on the opposing side of `shm_session`.
271 * E.g.: see session::shm::arena_lend::jemalloc::Shm_session::lend_object(),
272 * session::shm::arena_lend::jemalloc::Shm_session::lend_arena().
273 *
274 * Incidentally: to allow for SHM-provider-type-agnostic user code, where possible, SHM-enabled
275 * ipc::session::Session impls provide forwarding/wrapping `lend_object()` APIs:
276 * - ipc::session::shm::classic::Session_mv::lend_object().
277 * - ipc::session::shm::arena_lend::jemalloc::Session_mv::lend_object().
278 */
279 std::atomic<uint64_t> m_lend_count = 0;
280}; // struct Lender_obj_stats
281
282/**
283 * Flow-IPC SHM-module: SHM-provider-agnostic stats about *borrowing* objects, typically across a process boundary,
284 * from a SHM arena.
285 *
286 * @see Lender_obj_stats doc header (which will also take you to the foundational Owner_obj_stats's doc header).
287 * We track the activities on the opposing side of a (typically) cross-process relationship with one or
288 * more SHM-allocating arenas. All notes from those doc headers apply except those contradicted by the
289 * following information specific to Borrower_obj_stats.
290 *
291 * Consider `arena.construct<T>`, which yields `shared_ptr<T> obj`. (This is tracked in Owner_obj_stats).
292 * Next consider `.lend_object<T>(obj)`, which (1) is a promise to *borrow* this object elsewhere and (2) returns
293 * a blob encoding the SHM-handle `obj` in a process-agnostic manner. (This is tracked in Lender_obj_stats.)
294 *
295 * Now consider how the borrowing process can access `*obj` (in SHM). To wit: one calls
296 * `B.borrow_object<T>(blob)` (where `blob` is a copy of what `.lend_object<T>()` returned).
297 * This returns -- very much like the original `.construct<T>()` -- a `shared_ptr<T> obj_brwed` fitted with
298 * a custom disposer. That ref-counted-pointer group eventually reaches ref-count zero, and the *disposer*
299 * runs. `*obj` is GCed once every such borrowed-handle disposer and the original constructed-handle disposer
300 * have all executed. Therefore the constructed and borrowed handles act as a *cross-process `shared_ptr<T>`*.
301 *
302 * Borrower_obj_stats tracks *one* `B`'s borrower and borrower-disposer ops.
303 *
304 * ### What is `B`? ###
305 * This aspect depends on the SHM-provider: *arena-sharing* (SHM-classic) or *arena-lending* (SHM-jemalloc).
306 * - Arena-sharing: The arena itself handles borrowing things constructed (through that same arena-in-SHM)
307 * in an opposing process: `arena.borrow_object()`.
308 * E.g.: see classic::Pool_arena::borrow_object().
309 * - Arena-lending: A dedicated SHM-session object handles borrowing things: `shm_session.borrow_object(blob)`.
310 * E.g.: see session::shm::arena_lend::jemalloc::Shm_session::borrow_object().
311 *
312 * To reiterate: A Borrower_obj_stats tracks *one* arena-or-session's (depending on SHM-provider context)
313 * accumulated stats. This is a consequence of the difference in fundamental design of the two SHM-provider
314 * categories (arena-sharing versus arena-lending).
315 *
316 * @note OK, but Lender_obj_stats::m_lend_count (at least) is nevertheless always per-arena, even though
317 * `.lend_object()` is also executed on an arena-or-session object, depending. So why can't we do the
318 * same? The answer is out of our scope here, arguably, but nevertheless briefly: Applying the question to
319 * a `shm_session`: Can't it track borrow/dispose ops by *originating* cross-process arena? Answer:
320 * Sort of; there is no `arena` object to represent the *opposing* (borrowed-from) arena, but such stats
321 * could still be tracked by opposing-arena-ID or some such contraption. This may or may not be of
322 * utility, and it's conceivable we'd add such tracking (and then change the doc header you are reading).
323 * Even so, a point to convey here is this: Such stat-segregation would be (in the overall system design
324 * as of this writing) probably unique to stat-keeping: There is no other reason for the arena-lending
325 * SHM-provider's (e.g., SHM-jemalloc) user to worry about the originating arena per se, when borrowing
326 * an object. That's a fundamental aspect of the arena-lending design.
327 *
328 * @note The set of members is almost identical to that of Owner_obj_stats albeit applied to `.borrow_object()`
329 * instead of `.construct()`. It would have been not-unreasonable to reuse a common `struct` for both
330 * purposes. Nevertheless, for purposes of organization and clear documentation, we decided not to
331 * take advantage of this. Explaining the overloaded/dual purpose of each stat-member would have been
332 * unpleasant for all involved, we feel.
333 */
335{
336 // Data.
337
338 /**
339 * Count of the arena-or-session's `.borrow_object<T>()` calls by user. Each returns a `shared_ptr<T>`
340 * with a custom disposer.
341 *
342 * This is, in some key ways, similar to Owner_obj_stats::m_construct_count; see our `struct` doc header
343 * for background.
344 */
345 std::atomic<uint64_t> m_borrow_count = 0;
346
347 /**
348 * Of the `shared_ptr` groups counted in #m_borrow_count, count of the ones for which the disposer has
349 * executed. Recall the disposer executes once a ref-counted-pointer group's ref-count reaches zero.
350 *
351 * This is, in some key ways, similar to Owner_obj_stats::m_disposer_count; see our `struct` doc header
352 * for background, including the relationship between GC (garbage-collection of borrowed `*obj`s) and
353 * the tracked disposer.
354 */
355 std::atomic<uint64_t> m_disposer_count = 0;
356
357 /**
358 * Gauge: How many groups counted in #m_borrow_count have not yet had their disposer execute (as counted
359 * in #m_disposer_count). This should ~equal the latter subtracted from the former (modulo the usual minor
360 * concurrency artifacts).
361 *
362 * This is, in some key ways, similar to Owner_obj_stats::m_live_handle_groups; see our `struct` doc header
363 * for background.
364 */
365 std::atomic<int64_t> m_live_handle_groups = 0;
366
367 /// Max of #m_live_handle_groups observed so far.
368 std::atomic<int64_t> m_live_handle_groups_hi_wmark = 0;
369}; // struct Borrower_obj_stats
370
371/**
372 * Stats `struct` thematically relevant to gauging outstanding (not-yet-garbage-collected) first-class
373 * objects in a given in-SHM arena.
374 *
375 * Due to certain impl differences between SHM-providers (particularly arena-sharing versus arena-lending types),
376 * these stats would otherwise belong to different `struct`s depending on SHM-provider type. That is why it
377 * is a separate `struct`.
378 * - For *arena-sharing* SHM-providers (SHM-classic): These `m_` stats would belong to
379 * Shared_arena_obj_stats. Therefore a Live_obj_stats is reported alongside a Shared_arena_obj_stats.
380 * - For *arena-lending* SHM-providers (SHM-jemalloc): These `m_` stats would belong to
381 * Owner_obj_stats. Therefore a Live_obj_stats is reported alongside an Owner_obj_stats.
382 */
384{
385 // Data.
386
387 /**
388 * Gauge: Count of first-class -- `arena.construct()`ed -- objects that have not yet been destroyed
389 * (via GC), in the tracked arena-in-SHM.
390 *
391 * For *arena-sharing* SHM-providers (SHM-classic): This tracks all objects in the underlying arena, not
392 * merely those `arena.construct()`ed by any particular `arena` object.
393 *
394 * For *arena-lending* SHM-providers (SHM-jemalloc): There is no such distinction by definition, as there
395 * is only one `arena` per in-SHM arena.
396 */
397 std::atomic<uint64_t> m_live_objects = 0;
398
399 /// Max of #m_live_objects observed so far.
400 std::atomic<uint64_t> m_live_objects_hi_wmark = 0;
401
402 /**
403 * Gauge: Of the objects tracked by #m_live_objects, the (hopefully small) subset that are ready for GC (all disposers
404 * have executed cross-process) but due to impl design have yet to be destroyed.
405 *
406 * As of this writing:
407 * - For SHM-classic: All objects are synchronously/immediately destroyed during the execution of the last
408 * disposer, regardless of the `arena` object that generated the `shared_ptr` group that executed the last
409 * disposer. Therefore this is always zero.
410 * - For SHM-jemalloc: Destruction occurs, internally, on an opportunistic piggy-backed basis. Large values
411 * here would indicate that certain threads `.construct()`ed many objects and then stopped performing any
412 * SHM-related work, preventing the opportunistic GC from occurring (so far).
413 */
414 std::atomic<uint64_t> m_live_object_zombies = 0;
415
416 /// Max of #m_live_object_zombies observed so far.
417 std::atomic<uint64_t> m_live_object_zombies_hi_wmark = 0;
418}; // struct Live_obj_stats
419
420/**
421 * Stats `struct` tracking quantities applicable only to the arena-sharing SHM-provider type
422 * (at least SHM-classic) but otherwise thematically similar to Owner_obj_stats, Lender_obj_stats, Borrower_obj_stats.
423 *
424 * For example:
425 * - Take SHM-classic (arena-sharing): Owner_obj_stats::m_construct_count counts the `arena.construct()`s performed
426 * via the particular tracked classic::Pool_arena object; while #m_construct_count counts all of them in
427 * the shared arena-in-SHM, regardless of where it happened (could be 1 of any number of `Pool_arena`s referring
428 * to the same SHM-pool).
429 * - Now take SHM-jemalloc (arena-lending): There *is* only one jemalloc::Ipc_arena object that can refer
430 * to the given SHM-arena. So Owner_obj_stats::m_construct_count is sufficient.
431 */
433{
434 // Data.
435
436 /**
437 * Similar to Owner_obj_stats::m_construct_count but applies to all `arena.construct()`s executed w/r/t the
438 * tracked arena-in-SHM (regardless of `arena` object).
439 *
440 * Reminder: Applies to *arena-sharing* SHM-providers (at least SHM-classic) only.
441 */
442 std::atomic<uint64_t> m_construct_count = 0;
443
444 /**
445 * Of the `shared_ptr` groups counted in #m_construct_count, count of the ones for which the disposer has
446 * executed. Recall the disposer executes once a ref-counted-pointer group's ref-count reaches zero.
447 * This is to #m_construct_count what Owner_obj_stats::m_disposer_count is to Owner_obj_stats::m_construct_count.
448 *
449 * @note See background notes in Owner_obj_stats::m_disposer_count doc header.
450 *
451 * Reminder: Applies to *arena-sharing* SHM-providers (at least SHM-classic) only.
452 */
453 std::atomic<uint64_t> m_construct_disposer_count = 0;
454
455 /**
456 * Similar to Owner_obj_stats::m_destroy_count but applies to all destructions of `construct()`ed-objects
457 * stored in the tracked arena-in-SHM (regardless of which `arena` object executed the destruction).
458 *
459 * Reminder: Applies to *arena-sharing* SHM-providers (at least SHM-classic) only.
460 */
461 std::atomic<uint64_t> m_destroy_count = 0;
462
463 /**
464 * Of the destructions tracked by #m_destroy_count, the subset where a destruction occurs in a different process
465 * than the one that originally `.construct()`ed it. This is to #m_destroy_count what
466 * Owner_obj_stats::m_non_owner_destroy_count is to Owner_obj_stats::m_destroy_count.
467 *
468 * Reminder: Applies to *arena-sharing* SHM-providers (at least SHM-classic) only.
469 */
470 std::atomic<uint64_t> m_non_owner_destroy_count = 0;
471
472 /**
473 * Similar to Lender_obj_stats::m_lend_count but applies to all `arena.lend_object()`s executed w/r/t the
474 * tracked arena-in-SHM (regardless of `arena` object).
475 *
476 * Reminder: Applies to *arena-sharing* SHM-providers (at least SHM-classic) only.
477 */
478 std::atomic<uint64_t> m_lend_count = 0;
479
480 /**
481 * Similar to Borrower_obj_stats::m_borrow_count but applies to all `arena.borrow_object()`s executed w/r/t the
482 * tracked arena-in-SHM (regardless of `arena` object).
483 *
484 * Reminder: Applies to *arena-sharing* SHM-providers (at least SHM-classic) only.
485 */
486 std::atomic<uint64_t> m_borrow_count = 0;
487
488 /**
489 * Of the `shared_ptr` groups counted in #m_borrow_count, count of the ones for which the disposer has
490 * executed. Recall the disposer executes once a ref-counted-pointer group's ref-count reaches zero.
491 * This is to #m_borrow_count what Borrower_obj_stats::m_disposer_count is to Borrower_obj_stats::m_borrow_count.
492 *
493 * Reminder: Applies to *arena-sharing* SHM-providers (at least SHM-classic) only.
494 */
495 std::atomic<uint64_t> m_borrow_disposer_count = 0;
496}; // struct Shared_arena_obj_stats
497
498// Template implementations.
499
500template<typename Visitor>
501void declare_stats(const flow::util::stat::Stat_name& name_prefix,
502 const Owner_obj_stats* src_stats, Owner_obj_stats* target_stats,
503 Visitor&& visitor)
504{
505 FLOW_UTIL_STAT_DECLARE(m_n_shards, GAUGE);
506 FLOW_UTIL_STAT_DECLARE_HI_WMARK(m_n_shards_hi_wmark, m_n_shards);
507 FLOW_UTIL_STAT_DECLARE(m_construct_count, ACCUMULATOR);
508 FLOW_UTIL_STAT_DECLARE(m_disposer_count, ACCUMULATOR);
509 FLOW_UTIL_STAT_DECLARE(m_live_handle_groups, GAUGE);
510 FLOW_UTIL_STAT_DECLARE_HI_WMARK(m_live_handle_groups_hi_wmark, m_live_handle_groups);
511 FLOW_UTIL_STAT_DECLARE(m_destroy_count, ACCUMULATOR);
512 FLOW_UTIL_STAT_DECLARE(m_non_owner_destroy_count, ACCUMULATOR);
513}
514
515template<typename Visitor>
516void declare_stats(const flow::util::stat::Stat_name& name_prefix,
517 const Lender_obj_stats* src_stats, Lender_obj_stats* target_stats,
518 Visitor&& visitor)
519{
520 FLOW_UTIL_STAT_DECLARE(m_lend_count, ACCUMULATOR);
521}
522
523template<typename Visitor>
524void declare_stats(const flow::util::stat::Stat_name& name_prefix,
525 const Borrower_obj_stats* src_stats, Borrower_obj_stats* target_stats,
526 Visitor&& visitor)
527{
528 FLOW_UTIL_STAT_DECLARE(m_borrow_count, ACCUMULATOR);
529 FLOW_UTIL_STAT_DECLARE(m_disposer_count, ACCUMULATOR);
530 FLOW_UTIL_STAT_DECLARE(m_live_handle_groups, GAUGE);
531 FLOW_UTIL_STAT_DECLARE_HI_WMARK(m_live_handle_groups_hi_wmark, m_live_handle_groups);
532}
533
534template<typename Visitor>
535void declare_stats(const flow::util::stat::Stat_name& name_prefix,
536 const Live_obj_stats* src_stats, Live_obj_stats* target_stats,
537 Visitor&& visitor)
538{
539 FLOW_UTIL_STAT_DECLARE(m_live_objects, GAUGE);
540 FLOW_UTIL_STAT_DECLARE_HI_WMARK(m_live_objects_hi_wmark, m_live_objects);
541 FLOW_UTIL_STAT_DECLARE(m_live_object_zombies, GAUGE);
542 FLOW_UTIL_STAT_DECLARE_HI_WMARK(m_live_object_zombies_hi_wmark, m_live_object_zombies);
543}
544
545template<typename Visitor>
546void declare_stats(const flow::util::stat::Stat_name& name_prefix,
547 const Shared_arena_obj_stats* src_stats, Shared_arena_obj_stats* target_stats,
548 Visitor&& visitor)
549{
550 FLOW_UTIL_STAT_DECLARE(m_construct_count, ACCUMULATOR);
551 FLOW_UTIL_STAT_DECLARE(m_construct_disposer_count, ACCUMULATOR);
552 FLOW_UTIL_STAT_DECLARE(m_destroy_count, ACCUMULATOR);
553 FLOW_UTIL_STAT_DECLARE(m_non_owner_destroy_count, ACCUMULATOR);
554 FLOW_UTIL_STAT_DECLARE(m_lend_count, ACCUMULATOR);
555 FLOW_UTIL_STAT_DECLARE(m_borrow_count, ACCUMULATOR);
556 FLOW_UTIL_STAT_DECLARE(m_borrow_disposer_count, ACCUMULATOR);
557}
558
559} // namespace ipc::shm::stat
Stats-related sub-namespace, for ADL segregation and general organization.
Definition: shm_fwd.hpp:177
void declare_stats(const flow::util::stat::Stat_name &name_prefix, const Owner_obj_stats *src_stats, Owner_obj_stats *target_stats, Visitor &&visitor)
Declares the stats for Owner_obj_stats.
Definition: shm_stats.hpp:501
Flow-IPC SHM-module: SHM-provider-agnostic stats about borrowing objects, typically across a process ...
Definition: shm_stats.hpp:335
std::atomic< uint64_t > m_disposer_count
Of the shared_ptr groups counted in m_borrow_count, count of the ones for which the disposer has exec...
Definition: shm_stats.hpp:355
std::atomic< int64_t > m_live_handle_groups_hi_wmark
Max of m_live_handle_groups observed so far.
Definition: shm_stats.hpp:368
std::atomic< uint64_t > m_borrow_count
Count of the arena-or-session's .borrow_object<T>() calls by user.
Definition: shm_stats.hpp:345
std::atomic< int64_t > m_live_handle_groups
Gauge: How many groups counted in m_borrow_count have not yet had their disposer execute (as counted ...
Definition: shm_stats.hpp:365
Flow-IPC SHM-module: SHM-provider-agnostic stats about an arena if viewed as being capable or lending...
Definition: shm_stats.hpp:257
std::atomic< uint64_t > m_lend_count
Count of .lend_object(obj) calls, where shared_ptr<T> obj was obtained as arena.construct<T>(....
Definition: shm_stats.hpp:279
Stats struct thematically relevant to gauging outstanding (not-yet-garbage-collected) first-class obj...
Definition: shm_stats.hpp:384
std::atomic< uint64_t > m_live_objects_hi_wmark
Max of m_live_objects observed so far.
Definition: shm_stats.hpp:400
std::atomic< uint64_t > m_live_object_zombies
Gauge: Of the objects tracked by m_live_objects, the (hopefully small) subset that are ready for GC (...
Definition: shm_stats.hpp:414
std::atomic< uint64_t > m_live_objects
Gauge: Count of first-class – arena.construct()ed – objects that have not yet been destroyed (via GC)...
Definition: shm_stats.hpp:397
std::atomic< uint64_t > m_live_object_zombies_hi_wmark
Max of m_live_object_zombies observed so far.
Definition: shm_stats.hpp:417
Flow-IPC SHM-module: SHM-provider-agnostic stats about an arena object if viewed as solely a memory-m...
Definition: shm_stats.hpp:120
unsigned int m_n_shards_hi_wmark
Max of m_n_shards observed so far; ignore if sharding is not used to collect *this.
Definition: shm_stats.hpp:141
std::atomic< uint64_t > m_construct_count
Count of the arena object's .construct<T>() calls by user.
Definition: shm_stats.hpp:154
std::atomic< uint64_t > m_disposer_count
Of the shared_ptr groups counted in m_construct_count, count of the ones for which the disposer has e...
Definition: shm_stats.hpp:166
std::atomic< uint64_t > m_non_owner_destroy_count
Number of times the arena object has destroyed a first-class (construct()ed) object stored in the und...
Definition: shm_stats.hpp:242
std::atomic< uint64_t > m_destroy_count
Number of times the arena object has destroyed a first-class (construct()ed) object stored in the und...
Definition: shm_stats.hpp:207
std::atomic< int64_t > m_live_handle_groups_hi_wmark
Max of m_live_handle_groups observed so far.
Definition: shm_stats.hpp:181
std::atomic< int64_t > m_live_handle_groups
Gauge: How many groups counted in m_construct_count have not yet had their disposer execute (as count...
Definition: shm_stats.hpp:178
unsigned int m_n_shards
(Diagnostic gauge) Number of shards used in the aggregated computation of this struct's values (and q...
Definition: shm_stats.hpp:132
Stats struct tracking quantities applicable only to the arena-sharing SHM-provider type (at least SHM...
Definition: shm_stats.hpp:433
std::atomic< uint64_t > m_construct_disposer_count
Of the shared_ptr groups counted in m_construct_count, count of the ones for which the disposer has e...
Definition: shm_stats.hpp:453
std::atomic< uint64_t > m_construct_count
Similar to Owner_obj_stats::m_construct_count but applies to all arena.construct()s executed w/r/t th...
Definition: shm_stats.hpp:442
std::atomic< uint64_t > m_lend_count
Similar to Lender_obj_stats::m_lend_count but applies to all arena.lend_object()s executed w/r/t the ...
Definition: shm_stats.hpp:478
std::atomic< uint64_t > m_borrow_disposer_count
Of the shared_ptr groups counted in m_borrow_count, count of the ones for which the disposer has exec...
Definition: shm_stats.hpp:495
std::atomic< uint64_t > m_non_owner_destroy_count
Of the destructions tracked by m_destroy_count, the subset where a destruction occurs in a different ...
Definition: shm_stats.hpp:470
std::atomic< uint64_t > m_destroy_count
Similar to Owner_obj_stats::m_destroy_count but applies to all destructions of construct()ed-objects ...
Definition: shm_stats.hpp:461
std::atomic< uint64_t > m_borrow_count
Similar to Borrower_obj_stats::m_borrow_count but applies to all arena.borrow_object()s executed w/r/...
Definition: shm_stats.hpp:486