Flow-IPC 3.0.0
Flow-IPC project: Full implementation reference.
shm_session.hpp
Go to the documentation of this file.
1/* Flow-IPC: SHM-jemalloc
2 * Copyright (c) 2023 Akamai Technologies, Inc.; and other contributors.
3 * Each commit is copyright by its respective author or author's employer.
4 *
5 * Licensed under the MIT License:
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE. */
24
25/// @file
26#pragma once
27
29#include "ipc/session/standalone/shm/arena_lend/ipc_shm_message.capnp.h"
35#include "ipc/shm/arena_lend/owner_shm_pool_listener.hpp"
45#include "ipc/util/util_fwd.hpp"
46#include <flow/util/basic_blob.hpp>
47#include <flow/util/util.hpp>
48#include <flow/error/error.hpp>
49#include <boost/utility.hpp>
50#include <boost/unordered/unordered_map.hpp>
51#include <boost/unordered/unordered_set.hpp>
52#include <atomic>
53#include <vector>
54#include <optional>
55#include <cstring>
56
58{
59
60// Types.
61
62/**
63 * In SHM-jemalloc, a context between this process and a single (typically other) opposing process w/r/t
64 * lending (sharing) in-SHM objects from our `Ipc_arena`s to them, and their doing the same conversely.
65 *
66 * @note In the wider Flow-IPC world, centered around ipc::session::Session, a session is -- quite similarly --
67 * a context for process A IPCing with process B (again A is usually not the same as B but can be, such as
68 * in testing/debugging). We are too, really; just it's not general IPCing but rather sharing of in-SHM items
69 * via the lend-borrow paradigm.
70 *
71 * @note These notes (in Shm_session doc header) are written from the standpoint of presenting it (and the rest of
72 * SHM-jemalloc) as a standalone system. In most situations a general Flow-IPC user can think of
73 * SHM-providers, whether SHM-jemalloc or another one (such as SHM-classic in shm::classic), interchangeably.
74 *
75 * @see ipc::shm namespace doc header for high-level description of the lend-borrow SHM-arena paradigm.
76 *
77 * @see Ipc_arena: to put things into one's own SHM arena. Then to share such a thing with another process --
78 * possibly more than one -- establish a Shm_session and use Shm_session::lend_object(); on the other
79 * side Shm_session::borrow_object() (peer Shm_session object). Ipc_arena doc header explains this setup --
80 * from a SHM-jemalloc-as-standalone-module PoV -- in more detail than the present recap.
81 *
82 * The aforementioned Ipc_arena doc header hopefully explains most things of interest including where a `*this`
83 * fits into things. If you read/grok that first, then Shm_session should make sense. We cover anything
84 * left-over in that vein next:
85 *
86 * ### Shm_session lifetime versus Ipc_arena ###
87 * Like Ipc_arena, one accesses `Shm_session`s exclusively via `shared_ptr` handles, including using
88 * the create() factory instead of any direct ctor. As noted regarding Ipc_arena, if one does
89 * `session->lend_arena(arena)` (with `shared_ptr<Ipc_arena> arena`), then `*arena` shall outlive `*session`.
90 * Thus, after you've lent an arena through a session, you must end the conversation with the opposing process
91 * before the arena (most notably its in-SHM resources) is allowed to be destroyed. (As of this writing
92 * there is no undo-`lend_arena()` API, but it could be added. That would modify the preceding statements in
93 * a self-explanatory way.)
94 *
95 * ### Shm_session lifetime versus object-handles returned via borrow_object() ###
96 * In short: When `this->borrow_object<T>()` returns a handle -- `shared_ptr<T>` -- to an in-SHM object constructed
97 * in an opposing arena -- the resulting `shared_ptr` group silently retains a `shared_ptr<Session>` to
98 * `*this`, dropped only when that smart-pointer group reaches ref-count zero. (As with Ipc_arena: that's not
99 * the same as the actual in-SHM object being destroyed; that requires the owner and all other borrowers to
100 * also drop their handles. Here it's about just the returned, process-local `shared_ptr<T>`.) Hence a
101 * Shm_session outlives all objects borrowed through it.
102 *
103 * ### When/why and how to destroy your Shm_session ###
104 * Firstly, how: Step 1, let all the `borrow_object()`ed handles reach ref-count zero. (Ipc_arena equivalent of
105 * this, w/r/t letting Ipc_arena go away, is to let `construct()`ed handles do the same.) Step 2, nullify your
106 * handle(s) to `*this`.
107 *
108 * Secondly, why/when: This mirrors the identical question about the more general Flow-IPC sessions. The answer is
109 * two-fold (the two triggers being mutually exclusive):
110 * - Local trigger: That is, you don't want to talk to them anymore. So you destroy your Shm_session to them.
111 * - Opposing trigger: That is, they don't want to talk to you anymore. This requires your finding out that
112 * fact somehow. Formally any technique will do. Informally/in practice:
113 * - If you're using a `*this` as supplied to your by wider Flow-IPC ipc::session mechanism: It is handled
114 * for you. This isn't in our purview but for the sake of providing context: A `Session` issues the
115 * registed on-error callback; hence you shall destroy that `Session` -- the connection is no more.
116 * That SHM-enabled `Session` will therefore destroy its internally stored Shm_session
117 * (the thing you access via Session::shm_session() if desired).
118 * - If you're using SHM-jemalloc in standalone fashion: Detect error via the `shm_channel_error_handler`
119 * you gave us via Shm_session::create(). Once that fires, destroy the Shm_session.
120 *
121 * ### The SHM-channel ###
122 * To create/start a `*this`, you must provide to create() a freshly established #Shm_channel
123 * (`sync_io`-pattern core, basically a socket ready to transmit).
124 *
125 * Why is it needed? Answer: Internally an arena-lending SHM-provider, SHM-jemalloc in this case, must sometimes
126 * send messages for internal purposes to synchronize certain key state. (High-level example for context:
127 * a lend-arena message with some info about A is internally sent when one does `session->lend_arena(A)`;
128 * now the opposing Shm_session will be able to borrow_object() items that are `A->construct()`ed across process
129 * boundary.)
130 *
131 * If you're using a `*this` via an ipc::session general `Session`, then it worries about all that, so you don't
132 * need to; you won't even be calling create() in the first place. If however you are using it standalone
133 * (example: you **are** the code in ipc::session general `Session` eating our own dog food!):
134 *
135 * Establish a #Shm_channel yourself (various techniques are documented elswhere) and provide it to
136 * Shm_session::create(); from that point on the resulting `*this` takes over that object.
137 *
138 * ### Thread safety ###
139 * lend_object() and borrow_object() are safe to call concurrently with each other and with any other method,
140 * on the same `*this` or otherwise. (In practice these are the frequently-called ones, often from various threads.)
141 * The rest have the usual default level of thread safety -- e.g., lend_arena() is not safe to call concurrently
142 * with another lend_arena() on the same `*this` -- unless a method's doc header says otherwise (e.g., the stats
143 * accessors are safe to call anytime).
144 *
145 * @internal
146 *
147 * @todo shm::arena_lend::jemalloc::Shm_session could probably become `shm::arena_lend::Shm_session`, handling
148 * sessions dealing with arena-lending SHM-arenas backed by any memory-manager-backed #Arena type (which could
149 * become a template parameter). Historically that had likely been a tall order, but looking at `Shm_session`
150 * insides now, it is actually (1) fairly contained in its duties, and (2) seems to make no jemalloc-specific
151 * mentions other #Arena itself (which, again, would become a template parameter). (Regarding "fairly contained
152 * in its duties": E.g., borrowed-pool and borrowed-object handling is now in `Thread_lcl_obj_db_*` and
153 * Borrower_shm_pool_collection_repository singletons -- both already `Shm_arena`-parameterized and -segregated.
154 * Owner_shm_pool_repository handles owner-side pools and objects. Shm_session forwards things to them when
155 * needed is all.)
156 */
158 /* `private`, not `public`: end-users have no business with our logging accessors, and (relatedly) there is
159 * internal concurrency, so we must not expose a thread-unsafe set_logger()/get_logger(). The logger is fixed
160 * at construction; our own (possibly-concurrent) logging reads it safely as it never changes thereafter. */
161 private flow::log::Log_context,
162 public std::enable_shared_from_this<Shm_session>,
163 private boost::noncopyable
164{
165public:
166 // Types.
167
168 /// Short-hand for the SHM-arena type.
170
171 /**
172 * First-class handle to an outer object as returned by borrow_object(); equals `Arena::Handle`
173 * a/k/a shm::arena_lend::Obj_handle.
174 *
175 * @tparam T
176 * Outer object type.
177 */
178 template<typename T>
180
181 /**
182 * Alias for an unstructured channel for our internal use, as passed to create() and taken-over from there
183 * by a `*this`. Again: it must a fresh object of this type. In particular, no
184 * `start_*_ops()` should have been called (which implies no send/receive transmission API calls either).
185 *
186 * @internal
187 * ### Choice of `transport::Channel` type ###
188 * We don't need to transmit native handles, so it'll
189 * have just a blobs pipe; so as of this writing that leaves 3 possibilities (perusing channel.hpp):
190 * - transport::Posix_mqs_channel_of_blobs (2 POSIX MQs facing each other);
191 * - transport::Bipc_mqs_channel_of_blobs (2 SHM-backed bipc-supplied MQs facing each other -- the SHM
192 * system is not us, so this isn't an infinite loop);
193 * - transport::Socket_stream_channel_of_blobs (1 bidirectional local stream, a/k/a Unix-domain socket).
194 *
195 * All would work, and tests show perf differences are minute for small messages like ours. In terms of ease of
196 * setup of an existing channel `Socket_stream_channel_of_blobs` is hard to beat (just send over a single
197 * native handle); and in terms of low-maintenance of cleanup even more so (just close the handle on each side;
198 * done). So let's go with that.
199 */
201
202 /**
203 * Alias for a light-weight blob used to encode SHM-handles emitted by lend_object() and consumed by borrow_object().
204 *
205 * @internal
206 * They're little; TRACE-logging of deallocs and copies is of low value; otherwise this can be switched
207 * to `flow::util::Blob`. There's also a to-do somewhere to switch these to `array` (fixed-length buffer)
208 * which would be nice for some extra perf; however note lend_object() and borrow_object() are forwarded-to
209 * via ipc::session layer (optional to use but recommended), so such a change would need to be potentially
210 * coordinated with the rest of Flow-IPC including other SHM-providers (starting with SHM-classic in
211 * ipc::shm::classic).
212 */
213 using Blob = flow::util::Blob_sans_log_context;
214
215 /**
216 * Convenience alias for a `Stateless_allocator` suitable when *borrowing* STL-compliant containers et al through
217 * a SHM-jemalloc Shm_session. So if you're using owner-side, e.g.,
218 * `auto p_there = arena->construct<vector<T, Ipc_arena::Allocator<T>>>()`,
219 * then borrower-side to obtain a (reading-capable) handle to same you would:
220 * `auto p_here = session->borrow_object<vector<T, Shm_session::Borrower_arena_allocator<T>>>()`.
221 *
222 * @note If you're using general ipc::session::Session et al to establish a SHM-jemalloc-enabled setup,
223 * as opposed to using SHM-jemalloc in standalone fashion, then it is probably best
224 * to use `Session::Borrower_allocator` which would also work with other SHM-providers like
225 * SHM-classic.
226 *
227 * @tparam T
228 * Pointed-to type for the allocator. See standard C++ `Allocator` concept.
229 */
230 template<typename T>
234
235 /// Alias for a stats/info bundle type.
237
238 /// Alias for a stats type.
240
241 /// Alias for a stats type.
243
244 /**
245 * Alias for a (per-arena) stats-list type, as returned by borrower_pool_stats_process_wide().
246 *
247 * Spoiler alert: It is `vector<Own<Borrower_pool_stats>>`, where `Own` is a single-owner-pointer handle.
248 */
250
251 /// Alias for the per-pool info type, as returned by borrowed_shm_pool_live_info().
253
254 /// Constructors/destructor.
255 ~Shm_session();
256
257 // Methods.
258
259 /**
260 * Creates an instance of this class; the resulting Shm_arena object is immediately ready to receive incoming
261 * opposing lend_arena() calls, and you can call lend_arena(), lend_object(), and borrow_object().
262 *
263 * See class doc header for motivation for using the ref-counting handle paradigm.
264 *
265 * @warning `*logger` -- unless `logger` is null -- must exist at least past (1) all
266 * `borrow_object()`-returned pointers' groups and (2) of course the returned pointer group.
267 *
268 * @warning It is important that `shm_channel.remote_peer_process_credentials()` is properly set
269 * (we need the opposing PID for internal purposes). If the `Native_socket_stream` was set-up
270 * from a socket handle transmitted via IPC (and originally `connect_pair()`ed or equivalent),
271 * then it may be necessary for the caller to perform an extra step.
272 * See transport::Native_socket_stream::remote_peer_process_credentials() doc header for this info.
273 *
274 * ### About `shm_channel_error_handler` (and `shm_channel`) and error reporting ###
275 * Firstly:
276 *
277 * @see class doc header discussion of error handling -- or more specifically, the whys and hows of
278 * detecting that the IPC connection to the other side has gone down (usually gracefully) on account of
279 * the other side ending it (as opposed to the end user here deciding to end it).
280 * `shm_channel_error_handler` is a part of that picture but, as you'll note, not *necessarily* a
281 * mandatory one to use.
282 *
283 * `shm_channel` is a low-level (unstructured) channel -- transport::Channel -- of the specific type #Shm_channel,
284 * such that a similar peer object is passed to the opposing Shm_session to which we're connecting,
285 * typically (though not mandatorily; e.g. for testing, debugging) across a process boundary.
286 * It is a `sync_io`-pattern variant (formally with `transport::Channel::S_IS_SYNC_IO_OBJ == true`).
287 * (Arguably in plainer English: it's essentially a synchronously-used core, not dissimilar to
288 * a Unix domain socket FD (with Flow-IPC's internal protocol and API niceties of course).) It must be
289 * a freshly established channel: no calls to `Blob_{sender|receiver}`-concept APIs. `*this` shall subsume
290 * it via move semantics and own it. It'll be used internally for important communications such as pool-
291 * and arena-lending notifications and acks, but to a `*this` user it is black box stuff. Hold that thought.
292 *
293 * With one important exception (which we tackle just below) a `*this` cannot fail. Things like Linux `mmap()`
294 * failing or "we cannot open a named SHM object that the opposing Ipc_arena says it created a split second ago"
295 * are not reasonably recoverable; we log and abort or otherwise catastrophically fail. Don't worry about these.
296 *
297 * @note A Shm_session, by the arena-lending design, never allocates in the user's SHM arena(s);
298 * #Arena (`Ipc_arena`) does that. Shm_session exists entirely to enable lend_object() (prep
299 * an `Arena::construct<T>()`ed `T` to be borrowed by opposing side) and borrow_object() (accept that,
300 * yielding a handle-to-`T` on this side, so that it is accessible + participates in GC together with
301 * the opposing `construct()`ed handle and any other-process-similarly-borrowed handles-to-`T`).
302 * The point: at least #Arena can fail due to failure to allocate (out of RAM according to special SHM
303 * limits configured in OS: not atypical). Shm_session doesn't do that, so it can't. So:
304 *
305 * The exception is simply this: the session (a/k/a conversation with the other arena-lending-and-borrowing
306 * thing like us) may have ended, due to the other side deciding to do so (or crash or whatever). This
307 * is called "channel is hosed." `shm_channel` being hosed has the following effect:
308 *
309 * `*this` detects it, typically, as soon as it occurs. It is also technically possible that it first
310 * detects it when trying to *send* an internal message. Regardless: it is detected *at most once*
311 * and therefore is then reported to you immediately and *at most once*. To wit: `*this` shall
312 * execute *from thread W*: `shm_channel_error_handler(E)` (`E` being the reason for the channel-hosing).
313 * This mechanism is identical to async error reporting via the various async-I/O-pattern IPC primitives
314 * in ipc::transport (e.g.: transport::Blob_receiver::async_receive_blob()). It is just used for this one
315 * limited thing.
316 * - If you *do* need to use `*this`'s detecting the channel-hosing as *the* method of detecting
317 * other-side-caused ("opposing trigger") session-hosing, then in `shm_channel_error_handler()` --
318 * which runs from thread W -- we recommend you signal one of your user threads to end all use
319 * of `Shm_session`-borrowed objects, nullifying them; and destroy Shm_session. E.g., if you use
320 * boost.asio or `flow::async`, then it'd be typical to `post()` those steps onto your main thread.
321 * - Regardless: do not block inside `shm_channel_error_handler()`. Thread W also replies to the opposing
322 * Shm_session's internal requests; the opposing side waits for those replies, sometimes while inside an
323 * allocation in its arena; so blocking thread W can stall even the other process.
324 * - If you *do not* need to do that -- ipc::session is wrangling create() and `*this` for you, or
325 * you detect session-hosing via the hosing of some other channel or socket -- then in
326 * `shm_channel_error_handler` you could just log; or do nothing.
327 *
328 * @warning Do not destroy `*this` (e.g., by dropping the last `shared_ptr` to it) from within
329 * `shm_channel_error_handler()`: undefined behavior (likely `std::terminate()` due to thread W trying to
330 * self-join).
331 *
332 * Nevertheless, after this event (after channel's hosing is detected) but before Shm_session destruction
333 * (which you would allow, by dropping all borrowed handles and the `create()`-returned `Shm_session`-handle),
334 * APIs shall act as follows.
335 * - lend_arena() will return `false`. (Object-lending wouldn't work properly anyway so why lie to you?)
336 * - lend_object() will return an empty blob (so there's nothing for the opposing to borrow_object()).
337 * (We could return a functional/`borrow_object()`able blob here; but channel-hosed means there's a
338 * high chance the opposing (borrowing) process will have *no* information about the latest SHM-pools
339 * from our `lend_arena()`d `Ipc_arena`s, so passing a thing into borrow_object() is an invitation for
340 * crashing and other chaos. Plus in any case: if the session is down, then it's likely you wouldn't be
341 * able to transmit the blob anyway let alone then `borrow_object()` it.
342 * - borrow_object() (on `*this`) however will work normally. If you've managed to receive the blob
343 * to pass to that method, then both general IPC and opposing lend_arena() and lend_object() worked fine
344 * for in-SHM objects including that one; there's no reason to create more failure modes by having this
345 * artifically fail.
346 *
347 * Because Shm_session is not itself a communication channel, we're belaboring these points. That said this is
348 * no different from other async-I/O-pattern objects in Flow-IPC. Simply:
349 * - Be ready for lend_arena() and lend_object() to fail, indicating: this Shm_session is already hosed,
350 * and shm_channel_error_handler() shall fire imminently or already has. Typically one would stop
351 * normal processing but otherwise no-op. And orthogonally:
352 * - Handle `shm_channel_error_handler(E)` -- or your other method of session-hosing detection -- swiftly.
353 * As noted before: nullify all `this->borrow_object()`-returned handles; and destroy `*this` by dropping
354 * any handles from create().
355 * - You could also preempt the preceding bullet point *if* you happen to detect failed lend_arena() or
356 * lend_object() first. If you don't care about logging/reporting the `Error_code` associated with
357 * the channel-hosing, then you could end the Shm_session immediately. However you should still be ready
358 * to handle `shm_channel_error_handler(E)`, if it happens first. After all lend_object() and
359 * definitely lend_arena() are not done continuously, and it is proper to react to session-hosing ASAP.
360 *
361 * ### Details ##
362 * The following information is for context only, if you want to understand more precisely how/why we detect
363 * and report channel-hosing. It should not affect how you handle all this, and it is somewhat white-boxy info.
364 * Nevertheless: Here are the paths as of this writing to triggering `shm_channel_error_handler()` (and thus
365 * setting the internal flag that'll cause lend_arena() and lend_object() to fail subsequently as well).
366 * - lend_arena() itself tries to send internal `LendArena` message over `shm_channel`; the send fails
367 * synchronously. Thread: end user thread.
368 * - Result: post `shm_channel_error_handler(E)` onto thread W to execute ASAP. lend_arena() returns false.
369 * - `Arena::allocate()`, for an #Arena that has been successfully `lend_arena()`ed earlier, is told
370 * synchronously (by memory manager jemalloc) that a new SHM-pool is required, so it created it and informed
371 * every `Shm_session` (including `*this`) through which it has been let that this occurred, and that it
372 * must inform the opposing process(es), receive ack(s), and only then let `Arena::allocate()` return.
373 * However the send of the info fails synchronously. Thread: end user thread.
374 * - Result: post `shm_channel_error_handler(E)` onto thread W to execute ASAP. Arena `allocate()` proceeds
375 * normally after that. (Nota bene: Shm_session failure does not cause Ipc_arena allocation failure.)
376 * - The internal channel itself detects hosing while awaiting (like always) in-messages for internal use.
377 * Thread: that channel's own background thread.
378 * - Result: post `shm_channel_error_handler(E)` onto thread W to execute ASAP.
379 * - This includes the opposing side becoming stuck (not dead): Each side will issue, inside create(),
380 * Blob_sender::auto_ping() and Blob_receiver::idle_timer_run() on `shm_channel`. If it's unresponsive
381 * at any point, for a few seconds, you'll see E = transport::error::Code::S_RECEIVER_IDLE_TIMEOUT.
382 * - In thread W, we receive a message (`LendArena`, `LendPool`) and reply with ack response.
383 * However the send of the ack fails synchronously. Thread: W.
384 * - Result: execute `shm_channel_error_handler(E)` (already in thread W).
385 *
386 * @todo It'd be nice for Shm_session::create() to become a template with parameterized functor type
387 * instead of `flow::async::Task_asio_err` for `shm_channel_error_handler`. There is zero functional
388 * difference, as internally it is stored as a `Task_asio_err` (which a `Function<>`) anyway; but
389 * for consistency and maintainability it'd be nice.
390 *
391 * @param logger
392 * Used for logging purposes.
393 * @param shm_channel
394 * The channel used for transmitting internal messages. See above for requirements (which are simple).
395 * Subsumed/owned by the returned Shm_session.
396 * @param session_token_non_nil
397 * A session token to use internally for `shm_channel` communications; must be non-nil (else undefined
398 * behavior/assert may trip) and equal to what is given to the opposing `Shm_session`.
399 * If you have an `ipc::session::Session` (or equivalent) available: use its
400 * ipc::session::Session::session_token(). Otherwise use a value generated some other way.
401 * To forego the safety value offered here, you can manually generate a random one
402 * (`boost::uuids::random_generator()()`) offline and hard-code the resulting value on both sides.
403 * @param shm_channel_error_handler
404 * See above.
405 * @return An instance of this class.
406 */
407 static std::shared_ptr<Shm_session> create(flow::log::Logger* logger, Shm_channel&& shm_channel,
408 const transport::struc::Session_token& session_token_non_nil,
409 flow::async::Task_asio_err&& shm_channel_error_handler);
410
411 /**
412 * Registers an arena for lending to the borrower process (usually a separate process) on the other end of the
413 * session; it is required to do so before `this->lend_object<T>(p)` for any `p` that was
414 * `arena->construct<T>()`ed.
415 *
416 * @param arena
417 * The arena to be lent. It must not have been lent already via `*this`; if it has, this is a no-op
418 * that returns `false` (and logs a WARNING).
419 * @return Whether registration was successful. Failure indicates (other than in the aforementioned
420 * already-lent case) that either this lend_arena() itself failed
421 * on account of the hosing of `shm_channel`, or `shm_channel`'s hosing had been
422 * detected earlier. In any case, if so, `shm_channel_error_handler(E)` will fire or has fired
423 * in a background thread, and `*this` is hosed. See create() doc header.
424 */
425 bool lend_arena(const std::shared_ptr<Arena>& arena);
426
427 /**
428 * Registers an in-SHM object for lending to the borrower on the other end of the session; and returns a small blob
429 * to pass to borrow_object() in the opposing Shm_session to obtain access to the same in-SHM object.
430 * borrow_object() returns a handle much like `object` -- and participating in the same GC (garbage collection)
431 * of `*object` (thus making it effectively a cross-process `shared_ptr` group).
432 *
433 * An object that is `lend_object()`ed but not yet (or ever) subsequently `borrow_object()`ed against the
434 * opposing Shm_session is extant: it will not be destroyed until either (1) `*this` is destroyed or (2) the
435 * `borrow_object()` et al does occur. So if you fail to ever `borrow_object()` the returned blob, it is a leak
436 * until the lending `Shm_session` is destroyed, at the earliest. In more detail:
437 * - Think of it as a cross-process ref-count (*use-count*) of processes (owner, lenders). Ipc_arena::construct()
438 * sets it to 1. Local lend_object() increments it by 1. `construct()`ed `shared_ptr` group reaches local
439 * ref-count zero (owner-side disposer runs) => decrement by 1. Opposing-process `borrow_object()`ed
440 * `shared_ptr` group reaches local ref-count zero (borrower-side disposer runs) => decrement by 1. Note
441 * that opposing-process borrow_object() itself does not affect this use-count; this lend_object() already did
442 * so.
443 * - Until one does opposing-process borrow_object(), object is therefore (under continuing normal operation,
444 * nothing shutting down) safe from in-SHM garbage-collection (freeing), as use-count remains at least 1
445 * due to this lend_object(). By the same token, though, if one *never* calls it, then it remains "safe" from
446 * freeing for the same reason...
447 * - ...except that the destruction of the Ipc_arena itself will in fact free all owned memory including this
448 * object, whether or not its use-count had reached zero. However, Ipc_arena itself cannot be
449 * destroyed (even if you drop your `shared_ptr<Ipc_arena>` handle from Ipc_arena::create() and all
450 * `construct()`ed handles, including possibly `object` itself), until all
451 * `Shm_session`s through which it was `lend_arena()`d have been destroyed. That includes `*this`; so `*this`
452 * Shm_session going away is a necessary (not sufficient though) condition for `object` getting GCed --
453 * in that scenario.
454 *
455 * @note The serialization returned includes information on how to deserialize the object and not the contents
456 * of the object itself. It is small.
457 *
458 * @note It is up to the caller/user to transmit the returned blob via any IPC technique whatsoever. (Flow-IPC
459 * does provide a bunch of them; and ipc::transport::structured::Channel does it for you in a couple of
460 * senses that make sense for its mission.) `shm_channel` -- passed to `*this` via create() for "internal
461 * use" -- is *not* to be used by anyone for this purpose.
462 *
463 * @warning Be ready for this method returning empty blob. See below regarding return value.
464 *
465 * ### Thread safety ###
466 * Safe to call anytime. See class doc header.
467 *
468 * @tparam T
469 * The object type being lent. As of this writing it actually does not need to match the `T` used
470 * for Ipc_arena::construct() to obtain `object`; e.g., `T = void` might be useful in some cases.
471 * (borrow_object() is a different story. See its doc header.)
472 * @param object
473 * The handle to object to be lent; it *must* have come from Ipc_arena::construct().
474 * If not the method throws a `flow::error::Runtime_error` (itself an `std::runtime_error`).
475 * Moreover that Ipc_arena must have been successfully `lend_arena()`ed via `*this`; otherwise behavior
476 * is undefined.
477 * @return Non-empty blob on success; empty on failure. Failure indicates `shm_channel`'s hosing had been
478 * detected earlier. If so `shm_channel_error_handler(E)` has fired
479 * in a background thread, and `*this` is hosed. See create() doc header.
480 */
481 template<typename T>
482 Blob lend_object(const Handle<T>& object);
483
484 /**
485 * Yields a handle to the cross-process-GCed in-SHM object that was earlier `S->lend_object()`ed based on the
486 * small blob returned by the latter and passed to the present method. See lend_object() (and
487 * possibly Ipc_arena::construct() before that) first.
488 *
489 * @tparam T
490 * The object type that is being borrowed. While for plain old data this is straightforward -- typically
491 * the same `Tc` as given to `arena->construct<Tc>()` -- things change if that `Tc` involves at
492 * any level STL-compliant structure(s). A full explanation is impractical here; please see other
493 * docs including the guided manual. Here we'll just note the main points: 1: Wherever `Tc` used
494 * a `Stateless_allocator<R, Ipc_arena>`, `T` should use `Shm_session::Borrower_arena_allocator<R>`.
495 * 2: There is no allocation possible as a borrower. As of this writing the SHM mapped borrower-side
496 * is simply read-only; but even if we hadn't made it so via kernel, allocation is still only doable
497 * by the owner, per arena-lending-SHM-provider design. In any case you shall never perform
498 * anything -- like `vector::resize()` -- on the borrower side here, as the aforementioned
499 * #Borrower_arena_allocator simply cannot allocate; it only provides the (quite essential)
500 * `Pointer` type.
501 * @param serialized_object
502 * Thing returned by `opposing_session->lend_object()`.
503 * @return If successful, handle to cross-process-garbage-collectable object in SHM. If unsuccessful: null.
504 * The latter can only occur if `serialized_object` is of the wrong length and therefore clearly
505 * not coming from `lend_object()`. However, any other problem such as wrong data of the proper size =>
506 * undefined behavior. See create() doc header.
507 *
508 * ### Thread safety ###
509 * Safe to call anytime. See class doc header.
510 */
511 template<typename T>
512 Handle<T> borrow_object(const Blob& serialized_object) const;
513
514 /**
515 * Fills-out the stats/info contents of the given stat::Shm_session_info_dump: a printable bundling of stats/info
516 * relevant to (but not all necessarily owned by) `*this` Ipc_arena at this point in time. To summarize the
517 * resulting `*target_info_dump`... (see Ipc_arena::info_dump() doc header -- same deal).
518 *
519 * ### Rationale ###
520 * (See Ipc_arena::info_dump() doc header -- same deal.)
521 *
522 * @param target_info_dump
523 * The non-`->m_fmt` parts shall be assigned. `->m_fmt.m_verbose` is also consulted (so set it, if desired,
524 * before calling); see util::Info_dump_format::m_verbose.
525 */
526 void info_dump(Info_dump* target_info_dump);
527
528 /**
529 * Returns the process-wide borrower-side stat-set Borrower_pool_lookup_global_stats,
530 * aggregated across all `Shm_session`s in this process. See that type's doc header for the meaning of its
531 * stat-members; and see borrower_pool_stats_process_wide() for the notes on informational use, the
532 * live-`atomic<>` nature of the returned reference, consumption, thread safety, and resetting -- all of which
533 * apply equally here.
534 *
535 * @return See above.
536 */
538
539 /**
540 * Returns the process-wide borrower-side stat-set Borrower_pool_stats -- the
541 * running totals across all `Shm_session`s in this process -- and (optionally) output a copy of similar information
542 * broken down by arena, each identified and in ascending order by stat::Uniq_arena_id
543 * Borrower_pool_stats::m_uniq_arena_id. See stat::Borrower_pool_stats doc header for background on interpreting
544 * the data; some related notes are below.
545 *
546 * @note The returned-by-ref Borrower_pool_stats has its Borrower_pool_stats::m_uniq_arena_id all-zeroes, as it
547 * is aggregated info, not per-arena.
548 * @note (Assume `per_arena_stats_sz_limit_or_0 = 0`, and no eviction of historical per-arena data has occurred.)
549 * The returned totals could almost be computed by summing the per-arena `Borrower_pool_stats` (via
550 * `flow::util::stat::stats_sum()`) -- except the `HI_WMARK`s, which are accurate only on the returned
551 * totals (continuously updated there); that historical info is lost when split by arena across time.
552 * @note In each stat-set in `*per_arena_stats` Borrower_pool_stats::m_n_borrowed_arenas cannot exceed 1.
553 *
554 * ### Concurrency semantics ###
555 * The returned (totals) reference is to live `atomic<>`s which can change at any moment concurrently;
556 * hence even values grabbed in immediate succession can be slightly mutually incoherent. Consume via
557 * `flow::util::stat::load()` / `stats_assign()` / `print()` (et al); see `flow::util::stat` doc header.
558 *
559 * Each element in out-arg `*per_arena_stats`, by contrast, is a snapshot copy as of this call; it will
560 * not change on return.
561 *
562 * ### Limits on result size `per_arena_stats->size()` ###
563 * There are two limits, one user-controlled and one (usually much larger) not.
564 *
565 * The limit you can control is the in-arg `per_arena_stats_sz_limit_or_0`, if set to non-zero. Supposing
566 * we internally have history of N, greater than `per_arena_stats_sz_limit_or_0`, per-arena stat-sets:
567 * We shall output only `per_arena_stats_sz_limit_or_0` of them. This is useful for practical purposes
568 * when, say, logging all the results; it might be not too useful to print hundreds of stat-sets.
569 *
570 * @note `*n_arenas_omitted` is set to the number of stat-sets thus dropped past `per_arena_stats_sz_limit_or_0`.
571 *
572 * There is also an internal limit, which is a constant; as of this writing 1,000. To avoid unbounded growth
573 * of overall per-arena stats here, we irreversibly forget an old stat-set, if we are about to go past this limit.
574 * Therefore (even with `per_arena_stats_sz_limit_or_0 = 0` a/k/a infinity) `per_arena_stats->size()` shall
575 * not exceed this large internal limit.
576 *
577 * If arena-stat-sets are omitted due to the limit in-arg, and/or arena-stat-sets are forgotten due to the
578 * internal growth limit, the *oldest* (according to the same age criterion) arenas are omitted/forgotten.
579 * This *age* is determined by the following criterion: time passed since the last time a stat-modifying event
580 * has occurred for that arena. Namely:
581 * - the arena is borrowed through a session (opposing Shm_session::lend_arena());
582 * - the arena is unborrowed through a session (as of this writing: local Shm_session borrowing it is destroyed);
583 * - a pool through the arena was unborrowed (ditto);
584 * - a pool through the arena was borrowed (opposing process allocated in arena, and that allocation
585 * necessitated the creation -- and therefore automatic lending -- of a new SHM-pool in the arena).
586 *
587 * ### Thread safety ###
588 * Safe to call concurrently with anything on any `Shm_session` (or none).
589 *
590 * @param per_arena_stats
591 * If null, ignored; else cleared, then loaded with per-arena stat copies (the `unique_ptr` wrapper is
592 * for your convenience -- cheap moves; and the stat-sets are not natively copyable due to their `atomic`
593 * members). Sorted ascending by Uniq_arena_id::m_id1 (owner-PID), then `m_id2` (per-process ordinal).
594 * @param per_arena_stats_sz_limit_or_0
595 * (Ignored if `per_arena_stats` is null.) If not zero, result `per_arena_stats->size()` is limited to
596 * at most this many elements, with most-recently updated stat-sets given precedence.
597 * @param n_arenas_omitted
598 * (Ignored if `per_arena_stats` is null; or if `n_arenas_omitted` is itself null.)
599 * `*n_arenas_omitted` shall be set to the number of stat-sets
600 * internally available but omitted from `*per_arena_stats` due to `per_arena_stats_sz_limit_or_0`.
601 * (If `per_arena_stats_sz_limit_or_0 == 0`, `*n_arenas_omitted` shall be zero.)
602 * @return See above (the across-all-`Shm_session`s totals).
603 */
604 static const Borrower_pool_stats&
606 size_t per_arena_stats_sz_limit_or_0 = 0, size_t* n_arenas_omitted = nullptr);
607
608 /**
609 * Resets borrower_pool_lookup_global_stats() and borrower_pool_stats_process_wide(). The formal meaning of a reset
610 * is discussed in `flow::util::stat` doc header.
611 *
612 * @note These are process-global stat-sets; hence this resets them across the entire process.
613 * (Cf. the non-`static` borrower_pool_stats_reset().)
614 * To be clear, though, this does not touch anything but the data for the aforementioned
615 * two `static ..._globals_stats()`. Anything accessed per-`Shm_session` (so non-`static`
616 * borrower_pool_stats_reset()) is reset only via correspoding non-`static` `borrower_pool_stats_reset()`,
617 * for each Shm_session of interest.
618 */
619 static void global_stats_reset();
620
621 /**
622 * Returns the borrower-side stat-set Borrower_pool_stats limited to `*this`
623 * `Shm_session` -- i.e., the same kind of data as borrower_pool_stats_process_wide() but covering only the
624 * borrowing activity conducted through `*this`. See stat::Borrower_pool_stats doc header for the meaning of
625 * its stat-members (including how a per-`Shm_session` view subdivides versus the process-wide one).
626 *
627 * All notes for return-value of borrower_pool_stats_process_wide() apply to the returned reference
628 * (live `atomic<>`s; consumption; also thread safety in general).
629 *
630 * @return See above.
631 */
633
634 /**
635 * Resets borrower_pool_stats() -- the per-`*this`-`Shm_session` view only; does not affect the process-wide
636 * stats (borrower_pool_lookup_global_stats() / borrower_pool_stats_process_wide()). The formal meaning of a reset is
637 * discussed in `flow::util::stat` doc header.
638 */
640
641 /**
642 * For informational/stats-adjacent purposes, returns information on currently open (and memory-mapped)
643 * SHM-pools, as borrowed across all `Shm_session`s in this process / from all borrowed arenas, sorted by
644 * pool ID -- that is, in chronological order of creation, across owner processes. Each pool record includes
645 * the ID of the containing arena as uniquely identified machine-wide (between boots); thus one can tell the
646 * order in which the borrowed pools were created machine-wide -- the arena IDs are interleaved.
647 *
648 * Informational purposes only (logging/monitoring/etc.). The borrowed pool-set can change at any moment
649 * concurrently.
650 *
651 * ### Thread safety ###
652 * Safe to call concurrently with anything on any `Shm_session` (or none).
653 *
654 * @return See above.
655 */
656 static std::vector<Shm_pool_info> borrowed_shm_pool_live_info();
657
658 // Would be private, but as of this writing some items are used by white-boxy unit tests.
659#ifdef IPC_DOXYGEN_ONLY // Compiler ignores; Doxygen sees: document the below as the private it conceptually is.
660private:
661#else // Compiler sees; Doxygen ignores.
662protected:
663#endif
664 // Types.
665
666 /// Alias for a SHM pool.
667 using Shm_pool = ipc::shm::arena_lend::Shm_pool;
668 /// Short-hand for pool ID type.
670 /// Short-hand for pool offset type.
671 using pool_offset_t = Shm_pool::size_t;
672 /// Short-hand for pool-collection (a/k/a arena) ID type.
674 /// Short-hand for util::Shared_name; used in particular for SHM pool names at least.
676
677 /**
678 * Alias for a structured channel for our internal use.
679 *
680 * ### Rationale for alias target ###
681 * The basic choices are transport::struc::Channel_via_heap and `"transport::struc::shm::*::Channel"`.
682 * The latter is pretty crazy. That'd be, as of this writing, either
683 * transport::struc::shm::arena_lend::jemalloc::Channel
684 * (infinite compile-time recursion!) or transport::struc::shm::classic::Channel (which is somewhat more
685 * conceivable but a huge pain in the butt for our chief user, namely ipc::session::shm::Session_mv and company --
686 * they'd have to set up SHM-classic just to have a chance to set up SHM-jemalloc). Anyway clearly we must use
687 * the non-zero-copy transport::struc::Channel_via_heap. Our messages are small, so it is a good use of it.
688 */
690
691 /**
692 * A serialized object handle: a compact representation of an in-SHM object's location and lending metadata,
693 * transmitted as a blob via IPC. Produced by lend_object(), consumed by borrow_object(), usually across
694 * process boundary.
695 *
696 * Why borrow_object() needs these particular data ultimately:
697 * - #m_pool_id, #m_pool_offset: Needed simply to obtain the vaddr of the object in this (borrowing) process;
698 * the vaddr in the opposing (owner) process is an unrelated number.
699 * `Borrower_shm_pool_collection_repository::to_address()` (a `static`) makes this translation.
700 * (This is not the only place such a lookup is performed; borrower-side Shm_pool_offset_ptr does the same.
701 * This comes up typically in SHM-stored STL-compliant containers which internally store these pointers
702 * due to the allocator used on owner side. However, borrower-side, that is all subsequent to this
703 * initial outer-object setup in borrow_object(). Then the STL magic, for applicable `T`s, can begin.)
704 * - #m_lend_tracker_pool_id, #m_use_ct_idx: In the fast path the disposer attached to `borrow_object()`-returned
705 * `shared_ptr` shall thread-locally (1) look up the aux SHM-pool `Lend_tracker_pool` in a map and (2) decrement
706 * the atomic use-count for this particular object, at that index.
707 * - #m_collection_id: In doing so it may need to open that pool by name (slow-path) in which case
708 * this collection ID is needed: the pool name base is saved on a per-arena (collection) basis,
709 * when we receive the opposing `Shm_session`'s message during its lend_arena() call.
710 * - The owner ID (`owner_id_t` type) is also necessary here; but that identifies the entire owner
711 * (opposing) process; that is Shm_session::m_remote_process_id.
712 */
714 {
715 // Data.
716
717 /// The collection ID of the arena in which this object was created. Reminder: it is unique only given a process.
719 /// Pool identifier. Reminder: this is globally unique until reboot.
721 /// The offset within the pool where this object resides.
723 /// Pool ID of the Lend_tracker_pool holding the use-count slot for this object. Same namespace as #m_pool_id.
725 /// Index of the use-count slot within the above Lend_tracker_pool.
727 }; // struct Shm_object_handle
728
729 /**
730 * The `Owner_shm_pool_listener` impl object that we register within a particular `Ipc_arena`
731 * (Ipc_arena::add_shm_pool_listener()) when it is lent via lend_arena(). As a result we receive these
732 * basic events:
733 * - a SHM-pool was added (initially in lend_arena(); or during allocation on-demand from the memory manager
734 * jemalloc) [so we need to inform the opposing -- borrower -- side, so it is able to access the stuff
735 * in SHM in that pool];
736 * - a SHM-pool was removed (likely during deallocation as requested by ditto).
737 *
738 * We unregister it from its `Ipc_arena` in Shm_session destructor. If there were an un-`lend_arena()`
739 * (not as of this writing but could happen), then we'd do it there too. (Destructor = un-lending all
740 * arenas being lent.)
741 */
744 {
745 public:
746 // Constructors/destructor.
747
748 /**
749 * Constructor.
750 *
751 * @param owner
752 * Containing Shm_session.
753 * @param collection_id
754 * The arena (collection) ID that the notifications are emitting from; the owner is our process
755 * (owned ID = our PID).
756 */
758
759 // Methods.
760
761 /**
762 * Implements super-class API (notification called upon initial registration in lend_arena() in a synchronous
763 * manner once). Essentially equivalent to notify_created_shm_pool() for each element in `shm_pool`.
764 * @param shm_pools
765 * The current set of active SHM pools; may be empty.
766 */
767 void notify_initial_shm_pools(const std::set<std::shared_ptr<Shm_pool>>& shm_pools) override;
768
769 /**
770 * Implements super-class API (see inner class doc header for recap).
771 * @param shm_pool
772 * See above.
773 */
774 void notify_created_shm_pool(const std::shared_ptr<Shm_pool>& shm_pool) override;
775
776 /**
777 * Implements super-class API.
778 * @param shm_pool
779 * See above.
780 */
781 void notify_removed_shm_pool(const std::shared_ptr<Shm_pool>& shm_pool) override;
782
783 private:
784 // Data.
785
786 /// See ctor.
788 /// See ctor.
790 }; // class Shm_pool_listener_impl
791
792 // Constructors.
793
794 /**
795 * Constructor (private due to `shared_ptr` factory pattern).
796 *
797 * @param logger
798 * See create().
799 * @param shm_channel
800 * See create().
801 * @param session_token_non_nil
802 * See create().
803 * @param shm_channel_error_handler
804 * See create().
805 */
806 Shm_session(flow::log::Logger* logger, Shm_channel&& shm_channel,
807 const transport::struc::Session_token& session_token_non_nil,
808 flow::async::Task_asio_err&& shm_channel_error_handler);
809
810 // Methods.
811
812 /**
813 * Returns the remote process ID.
814 * @return See above.
815 */
817
818 /**
819 * Equivalent to `lend_shm_pool(collection_id, P)` for each `P` in `shm_pools`. As of this writing
820 * occurs within lend_arena() via Shm_pool_listener_impl.
821 *
822 * @param collection_id
823 * The arena (collection) ID of our owner-side Ipc_arena owning these pools.
824 * @param shm_pools
825 * See above.
826 */
827 void lend_shm_pools(collection_id_t collection_id, const std::set<std::shared_ptr<Shm_pool>>& shm_pools);
828
829 /**
830 * Registers a SHM pool for lending and sends messages to the borrower about it;
831 * returns on acknowledgment (or failure to send) only. As of this writing triggered by either
832 * lend_shm_pools() or, arguably more significantly, Shm_pool_listener_impl::notify_created_shm_pool().
833 *
834 * On error: set_disconnected(); return `false`.
835 *
836 * Regarding the aforementioned ack:
837 * - It shall arrive immediately (think of it as a function call across potential process boundary).
838 * (If the opposing process is stuck, then #m_shm_channel idle timer -- see ctor -- bounds the wait.)
839 * - It is not safe for our caller, presumably Ipc_arena::allocate() (by way of jemalloc and then more
840 * owner-y arena-y Flow-IPC stuff), to proceed until it occurs. In fact the same procedure must repeat
841 * for each Shm_session (like `*this`) through which that Ipc_arena has been `lend_arena()`d. Otherwise
842 * those borrower process(es) may not be able to properly access in-SHM things in these pools (there would
843 * be a race between user sending-over `lend_borrow()`-returned blobs and our sending `LendPool` internal
844 * messages).
845 *
846 * @param collection_id
847 * The arena (collection) ID of our owner-side Ipc_arena owning these pools.
848 * @param shm_pool
849 * The SHM pool.
850 * @return Whether the operation was successful. It would fail if only if we cannot communicate the info
851 * to borrower.
852 */
853 bool lend_shm_pool(collection_id_t collection_id, const std::shared_ptr<Shm_pool>& shm_pool);
854
855 /**
856 * As Shm_pool_listener_impl::notify_created_shm_pool() (by Ipc_arena) => lend_shm_pool(),
857 * Shm_pool_listener_impl::notify_removed_shm_pool() => this method.
858 *
859 * Currently a no-op (logs only); borrower-side cleanup is deferred to ~Shm_session().
860 * See `@todo` in the implementation about that.
861 *
862 * @param collection_id
863 * See lend_shm_pool().
864 * @param shm_pool
865 * See lend_shm_pool().
866 */
867 void remove_lender_shm_pool(collection_id_t collection_id, const std::shared_ptr<Shm_pool>& shm_pool);
868
869 /**
870 * Helper that sends message over internal-use #m_shm_channel and returns on ack (or failure to send).
871 * Proceeds regardless of #m_connected (to gate based on it, do so before calling).
872 *
873 * On error: set_disconnected(); return `false`.
874 *
875 * @param message
876 * The message to send.
877 * @param operation
878 * The use case that the message is related to (for logging).
879 * @return `true` iff sent OK and got ack. See `ipc_shm_message.capnp` for the (straightforward) schema.
880 */
882
883 /**
884 * Helper that sends ack to having received a thing that the opposing side sent via its send_sync_request().
885 * Proceeds regardless of #m_connected (to gate based on it, do so before calling).
886 *
887 * On error: set_disconnected().
888 *
889 * @param original_message
890 * Message we are acking.
891 * @param operation
892 * See send_sync_request().
893 */
894 void send_response(const Shm_struc_channel::Msg_in* original_message, util::String_view operation);
895
896 /**
897 * Borrows an arena shared by the lender: in thread W a/k/a #m_serial_task_loop handle `LendArena` message.
898 *
899 * @param collection_id
900 * The identifier of the arena.
901 * @param pool_name_base
902 * The pool-name prefix for this arena's SHM pools.
903 */
904 void receive_arena(collection_id_t collection_id, Shared_name&& pool_name_base);
905
906 /**
907 * Helper that permanently sets #m_connected `true` => `false`, so that lend_arena(), lend_object(), and
908 * lend_shm_pool() short-circuit to no-op/failure subsequently. #m_connected shall be set to `false` exclusively via
909 * this method, as it's the way we trigger the callback in the promised manner.
910 *
911 * @note This shall be called at most once. That is natural, because #m_shm_channel by struc::Channel contract
912 * can report channel-hosing at most once. If that contract is broken (or we have a Shm_session bug in
913 * relaying channel-hosing to set_disconnected()) then assert may trip.
914 *
915 * It is thread-safe in that we may call it from any end user thread
916 * (lend_arena() from user, lend_shm_pool() from `Arena::allocate()`); or from thread W (#m_serial_task_loop)
917 * when handling incoming internal messages for opposing `lend_arena()` and so on; or from #m_shm_channel
918 * background thread (the channel-hosed handler).
919 *
920 * ### Discussion ###
921 * Blocking the future `lend_*()` is a best-effort thing. In create() doc header we explain that the on-error handler
922 * `shm_channel_error_handler()` is how we communicate the first and only session-hosing officially; and certain
923 * public API calls will intentionally stop working for the cited reasons, those reasons being *not* communicating
924 * the hosing but avoiding entropy and undefined behavior (implication being, what if the user continues to try
925 * using stuff they shouldn't pass that callback executing?). Subtlety: lend_shm_pool() is not a public API
926 * for the end user, though it is invoked from the #Arena allocation path (sometimes). It involves sending
927 * things via #m_shm_channel; and it stands to reason that once we know the channel is hosed, there's no point
928 * trying it; it is probably harmless but entropy-increasing.
929 *
930 * @param err_code
931 * Cause of channel-hosing.
932 */
933 void set_disconnected(const Error_code& err_code);
934
935private:
936 // Methods.
937
938 /**
939 * Borrows a SHM pool from the lender: in thread W a/k/a #m_serial_task_loop handle `LendPool` message.
940 *
941 * @param collection_id
942 * The identifier of the collection in which the pool resides.
943 * @param shm_pool_id
944 * The identifier of the SHM pool.
945 * @param pool_size
946 * The size of the SHM pool.
947 */
948 void receive_shm_pool(collection_id_t collection_id, pool_id_t shm_pool_id, pool_offset_t pool_size);
949
950 // Data.
951
952 /// The other end's process ID, cached from #m_shm_channel; used for registering borrowed items.
954
955 /**
956 * Whether the session is connected; `true` to start; when set to `false` #m_shm_channel_error_handler is invoked
957 * from thread W (#m_serial_task_loop); and this state is permanent.
958 *
959 * Note that the value may change within a method concurrently. Use set_disconnected(), exclusively, to
960 * modify this value; as it will properly trigger #m_shm_channel_error_handler as needed and never again.
961 *
962 * Also set_disconnected() will only set `m_connected = false` in thread W; that's a choice to reduce entropy:
963 * `m_serial_task_loop.stop()` (at shutdown) can thus cause `m_connected` to be immutable subsequently.
964 *
965 * ### Perf discussion ###
966 * The most frequent, by far, reason to access this is lend_shm_pool(). While any #Arena `allocate()` *can*
967 * trigger that, only a small % of them will do so. Therefore we can use the most restrictive ordering
968 * specifier (as-if `m_connected` is protected by a standard mutex) for simplicity with negligible perf impact.
969 */
970 std::atomic<bool> m_connected;
971
972 /**
973 * Per-collection set of borrowed pool IDs; used for cleanup in dtor.
974 *
975 * ### Thread safety ###
976 * It is only accessed in receive_arena() and receive_shm_pool(), both in thread W; and then in
977 * dtor -- after thread W is joined -- so no synchronization is required.
978 */
979 boost::unordered_map<collection_id_t, boost::unordered_set<pool_id_t>> m_borrower_pool_id_map;
980
981 /**
982 * Maps an arena to its SHM-pool listener; this is used for receiving changes in the SHM pools.
983 *
984 * ### Thread safety ###
985 * Accessed in lend_arena() (user thread) and dtor (after thread W is joined). No synchronization:
986 * lend_arena() is not thread-safe w/r/t concurrent calls (nor documented as such), and dtor runs
987 * after all user activity has ceased.
988 */
989 boost::unordered_map<std::shared_ptr<Arena>,
990 std::unique_ptr<Shm_pool_listener_impl>> m_shm_pool_listener_map;
991
992 /**
993 * Stats about borrower SHM-pool (and containing SHM-arena) borrowing (opening) and
994 * unborrowing (closing) *from the PoV of `this` Shm_session object*. For background on the latter
995 * emphasized point please see stat::Borrower_pool_stats doc header. In short, though, it means that:
996 * - borrowing/registering an arena (and borrowing/registering contained SHM-pool) is equal to
997 * first-registering that arena (and first-registering contained SHM-pool); and
998 * - unborrowing/deregistering an arena (and unborrowing/deregistering contained SHM-pool) is equal to
999 * last-deregistering that arena (and last-deregistering contained SHM-pool).
1000 *
1001 * That is, some of the counters are degenerate in that they will equal related counters.
1002 * There is also global counting of such things in Borrower_shm_pool_collection_repository (singleton), where
1003 * that is not the case: e.g., a pool can be borrowed twice and then unborrowed twice -- that would count
1004 * 2 registerings, 2 unregisterings, 1 first-registering (= open pool + memory-map it), 1 last-deregistering
1005 * (= memory-unmap + close it). That is because the same pool (also arena) can be borrowed twice, once
1006 * through one Shm_session, again through another; but in reality it is only opened/mapped the first time.
1007 *
1008 * ### Design/performance ###
1009 * The most frequent events are borrow-pool and unborrow-pool and are fairly rare. They can be stat-consumed
1010 * by user (also rare). Concurrency is possible at least because consumption can occur concurrently with
1011 * the borrow-pool stat-updates, so we use `atomic<>`s, but due to the rarity of updates
1012 * no sharding is necessary. This follows the concurrent, non-sharded design (as introduced in
1013 * `flow::util::stat` doc header).
1014 *
1015 * No mutex is necessary.
1016 */
1018
1019 /**
1020 * The channel used for transmitting SHM-pool messages.
1021 * Note that by transport::struc::Channel contract it *is safe* to execute `m_shm_channel.X()`
1022 * and `m_shm_channel.Y()` concurrently for all `X` and `Y` (whether they're the same method or not).
1023 */
1024 std::optional<Shm_struc_channel> m_shm_channel;
1025
1026 /**
1027 * Callback executed when a channel error code is emitted when using #m_shm_channel.
1028 * Emptied after calling it which must only occur once (for cleanliness + lower memory use).
1029 */
1030 flow::async::Task_asio_err m_shm_channel_error_handler;
1031
1032 /**
1033 * Task engine to process tasks in a serial manner (a/k/a thread W). The only use case currently is incoming
1034 * SHM-channel messages from the lender. The messages must be serialized, as out-of-ordering may cause issues.
1035 * In particular:
1036 * -# An object being borrowed is processed prior to the SHM pool containing it.
1037 * -# A SHM pool being borrowed is processed prior to a SHM pool being removed with the same address.
1038 * -# A SHM pool being borrowed is registered prior to the collection containing it.
1039 */
1040 flow::async::Single_thread_task_loop m_serial_task_loop;
1041}; // class Shm_session
1042
1043// Free functions: in *_fwd.hpp.
1044
1045// Template implementations.
1046
1047template<typename T>
1049{
1054 using flow::error::Runtime_error;
1055 using flow::util::default_init_at;
1056
1057 Thread_lcl_obj_db_admin<Arena>::this_thread_piggy_scan(); // Opportunistic!
1058
1059 if (!m_connected) // This best-effort gating is discussed in create() doc header.
1060 {
1061 FLOW_LOG_TRACE("Disconnected, so ignoring object lending attempt.");
1062 return {};
1063 }
1064 // else
1065
1066 /* Note: This is the one Obj_handle-impl-specific call around here: should Obj_handle ever
1067 * switch impls (e.g., to boost::shared_ptr), use that impl's get_deleter() equivalent. */
1068 auto const * const disposer = std::get_deleter<Owner_obj_disposer_and_mdt<Arena>>(object);
1069 if (!disposer)
1070 {
1071 /* @todo In the rest of Flow[-IPC] we might assert(false) here; or emit an error Flow-style (which in fact
1072 * throws Runtime_error, albeit with a nice Error_code, if one doesn't supply non-null Error_code* err_code).
1073 * We could also return an empty Blob here per our own doc header. Leaving it alone for now so as to not
1074 * rock the boat. Deciding this would be part of to-do(s) around SHM-jemalloc w/r/t straightening out
1075 * error reporting for consistency (internally and versus Flow-IPC in general) and robustness. For now, guidance:
1076 * 1, assert() would not be crazy; it's not that different from passing null when non-null is expected.
1077 * 2, on the other hand, if one is not used to the project they might pass some random shared_ptr<T> in here,
1078 * and a nice error might be a more civilized way of informing them. If so: probably this should just be
1079 * one of many Flow-IPC APIs with Flow error-emission semantics (take Error_code*, on error throw if null,
1080 * set out-arg otherwise; on success don't throw/set to falsy Error_code respectively). -ygoldfel */
1081 throw Runtime_error{"lend_object() was given handle (shared_ptr) without disposer of the expected type; "
1082 "did you get it from Ipc_arena::construct() as required?"};
1083 }
1084 // else
1085
1086 const auto object_ptr = object.get();
1087
1088 Blob blob{sizeof(Shm_object_handle)};
1089 const auto& shm_arena = *disposer->m_shm_arena;
1090 const auto collection_id = shm_arena.get_id();
1091 const auto use_ct_idx = disposer->m_use_ct_idx;
1092 const auto lend_tracker_pool_id = disposer->m_lend_tracker_pool_id;
1093
1094 const auto object_handle = reinterpret_cast<Shm_object_handle*>(blob.data());
1095 /* No-op in itself (does not zero members; in our case it'd be wasteful: lend_object() can be a hot path).
1096 * Informs compiler this is a genuine Shm_object_handle, so we can do the following assignments without fear
1097 * of breaking aliasing rules. */
1098 default_init_at(object_handle);
1099
1100 object_handle->m_collection_id = collection_id;
1101 object_handle->m_lend_tracker_pool_id = lend_tracker_pool_id;
1102 object_handle->m_use_ct_idx = use_ct_idx;
1103 Owner_shm_pool_repository::from_address(object_ptr, // <-- from that | compute these --v
1104 object_handle->m_pool_id, object_handle->m_pool_offset);
1105
1106 if (flow::util::this_thread_unique_token() == disposer->m_cting_thread_token)
1107 {
1108 /* shared_ptr being lent in the same thread that created the shared_ptr: we can report it
1109 * to the thread-local obj-DB *admin* directly. We know it exists in this thread, so might as well;
1110 * in any case admin can do anything client can do; so that's our policy; no need to duplicate the associated
1111 * data in the same thread unnecessarily. */
1112 Thread_lcl_obj_db_admin<Arena>
1113 ::this_thread_obj_db()->lending_obj(collection_id, use_ct_idx);
1114 }
1115 else
1116 {
1117 Thread_lcl_obj_db_client
1118 ::this_thread_obj_db()->lending_obj(shm_arena, lend_tracker_pool_id, use_ct_idx);
1119 }
1120
1121 return blob;
1122} // Shm_session::lend_object()
1123
1124template<typename T>
1126{
1129 using Borrower_shm_pool_collection_repository
1131 using std::memcpy;
1132
1133 Thread_lcl_obj_db_admin<Arena>::this_thread_piggy_scan(); // Opportunistic!
1134
1135 const size_t blob_size = blob.size();
1136 if (blob_size != sizeof(Shm_object_handle))
1137 {
1138 FLOW_LOG_WARNING("Blob size [" << blob_size << "] does not match expected "
1139 "size [" << sizeof(Shm_object_handle) << "]. This indicates a bug on someone's part; "
1140 "either the blob was somehow improperly formed owner-side, or the user has supplied something "
1141 "unequal to what was formed there. borrow_object() returning null as advertised.");
1142 return {};
1143 }
1144 // else
1145
1146 /* memcpy() it out of there: the source address may not be aligned. (In many APIs such things are assumed as a
1147 * matter of course, but as `blob` may be IPCed-over to us via any technique, we're being extra defensive.) */
1148 Shm_object_handle object_handle;
1149 memcpy(&object_handle, blob.const_data(), sizeof(object_handle));
1150
1151 const auto obj_addr
1152 = Borrower_shm_pool_collection_repository::to_address_safe(object_handle.m_pool_id,
1153 object_handle.m_pool_offset);
1154 if (!obj_addr)
1155 {
1156 /* We used to_address_safe() (rather than the marginally quicker and subsequently-used to resolve various
1157 * pointers such as inside STL-compliant containers (off borrowed handles like this one... but valid)
1158 * to_address()) as a safety measure against bogus/buggy `blob`s. This follows the philosophy in
1159 * classic::Pool_arena::borrow_object() to be defensive at this particular point, where we process an
1160 * ostensibly IPCed-over datum. That said: this is a best-effort thing: In particular m_pool_offset as of
1161 * this writing can point outside the pool (to_address_safe() will not detect it); never mind any check for
1162 * whether sizeof(T) would lie beyond the pool's bounds. classic's equivalent as of this writing can and
1163 * does perform those checks.
1164 *
1165 * Inside Borrower_shm_pool_collection_repository::to_address_safe() there are some notes on what it would take to
1166 * extend this -- but, again, it is a best-effort safety measure. */
1167
1168 FLOW_LOG_WARNING("Borrowed blob encodes pool ID [" << object_handle.m_pool_id << "], but this pool ID "
1169 "is no longer borrowed or has never been borrowed; treating the blob as invalid. "
1170 "borrow_object() returning null as advertised for invalid SHM-handle blobs.");
1171 return {};
1172 }
1173 // else
1174
1175 /* See doc header for Shm_object_handle for recap of which pieces of info below are used when/why.
1176 * See doc header for construct_with_borrower_obj_disposer() for a recap of the properties of the disposer
1177 * we are attacking to the shared_ptr returned here. */
1178 return construct_with_borrower_obj_disposer<T, Shm_session>
1179 (static_cast<T*>(obj_addr),
1180 object_handle.m_lend_tracker_pool_id, // Datum 1/1 used in the fast path.
1181 object_handle.m_use_ct_idx, // Datum 2/2 used in the fast path.
1182 m_remote_process_id, // A/k/a owner_id of type owner_id_t. Used in slow path.
1183 object_handle.m_collection_id, // Used in slow path.
1184 /* As of this writing this value (shared_ptr<const Shm_session> pointing to *this) isn't used per se;
1185 * but it prevents death of *this Shm_session while shared_ptr<T> handles that came from its
1186 * .borrow_object() still live. For better or worse those are the semantics we want to guarantee with the
1187 * factory+private ctor pattern. See Shm_session doc header. */
1188 shared_from_this());
1189
1190 /* Interesting fact; could be useful at some point: *this is actually only a little necessary for borrow_object()'s
1191 * impl. Suppose it were not necessary: borrow_object() could be static; that change could be propagated to
1192 * ipc::session::Session (though then it would become more asymmetrical to arena-sharing providers including
1193 * SHM-classic) and possibly somewhat beyond. At any rate in and of itself relationships would become simpler,
1194 * all else being equal (which it's not, but read on). As for how *this is required as of this writing:
1195 * - m_remote_process_id: Convenient, but could be obtained some other way such as directly from the
1196 * m_shm_channel API or encoded in object_handle.
1197 * - shared_from_this() a/k/a `this` itself: Passed to construct_with_borrower_obj_disposer(), but as noted
1198 * it is only stored to ++ its group ref-count, preventing *this death before all the borrowed SHM-handles
1199 * it generates (in this method) are dropped. That is actually an ontensibly important guarantee, and
1200 * other things document it. Though, as I (ygoldfel) understand it, it is apparently a stand-alone feature;
1201 * don't think anything specifically relies on it, outside of potentially user code to have been written
1202 * by now (removing it would be a breaking change). It's a nice guarantee; at least it is quasi-symmetrical
1203 * with owner-side Ipc_arena similarly not dying until all it-construct()ed SHM-handles are dropped.
1204 * Not suggesting removing this dependency; as written or implied just above it makes some sense. Good to keep
1205 * in mind though. */
1206} // Shm_session::borrow_object()
1207
1208} // namespace ipc::session::shm::arena_lend::jemalloc
The Owner_shm_pool_listener impl object that we register within a particular Ipc_arena (Ipc_arena::ad...
Shm_pool_listener_impl(Shm_session &owner, collection_id_t collection_id)
Constructor.
void notify_created_shm_pool(const std::shared_ptr< Shm_pool > &shm_pool) override
Implements super-class API (see inner class doc header for recap).
void notify_initial_shm_pools(const std::set< std::shared_ptr< Shm_pool > > &shm_pools) override
Implements super-class API (notification called upon initial registration in lend_arena() in a synchr...
void notify_removed_shm_pool(const std::shared_ptr< Shm_pool > &shm_pool) override
Implements super-class API.
In SHM-jemalloc, a context between this process and a single (typically other) opposing process w/r/t...
const util::process_id_t m_remote_process_id
The other end's process ID, cached from m_shm_channel; used for registering borrowed items.
boost::unordered_map< std::shared_ptr< Arena >, std::unique_ptr< Shm_pool_listener_impl > > m_shm_pool_listener_map
Maps an arena to its SHM-pool listener; this is used for receiving changes in the SHM pools.
void info_dump(Info_dump *target_info_dump)
Fills-out the stats/info contents of the given stat::Shm_session_info_dump: a printable bundling of s...
static std::vector< Shm_pool_info > borrowed_shm_pool_live_info()
For informational/stats-adjacent purposes, returns information on currently open (and memory-mapped) ...
Blob lend_object(const Handle< T > &object)
Registers an in-SHM object for lending to the borrower on the other end of the session; and returns a...
Shm_pool::size_t pool_offset_t
Short-hand for pool offset type.
Borrower_pool_stats m_borrower_pool_stats
Stats about borrower SHM-pool (and containing SHM-arena) borrowing (opening) and unborrowing (closing...
void lend_shm_pools(collection_id_t collection_id, const std::set< std::shared_ptr< Shm_pool > > &shm_pools)
Equivalent to lend_shm_pool(collection_id, P) for each P in shm_pools.
void send_response(const Shm_struc_channel::Msg_in *original_message, util::String_view operation)
Helper that sends ack to having received a thing that the opposing side sent via its send_sync_reques...
bool send_sync_request(Shm_struc_channel::Msg_out &message, util::String_view operation)
Helper that sends message over internal-use m_shm_channel and returns on ack (or failure to send).
Shm_pool::pool_id_t pool_id_t
Short-hand for pool ID type.
bool lend_arena(const std::shared_ptr< Arena > &arena)
Registers an arena for lending to the borrower process (usually a separate process) on the other end ...
void receive_arena(collection_id_t collection_id, Shared_name &&pool_name_base)
Borrows an arena shared by the lender: in thread W a/k/a m_serial_task_loop handle LendArena message.
Handle< T > borrow_object(const Blob &serialized_object) const
Yields a handle to the cross-process-GCed in-SHM object that was earlier S->lend_object()ed based on ...
static const Borrower_pool_lookup_global_stats & borrower_pool_lookup_global_stats()
Returns the process-wide borrower-side stat-set Borrower_pool_lookup_global_stats,...
void set_disconnected(const Error_code &err_code)
Helper that permanently sets m_connected true => false, so that lend_arena(), lend_object(),...
boost::unordered_map< collection_id_t, boost::unordered_set< pool_id_t > > m_borrower_pool_id_map
Per-collection set of borrowed pool IDs; used for cleanup in dtor.
void borrower_pool_stats_reset()
Resets borrower_pool_stats() – the per-*this-Shm_session view only; does not affect the process-wide ...
std::atomic< bool > m_connected
Whether the session is connected; true to start; when set to false m_shm_channel_error_handler is inv...
const Borrower_pool_stats & borrower_pool_stats() const
Returns the borrower-side stat-set Borrower_pool_stats limited to *this Shm_session – i....
bool lend_shm_pool(collection_id_t collection_id, const std::shared_ptr< Shm_pool > &shm_pool)
Registers a SHM pool for lending and sends messages to the borrower about it; returns on acknowledgme...
void remove_lender_shm_pool(collection_id_t collection_id, const std::shared_ptr< Shm_pool > &shm_pool)
As Shm_pool_listener_impl::notify_created_shm_pool() (by Ipc_arena) => lend_shm_pool(),...
static std::shared_ptr< Shm_session > create(flow::log::Logger *logger, Shm_channel &&shm_channel, const transport::struc::Session_token &session_token_non_nil, flow::async::Task_asio_err &&shm_channel_error_handler)
Creates an instance of this class; the resulting Shm_arena object is immediately ready to receive inc...
Definition: shm_session.cpp:46
ipc::session::shm::arena_lend::Borrower_pool_stats_list Borrower_pool_stats_list
Alias for a (per-arena) stats-list type, as returned by borrower_pool_stats_process_wide().
flow::async::Task_asio_err m_shm_channel_error_handler
Callback executed when a channel error code is emitted when using m_shm_channel.
static const Borrower_pool_stats & borrower_pool_stats_process_wide(Borrower_pool_stats_list *per_arena_stats=nullptr, size_t per_arena_stats_sz_limit_or_0=0, size_t *n_arenas_omitted=nullptr)
Returns the process-wide borrower-side stat-set Borrower_pool_stats – the running totals across all S...
void receive_shm_pool(collection_id_t collection_id, pool_id_t shm_pool_id, pool_offset_t pool_size)
Borrows a SHM pool from the lender: in thread W a/k/a m_serial_task_loop handle LendPool message.
ipc::shm::arena_lend::collection_id_t collection_id_t
Short-hand for pool-collection (a/k/a arena) ID type.
static void global_stats_reset()
Resets borrower_pool_lookup_global_stats() and borrower_pool_stats_process_wide().
ipc::shm::arena_lend::Shm_pool Shm_pool
Alias for a SHM pool.
std::optional< Shm_struc_channel > m_shm_channel
The channel used for transmitting SHM-pool messages.
flow::util::Blob_sans_log_context Blob
Alias for a light-weight blob used to encode SHM-handles emitted by lend_object() and consumed by bor...
Arena::Handle< T > Handle
First-class handle to an outer object as returned by borrow_object(); equals Arena::Handle a/k/a shm:...
flow::async::Single_thread_task_loop m_serial_task_loop
Task engine to process tasks in a serial manner (a/k/a thread W).
Shm_session(flow::log::Logger *logger, Shm_channel &&shm_channel, const transport::struc::Session_token &session_token_non_nil, flow::async::Task_asio_err &&shm_channel_error_handler)
Constructor (private due to shared_ptr factory pattern).
Definition: shm_session.cpp:55
util::process_id_t get_remote_process_id() const
Returns the remote process ID.
When borrowing STL-compliant in-SHM data structures, use this type as the Arena template parameter to...
Singleton repository for SHM-pool data pertaining to all SHM-arenas owned by this process.
(Internal-use) Thread_lcl_obj_db_admin<A>::this_thread_obj_db() is the current thread's module that t...
(Internal-use) Where a Thread_lcl_obj_db_admin cannot handle lend-object and dispose-object tracking,...
Represents/manages a segregated collection of in-SHM (shared memory) objects and buffers allocated us...
Definition: ipc_arena.hpp:177
Obj_handle< Pointed_type > Handle
First-class handle – a shared_ptr of some kind by definition – to objects construct<>()ed by this are...
Definition: ipc_arena.hpp:206
Stateless allocator usable with STL-compliant containers to store (or merely read) them directly in S...
A Channel with a blobs pipe only (no handles pipe) that uses a Unix domain socket connection as the u...
Definition: channel.hpp:1257
Owning and wrapping a pre-connected transport::Channel peer (an endpoint of an established channel ov...
Definition: channel.hpp:645
typename Sync_io_obj::Msg_out Msg_out
Encapsulation of any out-message payload sent or meant to be sent via send() (et al) by a *this of th...
Definition: channel.hpp:692
typename Sync_io_obj::Msg_in Msg_in
Encapsulation of any in-message instance received by a *this of this type.
Definition: channel.hpp:701
String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent entity fr...
Support for SHM-backed ipc::session sessions and session-servers with the SHM-jemalloc (ipc::shm::are...
std::vector< std::unique_ptr< ipc::shm::arena_lend::stat::Borrower_pool_stats > > Borrower_pool_stats_list
Canonical short-hand for a list of per-arena stat::Borrower_pool_stats, as produced by the borrower-s...
uint32_t pool_id_t
Pool ID type.
Obj_handle< T > construct_with_borrower_obj_disposer(T *addr, pool_id_t lend_tracker_pool_id, use_ct_idx_t use_ct_idx, owner_id_t owner_id, collection_id_t collection_id, std::shared_ptr< const Shm_session > &&shm_session_to_keep_alive)
(Roughly) the borrower-side counterpart of Owner_obj_disposer_and_mdt, this returns a borrower-side h...
uint32_t use_ct_idx_t
Index into a given Lend_tracker_pool's bulk storage of arena-construct()ed (outer a/k/a first-class) ...
uint32_t collection_id_t
Identifier type for a shared memory pool collection.
util::Shared_name Shared_name
Short-hand for util::Shared_name; used in particular for SHM pool names at least.
boost::uuids::uuid Session_token
A type used by struc::Channel for internal safety/security/auth needs.
Definition: struc_fwd.hpp:121
::pid_t process_id_t
Syntactic-sugary type for POSIX process ID (integer).
Definition: util_fwd.hpp:140
flow::util::String_view String_view
Short-hand for Flow's String_view.
Definition: util_fwd.hpp:112
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
Definition: common.hpp:313
A serialized object handle: a compact representation of an in-SHM object's location and lending metad...
pool_offset_t m_pool_offset
The offset within the pool where this object resides.
pool_id_t m_pool_id
Pool identifier. Reminder: this is globally unique until reboot.
ipc::shm::arena_lend::detail::use_ct_idx_t m_use_ct_idx
Index of the use-count slot within the above Lend_tracker_pool.
pool_id_t m_lend_tracker_pool_id
Pool ID of the Lend_tracker_pool holding the use-count slot for this object. Same namespace as m_pool...
collection_id_t m_collection_id
The collection ID of the arena in which this object was created. Reminder: it is unique only given a ...
Internal-use shared_ptr disposer object, attached to every cross-process-lendable object handle retur...
Printable (with knobs in contained util::stat::Info_dump_format) bundling of stats/info relevant to (...
Analogous subject matter to Owner_pool_lookup_global_stats but borrower-side.
Loosely similar subject matter to Owner_pool_stats but regarding the borrower-side view of SHM-pools,...
This info-struct contains basic properties of a SHM-pool (in a SHM-arena).