Flow-IPC 3.0.0
Flow-IPC project: Full implementation reference.
native_socket_stream_cfg.hpp
Go to the documentation of this file.
1/* Flow-IPC: Core
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
23#include <flow/log/log_fwd.hpp>
24#include <type_traits>
25#include <cstdint>
26#include <limits>
27
28namespace ipc::transport
29{
30
31// Types.
32
33/**
34 * Grouping of compile-type config/knobs (such as type aliases, constants) governing some of the behavior of
35 * Native_socket_stream and sync_io::Native_socket_stream, the latter in particular being a core IPC transport
36 * in Flow-IPC.
37 *
38 * ### Rationale ###
39 * Some or all of these definitions can be viewed as implementation details, which would normally imply
40 * class Native_socket_stream_cfg and everything in it should be in `detail/` and by Flow convention not
41 * for public use. Nevertheless we've kept it all public. We feel it is harmless to expose such things,
42 * and it could be helpful for logging/reporting, as much of it concerns key performance characteristics of
43 * arguably *the* core low-level IPC transport in Flow-IPC.
44 */
46{
47public:
48 // Types.
49
50 /**
51 * The type used to encode the internal meta-blob length/type-indicator for sync_io::Native_socket_stream and its
52 * derivative(s).
53 *
54 * @todo Native_socket_stream_cfg::low_lvl_payload_blob_length_t should arguably be renamed, since it represents
55 * a length (when not representing a message-type) only if #S_USE_OS_DGRAM_SUPPORT is `false`; otherwise
56 * it always represents a message-type only. We have kept the existing name arguably form legacy inertia and/or
57 * because a better name was not immediately forthcoming.
58 *
59 * If #S_USE_OS_DGRAM_SUPPORT is `false`: this puts a cap on how long the meta-blobs can be, and as of this writing
60 * it width and sign happen to be chosen (at 2-bytes, unsigned), because ~64Ki is a pretty reasonable max
61 * meta-blob size; arguably users should not be sending larger messages than that.
62 *
63 * If `S_USE_OS_DGRAM_SUPPORT == true`: this no longer dictates any cap and can be really any unsigned type.
64 * As of this writing we've decided to use the same type, basically, for simplicity/consistency. Further: a larger
65 * width seems an unnecessary use of space; while using only 1 byte "feels" like it could constrain future
66 * extensions. (Though, perhaps something equalling a native word, for some definition of word, would yield
67 * somewhat better perf somewhere? Look into it? Not an official to-do yet.)
68 */
70 static_assert(std::numeric_limits<low_lvl_payload_blob_length_t>::is_integer
71 && (!std::numeric_limits<low_lvl_payload_blob_length_t>::is_signed),
72 "low_lvl_payload_blob_length_t is at least in some cases used as a length type, "
73 "so it must be an unsigned integer of some width.");
74
75 // (Contant-dependent type(s) below.)
76
77 // Constants.
78
79#ifndef FLOW_OS_LINUX
80static_assert(false, "Native_socket_stream uses AF_LOCAL/SOCK_SEQPACKET and relies on Linux semantics for it; "
81 "this may be available in other OS with alleged spotty support, and we have not tested it "
82 "elsewhere. For now build in Linux only.");
83#endif
84 /**
85 * Whether the OS provides, and we shall use for sync_io::Native_socket_stream and derivative(s),
86 * Unix domain sockets of sub-type `SOCK_SEQPACKET` (connection-oriented, message boundary-preserving);
87 * if `true` then yes; if `false` then only `SOCK_STREAM` (connection-oriented, message boundary-non-preserving)
88 * shall be used instead.
89 *
90 * For Linux of any reasonably recent vintage this is `true`.
91 *
92 * A non-obvious Linux note: While, with this at `true`, we're able to internally yield some nice perf and
93 * simplicity gains -- as message boundaries no longer need to be implemented in user-land -- a new (versus
94 * `false`/`SOCK_STREAM`) OS limitation exists. While there is a byte-wise send buffer maximum either way,
95 * with `true`/`SOCK_SEQPACKET` there's also this: Linux caps the number of kernel-queued (unread) messages
96 * on such a socket at the value of the `net.unix.max_dgram_qlen` sysctl (per network namespace) when the socket is
97 * created. The kernel default is 10; systemd raises it to 512 at boot; a container with its own network
98 * namespace may have the kernel default. Beyond that limit (or the send-buffer limit, whichever applies first) a
99 * send would-block, whereupon we queue internally (see Native_handle_sender concept doc header). Functionally
100 * this is invisible to you; but if bursts of unread messages routinely exceed the limit, it may affect
101 * performance (not, as of this writing, empirically tested). If this does occur: consider raising the sysctl.
102 * Informally: 512 seems fairly generous; 10, less so.
103 *
104 * @todo Native_socket_stream_cfg::S_USE_OS_DGRAM_SUPPORT and Native_socket_stream_cfg::S_USE_OS_DGRAM_BATCH_SUPPORT
105 * is each currently determined based on availability in the *compiling* host OS; but for various
106 * reasons (both flexibility/testing and compatibility with other target platforms
107 * and versions) it might be helpful to make each configurable via the Flow-IPC build script(s). It could also
108 * be configured per individual Native_socket_stream at compile-time (a template parameter).
109 */
110 static constexpr bool S_USE_OS_DGRAM_SUPPORT = true;
111
112 /**
113 * Whether the OS provides, and we shall use for sync_io::Native_socket_stream and derivative(s),
114 * batch-receiving of multiple Unix domain socket *datagrams* in a single native OS-call.
115 * If `true` then yes; if `false` then only single-message OS-calls shall be used instead, even if
116 * one uses sync_io::Native_socket_stream::async_receive_blob_batch() and similar/derived APIs. In the latter
117 * case Flow-IPC code shall emulate batch-transmission via potentially multiple regular one-message OS-calls.
118 *
119 * Meaningful only if #S_USE_OS_DGRAM_SUPPORT is `true`. (Code internally shall check #S_USE_OS_DGRAM_SUPPORT
120 * and only if `true` then check this value.)
121 *
122 * For Linux of any reasonably recent vintage this is `true`.
123 */
124 static constexpr bool S_USE_OS_DGRAM_BATCH_SUPPORT = true;
125
126 /**
127 * For sync_io::Native_socket_stream and derivative(s):
128 * Value for the length/type-indicator field in each internal message's header (internally known as payload 1)
129 * that means "not a length; indicating this is a ping message" (if #S_USE_OS_DGRAM_SUPPORT is `false`) or
130 * just "this is a ping message" (otherwise).
131 *
132 * The other special value is 0 which indicates "graceful close, or no meta-blob" (depending on whether
133 * a native handle is attached).
134 */
136 = std::numeric_limits<low_lvl_payload_blob_length_t>::max();
137
138 /**
139 * The maximum length of a blob that can be sent by the protocol internally used by sync_io::Native_socket_stream
140 * and derivative(s). Native_socket_stream::send_native_handle() (and similar) shall synchronously emit a
141 * particular error, if `[meta_]blob.size()` exceeds this.
142 *
143 * As alluded to in #low_lvl_payload_blob_length_t doc header: The choice of value follows somewhat divergent
144 * reasoning depending on #S_USE_OS_DGRAM_SUPPORT. If `false` then we essentially use the highest possible value;
145 * since #S_PING_SENTINEL exists, that eliminates the highest possible value, so we use the next-highest.
146 *
147 * If #S_USE_OS_DGRAM_SUPPORT is `true`, though, we can use anything here that the OS shall accept without
148 * yielding a message-too-large (`EMSGSIZE` in Linux) in sending or receiving. As of this writing modern
149 * Linuxes happen to accept that same value (roughly), namely 64Ki, and since we chose #low_lvl_payload_blob_length_t
150 * partially because its max-value is a reasonable limit on message size (subjectively), we shall use the same
151 * value here (actually a couple bytes more for roundness).
152 *
153 * @warning I (ygoldfel) tested out `SOCK_SEQPACKET` (also `SOCK_DGRAM` which behaved identically) with a fairly
154 * modern Linux distro configured normally; this showed the limit was well above 128Ki but below 256Ki.
155 * This suggests our chosen value here may even be larger than what is allowed in reality depending on the host OS.
156 * If this occurs, it shall exhibit as follows: Our APIs won't throw an error when pre-checking the receive-buffer
157 * size in `async_receive_*()`; but then later the given Native_socket_stream shall get hosed due to an OS
158 * message-too-large, if indeed a too-large message manifests in your application.
159 */
161 ? 0x10000
162 : (S_PING_SENTINEL - 1);
163
164 /**
165 * Whether to periodically log various statistics about Native_socket_stream. Stats are always collected
166 * regardless of this setting; this controls only whether they are logged. See #S_STATS_LOG_SEV and
167 * #S_STATS_LOG_PERIOD for further knobs.
168 */
169 static constexpr bool S_STATS_LOG_ENABLED = false;
170
171 /// If #S_STATS_LOG_ENABLED: the log severity for stats output.
172 static constexpr flow::log::Sev S_STATS_LOG_SEV = flow::log::Sev::S_INFO;
173
174 /// If #S_STATS_LOG_ENABLED: how often to output stats via logs.
175 static constexpr util::Fine_duration S_STATS_LOG_PERIOD = boost::chrono::seconds{5};
176
177 /**
178 * For batch-receiving event-count histogram: the width of each event-count bucket.
179 *
180 * By example: consider the histogram for how many messages were received in each batch-receive. Suppose
181 * max batch size is 64. For each batch-receive, we can get either 0 (would-block) messages or 1 or 2 or ...
182 * or 64. There is always a bucket for 0, and then a bunch of evenly-sized buckets. Then, suppose
183 * `S_STATS_HISTO_MSG_CT_BUCKET_SZ == 2`. The next bucket would be for getting 1 or 2 messages; then for 3 or 4;
184 * etc., up to the bucket including 64.
185 *
186 * The value 1 is pretty good, as each possible outcome is separately tracked. Plus it is interesting when
187 * *any* batching (2+) occurred versus none (1).
188 */
189 static constexpr size_t S_STATS_HISTO_MSG_CT_BUCKET_SZ = 1;
190
191 // Constant-dependent types.
192
193 /**
194 * sync_io::Native_socket_stream and derivative(s) shall internally use a boost.asio socket type of this
195 * boost.asio protocol, as determined by #S_USE_OS_DGRAM_SUPPORT.
196 */
199
200private:
201 // Constructors/destructor.
202
203 /// Forbid instantiation, for it is pointless.
205}; // class Native_socket_stream_cfg
206
207} // namespace ipc::transport
Grouping of compile-type config/knobs (such as type aliases, constants) governing some of the behavio...
Native_socket_stream_cfg()=delete
Forbid instantiation, for it is pointless.
static constexpr bool S_STATS_LOG_ENABLED
Whether to periodically log various statistics about Native_socket_stream.
static constexpr flow::log::Sev S_STATS_LOG_SEV
If S_STATS_LOG_ENABLED: the log severity for stats output.
static constexpr size_t S_STATS_HISTO_MSG_CT_BUCKET_SZ
For batch-receiving event-count histogram: the width of each event-count bucket.
static constexpr util::Fine_duration S_STATS_LOG_PERIOD
If S_STATS_LOG_ENABLED: how often to output stats via logs.
std::conditional_t< S_USE_OS_DGRAM_SUPPORT, asio_local_stream_socket::Protocol_pkt_stream, asio_local_stream_socket::Protocol_byte_stream > Protocol
sync_io::Native_socket_stream and derivative(s) shall internally use a boost.asio socket type of this...
uint16_t low_lvl_payload_blob_length_t
The type used to encode the internal meta-blob length/type-indicator for sync_io::Native_socket_strea...
static constexpr low_lvl_payload_blob_length_t S_PING_SENTINEL
For sync_io::Native_socket_stream and derivative(s): Value for the length/type-indicator field in eac...
static constexpr size_t S_MAX_META_BLOB_LENGTH
The maximum length of a blob that can be sent by the protocol internally used by sync_io::Native_sock...
static constexpr bool S_USE_OS_DGRAM_BATCH_SUPPORT
Whether the OS provides, and we shall use for sync_io::Native_socket_stream and derivative(s),...
static constexpr bool S_USE_OS_DGRAM_SUPPORT
Whether the OS provides, and we shall use for sync_io::Native_socket_stream and derivative(s),...
local_ns::seq_packet_protocol Protocol_pkt_stream
Short-hand for boost.asio Unix domain stream-socket – with built-in message boundary preservation – p...
local_ns::stream_protocol Protocol_byte_stream
Short-hand for boost.asio Unix domain stream-socket – without built-in message boundary preservation ...
Flow-IPC module providing transmission of structured messages and/or low-level blobs (and more) betwe...
flow::Fine_duration Fine_duration
Short-hand for Flow's Fine_duration.
Definition: util_fwd.hpp:114