Flow-IPC 3.0.0
Flow-IPC project: Full implementation reference.
info_collector.hpp
Go to the documentation of this file.
1/* Flow-IPC: Sessions
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
25#include "ipc/util/util.hpp"
26#include <boost/weak_ptr.hpp>
27#include <boost/shared_ptr.hpp>
28#include <cassert>
29#include <ostream>
30
31namespace ipc::session
32{
33
34// Types.
35
36/**
37 * Bundles access to stats from the session master channel (SMC) -- the internal `struc::Channel` each Session uses
38 * for protocol traffic (log-in, channel-opening, graceful close, liveness status). The user obtains an
39 * `Info_collector*` via Session::info_collector().
40 *
41 * Two levels of stats are exposed as of this writing:
42 * - Structured-channel stats (transport::struc::stat::Channel_stats): master_channel_stats(),
43 * master_channel_stats_reset().
44 * - Transport-level stats from the underlying `Native_socket_stream`: master_channel_transport_snd_stats(),
45 * master_channel_transport_snd_stats_reset(), master_channel_transport_rcv_stats(),
46 * master_channel_transport_rcv_stats_reset().
47 *
48 * One can work in standard `flow::util::stat` ways with each stat-set accessed via `*_stats()`. This includes
49 * regular `struct` member reads (of course), sum/aggregation with others (`flow::util::stats_aggregate()` et al),
50 * and printing via `ostream << print(...)`.
51 *
52 * W/r/t the latter operation -- printing -- it is also possible to print all the `*_stats()` in one shot:
53 * simply use `os << *this`. You may use the simple knobs in (public, mutable) #m_fmt to affect the format of
54 * the print-op (e.g.: multi-line versus single-line).
55 *
56 * @note Slight subtlety: A `*this` is a live-accessor of information, not a store (dump) of information.
57 * It has those `*_reset()` methods, for one thing; but more to the point: It is not a classic `struct`
58 * with public data members storing the stats/info (cf., say, the SHM-providers' `Info_dump` pattern -- e.g.,
59 * shm::classic::Pool_arena::Info_dump). Each `*_stats()` call will grab a snapshot of those data
60 * as of this moment; they may not equal what it returns a split-second later. Session::info_collector(),
61 * which returns us, returns a pointer (`this`) and does not itself capture any stats/info.
62 * (Cf., say, shm::classic::Pool_arena::info_dump() which returns a new stats/info-filled object.)
63 * @note Corollary: `ostream << *this` shall internally perform all of `*_stats()` info-grabbing ops.
64 * (Cf. `ostream <<` of Pool_arena::Info_dump; it outputs serializations of already-stored stats/info.)
65 *
66 * ### Subject matter ###
67 * The aforementioned SMC (structured master channel) stats track the behavior of an internal facility. One does
68 * not normally concern themselves with these workings; but in short ipc::session relies on a struc::Channel
69 * over a transport::Native_socket_stream to establish and end any Session; as well as opening on-demand
70 * user `Channel`s throughout, plus liveness detection (so that if the opposing side ends the session, this side
71 * will quickly know and report to the user, who should stop that IPC session/start a new one/etc.). Some
72 * potential ways in which stats about this are useful:
73 * - to ascertain that the background traffic exchanged uses negligible resources (versus the user's actual
74 * IPC work which ipc::session makes possible by establishing IPC conversations a/k/a sessions);
75 * - in long-lived processes to get an idea of how many channels are opened when;
76 * - to generally understand what's happening behind the scenes.
77 *
78 * ### Thread safety ###
79 * It is safe to call all methods (`const` or otherwise) at any time from any thread (even though the Session operates
80 * the SMC in the background).
81 *
82 * `ostream << *this` is not formally safe w/r/t a concurrent modification of `*this` #m_fmt.
83 *
84 * ### Lifetime ###
85 * The pointer returned by Session::info_collector() is stable from PEER state (for `Client_session`, after
86 * `sync_connect()` returns; for `Server_session` throughout its lifetime once the user obtains access to
87 * it via successful Session_server::async_accept() or equivalent). Before PEER state,
88 * `Client_session::info_collector()` returns null.
89 *
90 * That pointer points to a member of the `Session` and hence must not be dereferenced beyond that `Session`'s
91 * life. However `Info_collector` is copyable, and a copy may safely outlive the source `Session`: once the
92 * `Session` (and hence the SMC) is gone -- master_channel_live() returns `false` -- the stat-getters return
93 * all-zero values; the resetters no-op; and printing `*this` yields a note that the stats
94 * are unavailable.
95 *
96 * @internal
97 *
98 * ### Impl notes ###
99 * It is quite straightforward, once the situation has gotten to `*this` existing. The only question that arises,
100 * we think, is thread safety: we guarantee above that they can call any of our methods anytime during the lifetime
101 * of the generating `Session`. There are two levels to that:
102 * - Is the saved pointer #m_master_channel pointing to the right thing, and is that thing alive? Answer:
103 * yes, as by the time either Client_session_impl or Server_session_impl creates a `*this`, *and* the user
104 * has access to the `Session` in the first place: each of those classes internally promises
105 * the pointee SMC data member stays around until the destruction of the `..._impl` and its containing
106 * `Session`.
107 * - Are the actual calls to which we forward safe to call, while the SMC (a struc::Channel) is doing
108 * concurrent work (receiving traffic and such)? Answer: yes:
109 * - `struc::Channel::stats()` (and `stats_reset()`) internally lock the SMC's own mutex -- advertised as
110 * safe-anytime; we are just another user in that sense.
111 * - struc::Channel::owned_channel() and struc::Channel::owned_channel_mutable() return
112 * `flow::util::Locked_proxy`s instead of raw ptrs/refs; as a result the async-I/O-pattern `Channel`'s
113 * internal mutex is locked through the end of the expression (in our case `...->*_stats[_reset]()`).
114 * We use those (as of this writing there is no alternative/not-locking way in fact).
115 *
116 * Note the Lifetime section speaks only of Session destruction causing `!master_channel_live()`. Internally
117 * there is also a degenerate window: Server_session_impl's init-channel-opening failure path resets the SMC
118 * `shared_ptr` *after* having emplaced its Info_collector member. However that is not user-reachable: a failed
119 * `Session_server::async_accept()` leaves the user's target `Server_session` empty (as-if default-cted), so the user
120 * never sees the affected `..._impl`. And even if some future refactor changed that: the graceful
121 * `!master_channel_live()` semantics make the window moot anyway.
122 *
123 * Regarding stat-related `*stats_configure_...()`s, such as on struc::Channel at least: Naively it might make
124 * sense to expose/forward those too. However, as of this writing, that would make no sense: The relevant
125 * `Channel`s are already `Channel::start()`ed by the time the user has access to the relevant Session.
126 *
127 * Lastly regarding #Master_structured_channel, which is a template parameter: The type of the SMC object
128 * in Client_session_impl and Server_session_impl is "almost" concrete but does depend on the `typename Mdt_payload`
129 * parameter, such as to #Client_session and #Server_session and equivalents, that must be supplied
130 * by the user (with a default of `Void`, in the likely case they do not wish to use that feature). As for what
131 * `Mdt_payload` is for: see Session concept. That topic is irrelevant to us here though.
132 *
133 * @endinternal
134 *
135 * @tparam Master_structured_channel_t
136 * This is an implementation detail. The concrete type for the user is aliased as Session::Info_collector.
137 */
138template<typename Master_structured_channel_t>
140{
141public:
142 // Types.
143
144 /// The concrete `struc::Channel` type of the session master channel.
145 using Master_structured_channel = Master_structured_channel_t;
146
147 // Data.
148
149 /**
150 * Formatting knobs for printing `*this` via `ostream <<`. util::stat::Info_dump_format is also used by other
151 * Flow-IPC code; here `m_multiline` is honored, while `m_verbose` has no effect (there is no verbose-only
152 * content to gate).
153 */
155
156 // Methods.
157
158 /**
159 * Returns whether the session master channel still exists; `false` means the source `Session` has been
160 * destroyed (`*this` being a copy that outlived it; see class doc header, Lifetime section). When `false`:
161 * the stat-getters return all-zero values; the resetters no-op; printing `*this` yields
162 * a note that the stats are unavailable.
163 *
164 * @return See above.
165 */
166 bool master_channel_live() const;
167
168 /**
169 * struc::Channel::stats() of the session master channel of the Session that spawned `*this`.
170 * @return See above; or an all-zero value if `!master_channel_live()`.
171 */
173
174 /**
175 * struc::Channel::reset_stats() of the session master channel of the Session that spawned `*this`.
176 * No-op if `!master_channel_live()`.
177 */
179
180 /**
181 * Native_socket_stream::native_handle_snd_stats() of the transport channel powering the session master
182 * channel of the Session that spawned `*this`.
183 *
184 * @note Despite the aforementioned reference to `native_handle`, the stats `struct` really covers both
185 * (arguably mainly) data being trafficked as well as native handles (which are indeed involved in
186 * various things, notably opening `Native_socket_stream`-containing channels through the Session).
187 * It's an artifact (not that we're saying it's a bad thing) of how concepts interact --
188 * Native_handle_sender versus Blob_sender and so on.
189 *
190 * @return See above; or an all-zero value if `!master_channel_live()`.
191 */
193
194 /**
195 * Native_socket_stream::native_handle_snd_stats_reset() of the transport channel powering the session master
196 * channel of the Session that spawned `*this`. No-op if `!master_channel_live()`.
197 */
199
200 /**
201 * Native_socket_stream::native_handle_rcv_stats() of the transport channel powering the session master
202 * channel of the Session that spawned `*this`.
203 *
204 * @note Same note as for master_channel_transport_snd_stats().
205 *
206 * @return See above; or an all-zero value if `!master_channel_live()`.
207 */
209
210 /**
211 * Native_socket_stream::native_handle_rcv_stats_reset() of the transport channel powering the session master
212 * channel of the Session that spawned `*this`. No-op if `!master_channel_live()`.
213 */
215
216private:
217 // Friends.
218
219 /// Attorney granting internal code access to at least the private ctor.
220 friend struct Info_collector_dtl;
221
222 // Types.
223
224 /// Short-hand for `shared_ptr` to #Master_structured_channel.
225 using Master_structured_channel_ptr = boost::shared_ptr<Master_structured_channel>;
226
227 /// Short-hand for `weak_ptr` observer of #Master_structured_channel_ptr.
228 using Master_structured_channel_observer = boost::weak_ptr<Master_structured_channel>;
229
230 // Constructors.
231
232 /**
233 * Constructs the collector, capturing a `weak_ptr` to the session master channel.
234 * Only callable by detail::Info_collector_dtl::ct_base().
235 *
236 * @param master_channel
237 * The SMC; must be non-null. A `weak_ptr` observer is stored.
238 */
239 explicit Info_collector(const Master_structured_channel_ptr& master_channel);
240
241 // Data.
242
243 /**
244 * `weak_ptr` to the session master channel.
245 *
246 * ### Semantics/rationale (why `weak_ptr`?) ###
247 * Internally to Client_session_impl and Server_session_impl, the SMC `shared_ptr` is immutable from PEER
248 * state through dtor -- even on session hosing the channel object remains alive in both Client_session_impl
249 * and Server_session_impl (and their public-facing subclasses). So `lock()` succeeds while the source
250 * `Session` lives (modulo the degenerate never-reached-working-order cases; see class doc header, Lifetime
251 * section) and fails once it is destroyed -- which is possible to observe from `*this` being a copy that
252 * outlived it. That distinction is exactly master_channel_live(); the accessors and `ostream <<` use it to
253 * behave gracefully (zeroes/no-op/note) rather than access a dead SMC.
254 */
256}; // class Info_collector
257
258// Free functions: in *_fwd.hpp.
259
260// Template implementations.
261
262template<typename Master_structured_channel_t>
264 m_master_channel(master_channel)
265{
266 assert(master_channel && "Info_collector ctor: master_channel must be non-null.");
267}
268
269template<typename Master_structured_channel_t>
271{
272 return !m_master_channel.expired();
273}
274
275template<typename Master_structured_channel_t>
277{
279
280 const auto master_channel = m_master_channel.lock();
281 return master_channel ? master_channel->stats() : Channel_stats{};
282}
283
284template<typename Master_structured_channel_t>
286{
287 const auto master_channel = m_master_channel.lock();
288 if (master_channel)
289 {
290 master_channel->stats_reset();
291 }
292}
293
294template<typename Master_structured_channel_t>
296{
298
299 const auto master_channel = m_master_channel.lock();
300 return master_channel ? master_channel->owned_channel()->native_handle_send_stats()
301 : Blob_snd_stats{1};
302}
303
304template<typename Master_structured_channel_t>
306{
307 const auto master_channel = m_master_channel.lock();
308 if (master_channel)
309 {
310 master_channel->owned_channel_mutable()->native_handle_send_stats_reset();
311 }
312}
313
314template<typename Master_structured_channel_t>
316{
318
319 const auto master_channel = m_master_channel.lock();
320 return master_channel ? master_channel->owned_channel()->native_handle_receive_stats()
321 : Blob_rcv_stats{1};
322}
323
324template<typename Master_structured_channel_t>
326{
327 const auto master_channel = m_master_channel.lock();
328 if (master_channel)
329 {
330 master_channel->owned_channel_mutable()->native_handle_receive_stats_reset();
331 }
332}
333
334template<typename Master_structured_channel_t>
335std::ostream& operator<<(std::ostream& os, const Info_collector<Master_structured_channel_t>& val)
336{
337 using util::String_view;
338 using flow::util::stat::print;
339
340 if (!val.master_channel_live())
341 {
342 // Per contract: the source Session is gone (or never reached working order); no stats exist to show.
343 return os << "[session master channel gone; no stats avail]";
344 }
345 // else
346
347 String_view ln; // Top-level-item separator.
348 if (val.m_fmt.m_multiline)
349 {
350 os << "- ";
351 ln = "\n- ";
352 }
353 else
354 {
355 ln = " | ";
356 }
357
358 return os << "smc-struc: [" << print(val.master_channel_stats()) << ']' << ln
359 << "smc-transport-snd: [" << print(val.master_channel_transport_snd_stats()) << ']' << ln
360 << "smc-transport-rcv: [" << print(val.master_channel_transport_rcv_stats()) << ']';
361 // (Intentional: no newline at the end.)
362} // operator<<(ostream&, Info_collector)
363
364} // namespace ipc::session
Bundles access to stats from the session master channel (SMC) – the internal struc::Channel each Sess...
bool master_channel_live() const
Returns whether the session master channel still exists; false means the source Session has been dest...
util::stat::Info_dump_format m_fmt
Formatting knobs for printing *this via ostream <<.
boost::shared_ptr< Master_structured_channel > Master_structured_channel_ptr
Short-hand for shared_ptr to Master_structured_channel.
transport::struc::stat::Channel_stats master_channel_stats() const
struc::Channel::stats() of the session master channel of the Session that spawned *this.
boost::weak_ptr< Master_structured_channel > Master_structured_channel_observer
Short-hand for weak_ptr observer of Master_structured_channel_ptr.
Info_collector(const Master_structured_channel_ptr &master_channel)
Constructs the collector, capturing a weak_ptr to the session master channel.
transport::stat::Blob_snd_stats master_channel_transport_snd_stats() const
Native_socket_stream::native_handle_snd_stats() of the transport channel powering the session master ...
Master_structured_channel_observer m_master_channel
weak_ptr to the session master channel.
void master_channel_transport_snd_stats_reset()
Native_socket_stream::native_handle_snd_stats_reset() of the transport channel powering the session m...
transport::stat::Blob_rcv_stats master_channel_transport_rcv_stats() const
Native_socket_stream::native_handle_rcv_stats() of the transport channel powering the session master ...
void master_channel_stats_reset()
struc::Channel::reset_stats() of the session master channel of the Session that spawned *this.
void master_channel_transport_rcv_stats_reset()
Native_socket_stream::native_handle_rcv_stats_reset() of the transport channel powering the session m...
Master_structured_channel_t Master_structured_channel
The concrete struc::Channel type of the session master channel.
Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...
Definition: app.cpp:29
std::ostream & operator<<(std::ostream &os, const App &val)
Prints string representation of the given App to the given ostream.
Definition: app.cpp:130
flow::util::String_view String_view
Short-hand for Flow's String_view.
Definition: util_fwd.hpp:112
This is the friend facade of Info_collector that exposes the private constructor, so that only intern...
Cumulative stats from the incoming-direction (receive) pipe of a Blob_receiver or Native_handle_recei...
Cumulative stats from the outgoing-direction (send) pipe of a Blob_sender or Native_handle_sender con...
Cumulative stats from a struc::Channel, composing the underlying sync_io::stat::Channel_stats with st...
Knobs meant to control the cosmetics of printing (ostream <<) a bundling of stats/info for some parti...
Definition: util.hpp:39
bool m_multiline
If true: Use newlines where appropriate for readability (except at the very end of the output); if fa...
Definition: util.hpp:81