Flow-IPC 3.0.0
Flow-IPC project: Full implementation reference.
native_socket_stream.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
27#include <flow/log/log.hpp>
28#include <flow/async/util.hpp>
29#include <boost/move/unique_ptr.hpp>
30#include <experimental/propagate_const>
31#include <iosfwd>
32#include <string>
33#include <utility>
34
35namespace ipc::transport
36{
37
38// Types.
39
40/**
41 * Implements both Native_handle_sender and Native_handle_receiver concepts by using a Unix domain
42 * socket, allowing high-performance but non-zero-copy transmission of discrete messages, each containing a native
43 * handle, a binary blob, or both. This is a low-level (core) transport mechanism; higher-level (structured)
44 * transport mechanisms may use Native_socket_stream to enable their work. Native_socket_stream, as of this writing,
45 * is unique in Flow-IPC, in that it is able to transmit not only blobs but also native handles.
46 *
47 * @see sync_io::Native_socket_stream and util::sync_io doc headers. The latter describes a general pattern which
48 * the former implements. Which of the two to use depends on your needs, as summarized in the util::sync_io
49 * doc header.
50 *
51 * ### Quick note on naming ###
52 * It appears somewhat inconsistent to name it `Native_socket_stream[_acceptor]`
53 * and not `Native_handle_stream[_acceptor]`, given the implemented concept names `Native_handle_*er` and
54 * a key payload type being `Native_handle`. It's subjective and a matter of aesthetics even, of course,
55 * but the reasoning is: It briefly conveys (or at least suggests) that the underlying transport
56 * is the Unix domain *socket* (with messages arranged in a *stream* at that); while also suggesting that *native*
57 * handles are transmissible over it (and it in fact is unique in that capability by the way). Perhaps
58 * something like `Native_handle_socket_stream[_acceptor]` would've been more accurate, but brevity is a virtue.
59 *
60 * @note The same reasoning applies for Socket_stream_channel and Socket_stream_channel_of_blobs with the
61 * `native_` part being left out for orthogonal aesthetic reasons.
62 *
63 * ### Blob_sender and Blob_receiver concept compatibility ###
64 * Native_socket_stream also implements the Blob_sender and Blob_receiver concepts. To wit:
65 * You must choose whether you shall use this as a `Blob_sender/receiver` or `Native_handle_sender/receiver`,
66 * and conversely the other side must do the same in matched fashion. Doing otherwise leads to undefined behavior.
67 * To use the latter concept pair simply use send_native_handle() and async_receive_native_handle() and never
68 * send_blob() or async_receive_blob(). To use the former concept pair simply do the reverse.
69 * On the other side do the matched thing. All the other methods/dtor are the same regardless of concept pair chosen.
70 *
71 * How does this work? Trivially: consider, say, send_native_handle() which can send a native handle, or none;
72 * and a blob, or none (but at least 1 of the 2). send_blob() can only take a blob (which must be present) and
73 * internally will simply act as send_native_handle() with a null handle. Conversely async_receive_native_handle()
74 * receives a handle (or none) and blob (or none); so async_receive_blob() will receive only a blob. The only added
75 * behaviors in `Blob_sender/receiver` "mode":
76 * - send_blob() requires a blob of size 1+. (send_native_handle() allows an empty meta-blob; i.e., no blob.)
77 * - async_receive_blob() will emit an error if a payload arrives with a native handle in it:
78 * error::Code::S_BLOB_RECEIVER_GOT_NON_BLOB. This would only occur if the other side were using the mismatched
79 * API.
80 *
81 * Further discussion might ignore the degraded `Blob_*` concepts, as they are a trivial degenerate case of
82 * `Native_handle_*`.
83 *
84 * @see other, dedicated Blob_sender and Blob_receiver impls namely, at least, the persistent-MQ-based
85 * Blob_stream_mq_sender and Blob_stream_mq_receiver. They may appeal due to potentially better performance
86 * and/or a creation/addressing scheme that is more appealing, depending.
87 *
88 * ### Informal comparison to other core transport mechanisms ###
89 * Firstly, as noted, it is currently unique in that it can transmit native handles. The rest of the discussion
90 * here is about transmitting blobs (called meta-blobs, meaning they potentially accompany native handles).
91 *
92 * It is intended for transmission of relatively short messages -- rough guidance
93 * being for max length being in the 10s-of-KiB range. With a modern Linux kernel on server hardware from about 2014
94 * to 2020, our performance tests show that its raw speed for messages of aforementioned size is comparable to
95 * zero-copy mechanisms based on POSIX message queues and Boost SHM-based message queues. Technically we found it
96 * to be somewhat faster than the latter and somewhat slower than the former. However, to transmit messages of
97 * arbitrary length without copying, one can send handles to SHM areas containing them (such as structured messages
98 * [de]serialized using zero-copy builders: capnp and the like). This further reduces the importance of relative perf
99 * compared to other low-level transports (which, as noted, is pretty close regardless -- though this is bound to
100 * stop being true for much longer messages, if the send-SHM-handles technique were to *not* be used).
101 *
102 * @see potentially perf-relevant notes in doc header for Native_socket_stream_cfg::S_USE_OS_DGRAM_SUPPORT.
103 *
104 * ### Cleanup ###
105 * Some core transports rely on SHM or SHM-style semantics, wherein the transport has kernel persistence, meaning
106 * some or all processes exiting does not free the resources involved. For those transports special measures must
107 * be taken to ensure cleanup after both relevant processes die or no longer use a transport instance. This is not the
108 * case for Native_socket_stream: When the underlying (hidden from user) Unix domain socket handle is closed by both
109 * sides, all resources are automatically cleaned up; and processes exiting always closes all such handles.
110 * These matters for all transports are hidden from the user as much as possible, but these internal facts are still
111 * relevant knowledge for the reader/user.
112 *
113 * ### How to use ###
114 * Generally, this class's behavior is dictated by the 2 main concepts it implements: Native_handle_sender and
115 * Native_handle_receiver. Though internally a single mechanism is used for both (incoming and outgoing) *pipes*
116 * (a term I use generically, not meaning "Unix FIFOs"), the two pipes are mostly decoupled. Bottom line:
117 * Behavior is dictated by the 2 concepts, so please see their respective doc headers first. Next, see the doc
118 * headers of the main concrete APIs which discuss any behaviors relevant to Native_socket_stream specifically:
119 * - dtor;
120 * - send_native_handle() (or send_blob()), `*end_sending()`, auto_ping();
121 * - async_receive_native_handle() (or async_receive_blob()), idle_timer_run();
122 * - async_receive_native_handle_batch() (or async_receive_blob_batch());
123 * - send_meta_blob_max_size() (or send_blob_max_size());
124 * - receive_meta_blob_max_size() (or receive_blob_max_size());
125 * - default ctor;
126 * - move ctor, move assignment.
127 *
128 * There is also:
129 * - remote_peer_process_credentials() which is specific to Native_socket_stream and not those concepts.
130 *
131 * That above list (in?)conspicuously omits the initialization API. So how does one create this connection
132 * and hence the two required `Native_socket_stream`s that are connected to each other (each in PEER state)?
133 *
134 * A Native_socket_stream object is always in one of 2 states:
135 * - PEER. Upon entering this state, the peer is connected (is an actual peer). At this stage it exactly
136 * implements the concepts Native_handle_sender, Native_handle_receiver, Blob_sender, Blob_receiver.
137 * If the pipe is hosed (via an error or graceful-close), it remains in this PEER state (but cannot do anything
138 * useful anymore). It is not possible to exit PEER state.
139 * - The only added behavior on top of the implemented concepts is remote_peer_process_credentials() (which is not
140 * a concept API but logically only makes sense when already connected, i.e., in PEER state).
141 * - NULL. In this state, the object is doing nothing; neither connected nor connecting.
142 *
143 * Therefore, to be useful, one must somehow get to PEER state in which it implements the aforementioned concepts.
144 * How to do this? Answer: There are 2* ways:
145 * - Use Native_socket_stream_acceptor on the side you've designated as the server for that connection or set of
146 * connections. This allows to wait for a connection and eventually, on success, moves the target
147 * Native_socket_stream passed to Native_socket_stream_acceptor::async_accept() to PEER state.
148 * - On the other side, use a Native_socket_stream in NULL state and invoke sync_connect() which will move
149 * synchronously and non-blockingly to PEER state (mission accomplished).
150 * If the connect fails (also synchronously, non-blockingly), it will go back to NULL state.
151 * - Use a mechanism, as of this writing ipc::transport::Channel at least, that uses the following technique:
152 * -# A process-wide Native_socket_stream connection is established using via the client-server method in the
153 * above bullet point.
154 * -# A socket-pair-generating OS call generates 2 pre-connected native Unix domain socket handles.
155 * -# E.g., use boost.asio `asio_local_stream_socket::connect_pair()`.
156 * -# 1 of the 2 handles is passed to the other side using the process-wide connection from step 1.
157 * -# On each side, construct Native_socket_stream using the #Native_handle-taking ctor thus entering PEER
158 * state directly at construction (on each side).
159 *
160 * Way 2 is better, because it requires no `Shared_name`s whatsoever, hence there are no thorny naming issues.
161 * The drawback is it requires an already-existing Native_socket_stream in order to transmit the handle! Chicken/egg!
162 * Therefore, the informal recommendation is -- if practical -- use way 1 once (create chicken); then subsequently use
163 * way 2 as needed to easily create further connections.
164 *
165 * Native_socket_stream is move-constructible and move-assignable (but not copyable). This is, at any rate,
166 * necessary to work with the boost.asio-style Native_socket_stream_acceptor::async_accept() API.
167 * A moved-from Native_socket_stream is as-if default-constructed; therefore it enters NULL state.
168 *
169 * (*) There is, in fact, another way -- one could call it way 2a. It is just like way 2; except that -- upon
170 * obtaining a pre-connected `Native_handle` (one of a pair) -- one first constructs a `sync_io` "core,"
171 * namely a sync_io::Native_socket_stream object, using the *exact* same signature (which takes a `Logger*`,
172 * a nickname, and the pre-connected `Native_handle`). Then, one constructs a `*this` by `move()`ing that
173 * guy into the `sync_io`-core-adopting ctor. The result is exactly the same. (Internally, whether one uses way 1
174 * or way 2, `*this` will create a sync_io::Native_socket_stream core itself. I tell you this, in case you are
175 * curious.)
176 *
177 * ### Why no async-connect method, only sync-connect? ###
178 * Without networking, the other side (Native_stream_socket_acceptor) either exists/is listening; or no.
179 * Connecting is a synchronous, non-blocking operation; so an `async_connect()` API in this context only makes
180 * life harder for the user. (However, there are some serious plans to add a networking-capable counterpart
181 * (probably via TCP at least) to Native_socket_stream; that one will almost certainly have an `async_connect()`,
182 * while its `sync_connect()` will probably become potentially blocking.)
183 *
184 * If you are worried about attempting to connect to a Native_stream_socket_acceptor whose internal backlog limit has
185 * been reached: please don't. It won't happen; we have contemplated the details.
186 *
187 * ### Thread safety ###
188 * We add no more thread safety guarantees than those mandated by the main concepts. To wit:
189 * - Concurrent access to 2 separate Native_socket_stream objects is safe.
190 * - After construction, concurrent access to the main transmission API methods (or
191 * remote_peer_process_credentials()) is not safe.
192 *
193 * @internal
194 * ### Implementation design/rationale ###
195 * Internally Native_socket_stream strictly uses the pImpl idiom (see https://en.cppreference.com/w/cpp/language/pimpl
196 * for an excellent overview). Update: Oops, that lasted a while, but with the introduction of
197 * `async_receive_*_batch<Msg_resource>()` (a method template), strict pImpl (`Impl` an inner class, stable ABI,
198 * and so on) is no longer possible. The template cannot be converted sanely into a function taking a `function<>`
199 * (and similar tech), unlike other templates merely using t-params for handler functors. (It's doable, but it would
200 * hurt perf unacceptably.) So, we use what I (ygoldfel) term "pImpl-lite". That is: it is pImpl that achieves
201 * performant and easily-coded move-semantics -- in the face of fairly complex async impl details --
202 * but does *not* achieve a stable ABI (the thing where one can change impl method bodies without recompiling
203 * the code/changing the binary signature of the class). Long story short-ish:
204 *
205 * - The "true" Native_socket_stream is actually the self-contained, but not publicly exposed,
206 * Native_socket_stream_impl class.
207 * - #m_impl is the `unique_ptr` to `*this` object's `Native_socket_stream_impl`. This becomes null only when
208 * Native_socket_stream is moved-from; but if one attempts to call a method (such as sync_connect()) on the
209 * moved-from `*this` #m_impl is lazily re-initialized to a new default-cted (therefore NULL-state)
210 * Native_socket_stream_impl.
211 * - Every public method of Native_socket_stream `*this` forwards to essentially the same-named method
212 * of Native_socket_stream_impl `*m_impl`. Subtleties:
213 * - Method templates that take a t-param like `Task_err_sz` for a functor forward to a corresponding
214 * non-template method in Native_socket_stream_impl, converting (e.g.) `Task_err_sz` for concretely-typed
215 * `Function<> flow::async::Task_asio_err_sz`. Historically this was done as a requirement of doing strict
216 * pImpl (which for aforementioned other reasons we can no longer do), but since the impl needs to store these
217 * guys as `Function<>`s anyway, we left this alone; as (1) don't fix what's not broken, (2) functions are
218 * less annoying than function templates generally, and (3) the perf is identical.
219 * - For a method template (like async_receive_blob_batch()) that cannot forward to a regular method
220 * (for aforementioned reasons), we forward to a method template in Native_socket_stream_impl.
221 * Hence, Native_socket_stream_impl cannot be an incomplete type in this class body and header file; and
222 * therefore it isn't strict pImpl. So we simply `#include` the header(s) with Native_socket_stream_impl.
223 * - Because the implementation is 100% encapsulated inside the `Impl` #m_impl, move-assignment and move-construction
224 * are acquired for "free":
225 * - Coding-ease "free": Default-generated move-ctor and move-assignment simply move the `unique_ptr` #m_impl.
226 * - Perf "free": `unique_ptr` move is cheap (nullify the source pointer after copying the pointer value
227 * into the target pointer). Though, there is an extra use of the heap per `*this`.
228 *
229 * Okay, if one is familiar with pImpl, none of this is surprising in terms of how it works. *Why* though? Answer:
230 * - I (ygoldfel) chose pImpl *not* to maintain a stable ABI/guarantee the implementation of methods can be changed
231 * without needing to recompile the code that `#include`s the present header file Native_socket_stream.hpp...
232 * - ...but rather specifically to get the move-ction and move-assignment for "free" as briefly explained above.
233 * - Consider the alternative. Suppose Native_socket_stream is implemented directly inside Native_socket_stream
234 * itself; and we still want move-semantics available to the user.
235 * - Writing the move-ctor/assignment in and of itself is very much non-trivial. The implementation involves
236 * many delicate data members including async task-engines and threads some of which may be related to one
237 * another. Swapping them properly, etc. etc., is no mean feat. Plus it's not exactly perf-y either.
238 * - Yet with pImpl-facilitated move semantics it becomes trivial. `Impl` is non-copyable, non-movable and can
239 * rely on a stable `this`. Move semantics become entirely the concern of the wrapper Native_socket_stream:
240 * nice and clean.
241 *
242 * This leaves two questions.
243 * - One: why do we want public move-semantics anyway? Why not just provide a non-movable Native_socket_stream
244 * and let the user worry about wrapping it in a `shared_ptr` or `unique_ptr`, if they want to move these objects
245 * around? Answer: Sure, that's perfectly reasonable. However:
246 * - Native_socket_stream_acceptor would need to impose shared-ownership/factory API in supplying the connected
247 * Native_socket_stream to the user asynchronously. That could be fine -- but it'd be nice to have
248 * acceptor semantics that are like boost.asio's as opposed to our own design.
249 * - Nevertheless Native_socket_stream_acceptor API could still be designed in factory fashion, even if it's
250 * inconsistent with what boost.asio users might expect. But: now consider the other guys implementing
251 * Blob_sender and Blob_receiver, namely the message queue-based (MQ-based) Blob_stream_mq_sender and
252 * and Blob_stream_mq_receiver. They don't use an acceptor API at all by their nature; and hence they don't
253 * need any factory semantics. So now some Blob_sender and Blob_receiver implementing classes use ownership
254 * semantics X, while others (Native_socket_stream) use ownership semantics Y -- despite implementing the
255 * same concepts. Is this okay? Sure, it's not bad; but it's not ideal all in all.
256 * - Therefore for consistency inside the overall ipc::transport API, as well as versus boost.asio API,
257 * making Native_socket_stream directly constructible (and movable when needed for acceptor semantics)
258 * ultimately is the nicer approach. (Historical note: An early version of this class and
259 * Native_socket_stream_acceptor indeed used the alternative, factory-based approach. So we've tried both.)
260 * - Two: what about the negatives of pImpl? It is after all a trade-off. I won't go over all the negatives here
261 * "formally" (see the cppreference.com page mentioned above for a survey). Long story short:
262 * - The indirection of all calls through a pointer is probably a minor perf hit in and of itself.
263 * - Factories involve their own perf hits, particularly since typically they use `shared_ptr` and not
264 * `unique_ptr` (which is quite quick in comparison).
265 * - The laborious need to code a copy of the entire public API, so as to forward to `Impl`, is *easily*
266 * no big deal compared to what it would take to make Native_socket_stream movable directly.
267 * - pImpl is not inlining-friendly and cannot be used for a header-only library. However:
268 * - The entire Flow-IPC library -- and all of Flow too -- to begin with refuses to be inlining-friendly and
269 * header-only; so this does not change that.
270 * - Both Flow-IPC and Flow encourage the use of LTO which will inline everything anyway.
271 * If LTO is not available then the resulting perf loss already applies to both libraries; so this
272 * does not change that. Conversely adding LTO will benefit everything including Native_socket_stream.
273 * - So we are consistent in this decision for better or worse.
274 *
275 * That's my (ygoldfel) story, and I am sticking to it. I should note that the same design philosophy is followed
276 * in much of Flow-IPC, `Native_socket_stream` being the first one to be coded.
277 *
278 * Again, though, it's no longer strict pImpl; more like just an impl class wrapped in a `unique_ptr` for easy/fast
279 * move-semantics implementation.
280 *
281 * The rest of the implementation is inside Native_socket_stream_impl and is discussed in that class's doc header.
282 *
283 * @see Native_socket_stream_impl doc header.
284 *
285 * @endinternal
286 *
287 * @see Native_handle_sender: implemented concept.
288 * @see Native_handle_receiver: implemented concept.
289 * @see Blob_sender: alternatively implemented concept.
290 * @see Blob_receiver: alternatively implemented concept.
291 */
293{
294private:
295 // Types.
296
297 /// Short-hand for the impl type we're wrapping. Cannot simply forward-declare as in pImpl; we do pImpl-lite.
299
300public:
301 // Types.
302
303 /// Useful for generic programming, the `sync_io`-pattern counterpart to `*this` type.
305 /// You may disregard.
307
308 /// Implements Native_handle_receiver concept API.
309 template<typename Msg_resource>
311 /// Implements Blob_receiver concept API.
312 template<typename Msg_resource>
313 using Blob_batch_in = typename Impl::template Blob_batch_in<Msg_resource>;
314
315 /// Implements Blob_sender concept API.
317 /// Implements Native_handle_sender concept API. Identical to #Blob_snd_stats for this impl.
319 /// Implements Blob_receiver concept API.
321 /// Implements Native_handle_receiver concept API. Identical to #Blob_rcv_stats for this impl.
323
324 // Constants.
325
326 /// Implements concept API.
328
329 /**
330 * Implements concept API; namely it is `true`: async_receive_native_handle() with
331 * smaller-than-`receive_meta_blob_max_size()` size shall *not* lead to error::Code::S_INVALID_ARGUMENT; hence
332 * error::Code::S_MESSAGE_SIZE_EXCEEDS_USER_STORAGE is possible.
333 *
334 * @see Native_handle_receiver::S_META_BLOB_UNDERFLOW_ALLOWED: implemented concept. Accordingly also see
335 * "Blob underflow semantics" in that concept class's doc header for potentially important discussion.
336 */
337 static constexpr bool S_META_BLOB_UNDERFLOW_ALLOWED = true;
338
339 /**
340 * Implements concept API; namely it is `true`: async_receive_blob() with smaller-than-`receive_blob_max_size()`
341 * size shall *not* lead to error::Code::S_INVALID_ARGUMENT; hence error::Code::S_MESSAGE_SIZE_EXCEEDS_USER_STORAGE
342 * is possible.
343 *
344 * @see Blob_receiver::S_BLOB_UNDERFLOW_ALLOWED: implemented concept. Accordingly also see
345 * "Blob underflow semantics" in sister concept class's doc header for potentially important discussion.
346 */
348
349 /**
350 * Implements concept API. As of this writing this value depends on
351 * Native_socket_stream_cfg::S_USE_OS_DGRAM_BATCH_SUPPORT and `S_USE_OS_DGRAM_SUPPORT`; if and only if both are
352 * `true`, then this value is `> 1`; otherwise it is `1`.
353 */
356
357 /// Implements concept API. Same notes as for #S_RCV_NATIVE_HANDLE_BATCH_SZ_RECOMMENDATION; the values are equal.
359
360 // Constructors/destructor.
361
362 /**
363 * Default ctor (object is in NULL state).
364 * Implements Native_handle_sender *and* Native_handle_receiver APIs at the same time, per their concept contracts.
365 * (Also implements Blob_sender *and* Blob_receiver APIs; they are identical.)
366 * All the notes for both concepts' default ctors apply.
367 *
368 * This ctor is informally intended for the following uses:
369 * - A moved-from Native_socket_stream (i.e., the `src` arg move-ctor and move-assignment operator)
370 * becomes as-if defaulted-constructed.
371 * - A target Native_socket_stream for Native_socket_stream_acceptor::async_accept() shall typically be
372 * default-cted; Native_socket_stream_acceptor shall asynchronously move-assign a logger-appointed,
373 * nicely-nicknamed object into that target `*this`.
374 *
375 * Therefore it would be unusual (though allowed) to make direct calls such as sync_connect() and send_blob()
376 * on a default-cted Native_socket_stream without first moving a non-default-cted object into it.
377 */
379
380 /**
381 * Creates a Native_socket_stream in NULL (not connected) state.
382 *
383 * This ctor is informally intended for the following use:
384 * - You create a Native_socket_stream that is logger-appointed and nicely-nicknamed; then you call
385 * sync_connect() on it in order to move it to, hopefully, PEER state.
386 * It will retain the logger and nickname throughout.
387 *
388 * Alternatively:
389 * - If you intend to get a Native_socket_stream from a pre-connected #Native_handle:
390 * Use that Native_socket_stream ctor.
391 * - If you intend to get a Native_socket_stream by accepting it server-style:
392 * Use the default Native_socket_stream ctor; pass that object to Native_socket_stream_acceptor::async_accept().
393 *
394 * @param logger_ptr
395 * Logger to use for subsequently logging.
396 * @param nickname_str
397 * Human-readable nickname of the new object, as of this writing for use in `operator<<(ostream)` and
398 * logging only.
399 */
400 explicit Native_socket_stream(flow::log::Logger* logger_ptr, util::String_view nickname_str);
401
402 /**
403 * Constructs the socket-and-meta-blob stream by taking over an already-connected native Unix domain socket handle.
404 * The socket must be freshly connected without any traffic exchanged on the connection so far; otherwise behavior
405 * undefined.
406 *
407 * ### Performance ###
408 * The taking over of `native_peer_socket_moved` should be thought of as light-weight.
409 *
410 * @param logger_ptr
411 * Logger to use for subsequently logging.
412 * @param native_peer_socket_moved
413 * The wrapped native handle shall be taken over by `*this`; and this wrapper object will be made
414 * `.null() == true`. In plainer English, the main point is, this is the native socket
415 * over which traffic will proceed.
416 * @param nickname_str
417 * Human-readable nickname of the new object, as of this writing for use in `operator<<(ostream)` and
418 * logging only.
419 */
420 explicit Native_socket_stream(flow::log::Logger* logger_ptr, util::String_view nickname_str,
421 Native_handle&& native_peer_socket_moved);
422
423 /**
424 * Implements Native_handle_sender *and* Native_handle_receiver APIs at the same time, per their concept contracts.
425 * (Also implements Blob_sender *and* Blob_receiver APIs; they are identical.)
426 *
427 * ### Performance ###
428 * The taking over of `sync_io_core_in_peer_state_moved` should be thought of as light-weight.
429 *
430 * @param sync_io_core_in_peer_state_moved
431 * See above.
432 */
433 explicit Native_socket_stream(Sync_io_obj&& sync_io_core_in_peer_state_moved);
434
435 /**
436 * Move-constructs from `src`; `src` becomes as-if default-cted (therefore in NULL state).
437 * Implements Native_handle_sender *and* Native_handle_receiver APIs at the same time, per their concept contracts.
438 * (Also implements Blob_sender *and* Blob_receiver APIs; they are identical.)
439 *
440 * @param src
441 * See above.
442 */
444
445 /// Copy construction is disallowed.
447
448 /**
449 * Implements Native_handle_sender *and* Native_handle_receiver APIs at the same time, per their concept contracts.
450 * (Also implements Blob_sender *and* Blob_receiver APIs; they are identical.)
451 * All the notes for both concepts' destructors apply but as a reminder:
452 *
453 * Destroys this peer endpoint which will end both-direction pipes (of those still open) and cancel any pending
454 * completion handlers by invoking them ASAP with error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER.
455 * As of this writing these are the completion handlers that would therefore be called:
456 * - Any handler passed to async_receive_native_handle() that has not yet been invoked.
457 * There can be 0 or more of these.
458 * - The handler passed to async_end_sending().
459 * Since it is not valid to call async_end_sending() more than once, there is at most 1 of these.
460 */
462
463 // Methods.
464
465 /**
466 * Move-assigns from `src`; `*this` acts as if destructed; `src` becomes as-if default-cted (therefore in NULL state).
467 * No-op if `&src == this`.
468 * Implements Native_handle_sender *and* Native_handle_receiver APIs at the same time, per their concept contracts.
469 * (Also implements Blob_sender *and* Blob_receiver APIs; they are identical.)
470 *
471 * @see ~Native_socket_stream().
472 *
473 * @param src
474 * See above.
475 * @return `*this` (see concept API).
476 */
478
479 /// Copy assignment is disallowed.
481
482 /**
483 * Returns nickname, a brief string suitable for logging. This is included in the output by the `ostream<<`
484 * operator as well. This method is thread-safe in that it always returns the same value.
485 *
486 * @return See above.
487 */
488 const std::string& nickname() const;
489
490 // Connect-ops API.
491
492 /**
493 * To be invoked in NULL state only, it synchronously and non-blockingly attempts to connect to an opposing
494 * Native_socket_stream_acceptor or sync_io::Native_socket_stream_acceptor
495 * listening at the given absolute Shared_name; and synchronously reports failure or success, the latter
496 * showing `*this` has entered PEER state. Failure means `*this` remains in NULL state.
497 *
498 * If invoked outside of NULL state this returns `false` and otherwise does nothing.
499 *
500 * Note that `*this` (modulo moves) that has entered PEER state can never change state subsequently
501 * (even on transmission error); once a PEER, always a PEER.
502 *
503 * @see Class doc header, section "Why no async-connect method, only sync-connect?" for potentially interesting
504 * context.
505 *
506 * @param absolute_name
507 * Absolute name at which the `Native_socket_stream_acceptor` is expected to be listening.
508 * @param err_code
509 * See `flow::Error_code` docs for error reporting semantics. #Error_code generated:
510 * system codes most likely from `boost::asio::error` or `boost::system::errc` (but never would-block).
511 * @return `false` if and only if invoked outside of NULL state (that is: in PEER state).
512 */
513 bool sync_connect(const Shared_name& absolute_name, Error_code* err_code = nullptr);
514
515 // Send-ops API.
516
517 /**
518 * Implements Native_handle_sender API per contract. Note this value equals send_blob_max_size() at any given
519 * time which is *not* a concept requirement. Its PEER-state constant value can also be accessed as
520 * non-concept-mandated Native_socket_stream_cfg::S_MAX_META_BLOB_LENGTH.
521 *
522 * @return See above.
523 */
524 size_t send_meta_blob_max_size() const;
525
526 /**
527 * Implements Blob_sender API per contract. Note this value equals send_meta_blob_max_size() at any given
528 * time which is *not* a concept requirement. Its PEER-state constant value can also be accessed as
529 * non-concept-mandated Native_socket_stream_cfg::S_MAX_META_BLOB_LENGTH.
530 *
531 * @return See above.
532 */
533 size_t send_blob_max_size() const;
534
535 /**
536 * Implements Native_handle_sender API per contract. Reminder: You may call this directly from within a
537 * completion handler you supplied to an earlier `async_receive_*()`. Reminder: It's not thread-safe
538 * to call this concurrently with other transmission methods on the same `*this`.
539 *
540 * @param hndl_or_null
541 * See above.
542 * @param meta_blob
543 * See above. Reminder: The memory area described by this arg need only be valid until this
544 * method returns. Perf reminder: That area will not be copied except for very rare circumstances.
545 * @param err_code
546 * See above. Reminder: In rare circumstances, an error emitted here may represent something
547 * detected during handling of a *preceding* send_native_handle() call but after it returned.
548 * #Error_code generated:
549 * error::Code::S_INVALID_ARGUMENT (non-pipe-hosing error: `meta_blob.size()` exceeds
550 * send_meta_blob_max_size()),
551 * error::Code::S_SENDS_FINISHED_CANNOT_SEND (`*end_sending()` was called earlier),
552 * error::Code::S_LOW_LVL_TRANSPORT_HOSED_CANNOT_SEND (the incoming-direction processing detected
553 * that the underlying transport is hosed; specific code was logged and can be obtained via
554 * async_receive_native_handle()),
555 * `boost::asio::error::eof` (underlying transport hosed due to graceful closing by the other side),
556 * other system codes most likely from `boost::asio::error` or `boost::system::errc` (but never
557 * would-block), indicating the underlying transport is hosed for that specific reason, as detected during
558 * outgoing-direction processing.
559 * @return See above.
560 */
561 bool send_native_handle(Native_handle hndl_or_null, const util::Blob_const& meta_blob,
562 Error_code* err_code = nullptr);
563
564 /**
565 * Implements Blob_sender API per contract. Reminder: You may call this directly from within a
566 * completion handler you supplied to an earlier `async_receive_*()`. Reminder: It's not thread-safe
567 * to call this concurrently with other transmission methods on the same `*this`.
568 *
569 * Reminder: `blob.size() == 0` results in undefined behavior (assertion may trip).
570 *
571 * @param blob
572 * See above. Reminder: The memory area described by this arg need only be valid until this
573 * method returns. Perf reminder: That area will not be copied except for very rare circumstances.
574 * @param err_code
575 * See above. Reminder: In rare circumstances, an error emitted here may represent something
576 * detected during handling of a *preceding* send_blob() call but after it returned.
577 * #Error_code generated: see send_native_handle().
578 * @return See above.
579 */
580 bool send_blob(const util::Blob_const& blob, Error_code* err_code = nullptr);
581
582 /**
583 * Implements Native_handle_sender, Blob_sender API per contract. Reminder: You may call this directly from within a
584 * completion handler you supplied to an earlier `async_receive_*()`.
585 * Reminder: It's not thread-safe to call this concurrently with other transmission methods on
586 * the same `*this`.
587 *
588 * #Error_code generated and passed to `on_done_func()`:
589 * error::Code::S_LOW_LVL_TRANSPORT_HOSED_CANNOT_SEND (same meaning as for send_native_handle()/send_blob()),
590 * `boost::asio::error::eof` (ditto),
591 * other system codes most likely from `boost::asio::error` or `boost::system::errc` (ditto),
592 * error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER (destructor called, canceling all pending ops;
593 * spiritually identical to `boost::asio::error::operation_aborted`).
594 *
595 * Reminder: In rare circumstances, an error emitted there may represent something
596 * detected during handling of a preceding send_native_handle() or send_blob() call but after it returned.
597 *
598 * @tparam Task_err
599 * See above.
600 * @param on_done_func
601 * See above.
602 * @return See above. Reminder: If and only if it returns `false`, we're not in PEER state, or `*end_sending()` has
603 * already been called; and `on_done_func()` will never be called, nor will an error be emitted.
604 */
605 template<typename Task_err>
606 bool async_end_sending(Task_err&& on_done_func);
607
608 /**
609 * Implements Native_handle_sender, Blob_sender API per contract. Reminder: It is equivalent to async_end_sending()
610 * but with a no-op `on_done_func`.
611 *
612 * @return See async_end_sending().
613 */
614 bool end_sending();
615
616 /**
617 * Implements Native_handle_sender, Blob_sender API per contract.
618 *
619 * @param period
620 * See above.
621 * @return See above.
622 */
623 bool auto_ping(util::Fine_duration period = boost::chrono::seconds{2});
624
625 /**
626 * Implements Blob_sender API per contract.
627 *
628 * If not in PEER state returns a zeroed-out stats object.
629 *
630 * Thread-safe: can be called concurrently with any other method including `send_*()`.
631 * The returned copy is a consistent snapshot.
632 *
633 * @return Stats snapshot by value.
634 */
636
637 /**
638 * Implements Blob_sender API per contract.
639 *
640 * If not in PEER state this is a no-op.
641 *
642 * Thread-safe: can be called concurrently with any other method including `send_*()`.
643 */
645
646 /**
647 * Returns the same stats as blob_send_stats(): since this transport uses a single Unix-domain-socket
648 * pipe for both blobs and native handles, the statistics are identical.
649 *
650 * Implements Native_handle_sender API per contract.
651 *
652 * Thread-safe: can be called concurrently with any other method including `send_*()`.
653 * The returned copy is a consistent snapshot.
654 *
655 * @return Stats snapshot by value.
656 */
658
659 /**
660 * Equivalent to blob_send_stats_reset(); see blob_send_stats() rationale.
661 *
662 * Implements Native_handle_sender API per contract.
663 *
664 * Thread-safe: can be called concurrently with any other method including `send_*()`.
665 */
667
668 // Receive-ops API.
669
670 /**
671 * Implements Native_handle_receiver API per contract. Note this value equals receive_blob_max_size() at any given
672 * time which is *not* a concept requirement.
673 *
674 * @return See above.
675 */
676 size_t receive_meta_blob_max_size() const;
677
678 /**
679 * Implements Blob_receiver API per contract. Note this value equals receive_meta_blob_max_size() at any given
680 * time which is *not* a concept requirement.
681 *
682 * @return See above.
683 */
684 size_t receive_blob_max_size() const;
685
686 /**
687 * Implements Native_handle_receiver API per contract. Reminder: You may call this directly from within a
688 * completion handler you supplied to an earlier `async_receive_*()`. Reminder: It's not thread-safe
689 * to call this concurrently with other transmission methods on the same `*this`.
690 *
691 * #Error_code generated and passed to `on_done_func()`:
692 * error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER (destructor called, canceling all pending ops;
693 * spiritually identical to `boost::asio::error::operation_aborted`),
694 * error::Code::S_MESSAGE_SIZE_EXCEEDS_USER_STORAGE (opposing peer has sent a message with a meta-blob exceeding
695 * `target_meta_blob.size()` in length; in particular one can give an empty buffer if no meta-blob expected),
696 * error::Code::S_RECEIVES_FINISHED_CANNOT_RECEIVE (peer gracefully closed pipe via `*end_sending()`),
697 * error::Code::S_LOW_LVL_TRANSPORT_HOSED_CANNOT_RECEIVE (the outgoing-direction processing detected
698 * that the underlying transport is hosed; specific code was logged and can be obtained via send_native_handle()),
699 * `boost::asio::error::eof` (underlying transport hosed due to graceful closing by the other side),
700 * other system codes most likely from `boost::asio::error` or `boost::system::errc` (but never
701 * would-block), indicating the underlying transport is hosed for that specific reason, as detected during
702 * incoming-direction processing.
703 *
704 * error::Code::S_INVALID_ARGUMENT shall never be emitted due to `target_meta_blob.size()` (or as of this writing
705 * for any other reason). See #S_META_BLOB_UNDERFLOW_ALLOWED.
706 *
707 * @tparam Task_err_sz
708 * See above.
709 * @param target_hndl
710 * See above.
711 * @param target_meta_blob
712 * See above. Reminder: The memory area described by this arg must be valid up to
713 * completion handler entry.
714 * @param on_done_func
715 * See above.
716 * @return See above.
717 */
718 template<typename Task_err_sz>
719 bool async_receive_native_handle(Native_handle* target_hndl, const util::Blob_mutable& target_meta_blob,
720 Task_err_sz&& on_done_func);
721
722 /**
723 * Implements Blob_receiver API per contract. Reminder: You may call this directly from within a
724 * completion handler you supplied to an earlier `async_receive_*()`. Reminder: It's not thread-safe
725 * to call this concurrently with other transmission methods on the same `*this`.
726 *
727 * #Error_code generated and passed to `on_done_func()`: see async_receive_native_handle().
728 * In addition: error::Code::S_BLOB_RECEIVER_GOT_NON_BLOB (opposing peer seems to have used a
729 * Native_socket_stream::send_native_handle() call, which they shouldn't in the first place, and supplied
730 * a non-null #Native_handle, which this Blob_receiver cannot accept).
731 *
732 * @tparam Task_err_sz
733 * See above.
734 * @param target_blob
735 * See above. Reminder: The memory area described by this arg must be valid up to
736 * completion handler entry.
737 * @param on_done_func
738 * See above.
739 * @return See above.
740 */
741 template<typename Task_err_sz>
742 bool async_receive_blob(const util::Blob_mutable& target_blob, Task_err_sz&& on_done_func);
743
744 /**
745 * Implements Native_handle_receiver API per contract. Reminder: You may call this directly from within a
746 * completion handler you supplied to an earlier `async_receive_*()`. Reminder: It's not thread-safe
747 * to call this concurrently with other transmission methods on the same `*this`.
748 *
749 * #Error_code generated and passed to `on_done_func()`: see async_receive_native_handle(),
750 * except error::Code::S_MESSAGE_SIZE_EXCEEDS_USER_STORAGE applies to `batch->target_payload_size()`; and
751 * additionally:
752 * error::Code::S_INVALID_ARGUMENT (if `!batch->initialized()` or `batch->full()`).
753 *
754 * @tparam Msg_resource
755 * See above.
756 * @tparam Task_err
757 * See above.
758 * @param batch
759 * See above.
760 * @param assume_would_block
761 * See above.
762 * @param on_done_func
763 * See above.
764 * @return See above.
765 */
766 template<typename Msg_resource, typename Task_err>
767 bool async_receive_native_handle_batch(Native_handle_batch_in<Msg_resource>* batch, bool assume_would_block,
768 Task_err&& on_done_func);
769
770 /**
771 * Implements Blob_receiver API per contract. Reminder: You may call this directly from within a
772 * completion handler you supplied to an earlier `async_receive_*()`. Reminder: It's not thread-safe
773 * to call this concurrently with other transmission methods on the same `*this`.
774 *
775 * #Error_code generated and passed to `on_done_func()`: see async_receive_native_handle_batch().
776 * In addition: error::Code::S_BLOB_RECEIVER_GOT_NON_BLOB (opposing peer seems to have used a
777 * Native_socket_stream::send_native_handle() call, which they shouldn't in the first place, and supplied
778 * a non-null #Native_handle, which this Blob_receiver cannot accept).
779 *
780 * @tparam Msg_resource
781 * See above.
782 * @tparam Task_err
783 * See above.
784 * @param batch
785 * See above.
786 * @param assume_would_block
787 * See above.
788 * @param on_done_func
789 * See above.
790 * @return See above.
791 */
792 template<typename Msg_resource, typename Task_err>
793 bool async_receive_blob_batch(Blob_batch_in<Msg_resource>* batch, bool assume_would_block, Task_err&& on_done_func);
794
795 /**
796 * Implements Native_handle_receiver, Blob_receiver API per contract.
797 *
798 * @param timeout
799 * See above.
800 * @return See above.
801 */
802 bool idle_timer_run(util::Fine_duration timeout = boost::chrono::seconds{5});
803
804 /**
805 * Implements Blob_receiver API per contract.
806 *
807 * If not in PEER state returns a zeroed-out stats object.
808 *
809 * Thread-safe: can be called concurrently with any other method including `async_receive_*()`.
810 * The returned copy is a consistent snapshot.
811 *
812 * @see Native_socket_stream_msg_batch_in keeps additional per-batch receive stats orthogonal to these.
813 *
814 * @return Stats snapshot by value.
815 */
817
818 /**
819 * Implements Blob_receiver API per contract.
820 *
821 * If not in PEER state this is a no-op.
822 *
823 * Thread-safe: can be called concurrently with any other method including `async_receive_*()`.
824 */
826
827 /**
828 * Returns the same stats as blob_receive_stats(): since this transport uses a single Unix-domain-socket
829 * pipe for both blobs and native handles, the statistics are identical.
830 *
831 * Implements Native_handle_receiver API per contract.
832 *
833 * Thread-safe: can be called concurrently with any other method including `async_receive_*()`.
834 * The returned copy is a consistent snapshot.
835 *
836 * @return Stats snapshot by value.
837 */
839
840 /**
841 * Equivalent to blob_receive_stats_reset(); see blob_receive_stats() rationale.
842 *
843 * Implements Native_handle_receiver API per contract.
844 *
845 * Thread-safe: can be called concurrently with any other method including `async_receive_*()`.
846 */
848
849 // Misc API.
850
851 /**
852 * OS-reported process credential (PID, etc.) info about the *other* connected peer's process, at the time
853 * that the OS first established (via local-socket-connect or local-socket-connected-pair-generate call) that
854 * opposing peer socket -- unless overridden via mutator (please read here).
855 *
856 * @see session::Session::remote_peer_process_credentials() offers centralized, caveat-free access to similar info.
857 * It is however higher-level and requires the use of ipc::session infrastructure.
858 * struc::Channel::remote_peer_process_credentials() may also be useful.
859 *
860 * ### Exact semantics ###
861 * If one is not careful, it is possible to get in hard-to-realize trouble with this accessor. Here are the
862 * exact semantics; then we'll briefly discuss their implications.
863 *
864 * - If called in non-PEER state, returns util::NULL_PROCESS_CREDENTIALS but emits no error.
865 * - If called in PEER state, but incoming/outgoing-direction processing earlier detected
866 * that the underlying transport is hosed, returns `NULL_PROCESS_CREDENTIALS` (unless throws) and emits
867 * error::Code::S_LOW_LVL_TRANSPORT_HOSED.
868 * - As of this writing no other errors are possible.
869 * - Otherwise (main case):
870 * - If remote_peer_process_credentials() *mutator* has been called, returns copy of the value given to that
871 * mutator last. Otherwise:
872 * - Returns the values accurate at the time that the connection was originally established. To wit:
873 * - If `*this` established connection via sync_connect() (or equivalent such as via native OS-connect):
874 * returns the opposing process's info saved at the moment of successful OS-connect.
875 * - If `*this` established connection via Native_socket_stream_acceptor::async_accept() (or equivalent such
876 * as via native OS-accept):
877 * returns the opposing process's info saved at the moment of successful OS-connect (background-accept op
878 * subsequent to native OS-listen).
879 * - If `*this` established connection via boost.asio `connect_pair()` (or equivalent such as via native OS
880 * `socketpair()`): returns the info for the process that executed OS-socket-pair, at the time that
881 * it was called.
882 *
883 * ### Rationale/implications ###
884 * In the *main case* path above: All of that is reasonable and as one would expect, with the exception of the very
885 * last bullet point, because typically (not always) one would then transmit one of the handles (FDs) returned by
886 * OS-socket-pair call to another process (over yet another Native_socket_stream) and then communicate over
887 * that channel. In particular `ipc::session`-created `Channel`s internally operate that way. In that case
888 * one might invoke remote_peer_process_credentials() and expect the PID/etc. of the opposing process; but that
889 * will be accurate only for the guy who received the handle/FD; whereas for the guy who sent it this method
890 * will return that guy's own PID... unhelpful, usually.
891 *
892 * The following are the key implications/corollaries of that.
893 * - You should be aware of it, instead of assuming "opposing peer" means what you think it means.
894 * - If you'd like to override this behavior, when it is inconvenient, use the mutator
895 * remote_peer_process_credentials() (as shown above, those values will then be returned). You would need
896 * the correct values to pass to it, but these are usually available from the socket-stream used to
897 * transmit the FD. Namely one would perhaps call *that* guy's `.remote_peer_process_credentials()`.
898 * - The `Channel`s returned by ipc::session already internally use this mechanism. That is
899 * Channel::remote_peer_process_credentials() will yield the actual opposing process's stuff, assuming
900 * an ipc::session::Session or variant generated the Channel. (If you create the `Channel` yourself, then
901 * it is up to you to use the mutator to correct for this yourself.)
902 *
903 * @return See above; or util::NULL_PROCESS_CREDENTIALS if invoked outside of PEER state or in case of error.
904 * The 2 eventualities can be distinguished by checking `*err_code` truthiness. Better yet
905 * only call remote_peer_process_credentials() in PEER state, as it is otherwise conceptually meaningless.
906 *
907 * @param err_code
908 * See `flow::Error_code` docs for error reporting semantics. #Error_code generated:
909 * error::Code::S_LOW_LVL_TRANSPORT_HOSED (the incoming/outgoing-direction processing detected
910 * that the underlying transport is hosed; specific code was logged and can be obtained via
911 * async_receive_native_handle() or similar).
912 */
914
915 /**
916 * Overrides what same-named accessor shall return subsequently; useful when a socket-stream endpoint travels
917 * to another process relative to where the connection was first established.
918 *
919 * After calling this mutator, the same-named accessor shall return a value equal to `creds`, unless it yields
920 * `{}` (error case).
921 *
922 * @see doc header for remote_peer_process_credentials() accessor for important relevant information.
923 *
924 * @param creds
925 * The value to save.
926 * @return `true` if called in PEER state (`creds` was saved);
927 * `false` if called in another state (`creds` was not saved).
928 */
930
931private:
932 // Types.
933
934 /// Short-hand for `const`-respecting wrapper around Native_socket_stream_impl for the pImpl-lite idiom.
935 using Impl_ptr = std::experimental::propagate_const<boost::movelib::unique_ptr<Impl>>;
936
937 // Friends.
938
939 /// Friend of Native_socket_stream.
940 friend std::ostream& operator<<(std::ostream& os, const Native_socket_stream& val);
941
942 // Methods.
943
944 /**
945 * Helper that simply returns #m_impl while guaranteeing that #m_impl is non-null upon return. All
946 * forwarding-to-#m_impl methods (including `const` ones) shall access #m_impl through this impl() method only.
947 *
948 * ### Design/rationale ###
949 * It returns #m_impl... but if it is null, then it is first re-initialized to a new default-cted
950 * Native_socket_stream_impl. That is:
951 * - Any ctor will make #m_impl non-null.
952 * - But moving-from `*this` will make #m_impl null.
953 * - However *if* the user were to invoke a public method of `*this` after the latter, then that public method
954 * shall invoke impl() which shall make #m_impl non-null again, which will ensure the public method works fine.
955 *
956 * Why? Answer: This is the usual question of what to do with null m_impl (e.g., it's mentioned in cppreference.com
957 * pImpl page) which occurs when one moves-from a given object. There are various possibilities; but in our case we
958 * do want to enable regular use of a moved-from object; hence we promised that a moved-from object is simply in
959 * NULL (not-connected) state, as-if default-cted. So we do that lazily (on-demand) on encountering
960 * null m_impl. Of course we could do it directly inside move ctor/assignment (non-lazily), but in many cases such
961 * objects are abandoned in practice, so it's best not to start threads, etc., unless really desired. The
962 * null check cost is minor.
963 *
964 * Caveat: To be used by `const` methods this method must be `const`; but it must at times modify #m_impl
965 * (the pointer... not the pointee). For that reason #m_impl must be `mutable`. That is fine: `mutable` for
966 * lazy evaluation is a vanilla pattern.
967 *
968 * @return Reference to #m_impl.
969 */
970 Impl_ptr& impl() const;
971
972 // Data.
973
974 /**
975 * The true implementation of this class. See also our class doc header; and impl() (in particular explaining
976 * why this is `mutable`).
977 *
978 * Do not access directly but only via impl().
979 */
981}; // class Native_socket_stream
982
983// Free functions: in *_fwd.hpp.
984
985/* Template implementations.
986 * It is forwarding to impl() (essentially m_impl) a-la pImpl (but pImpl-lite... we have templates... while
987 * most of these can be de-templatized by using Function<> to maintain strict pImpl, the *_batch() guys cannot
988 * due to `typename Msg_resource`). */
989
990template<typename Task_err>
991bool Native_socket_stream::async_end_sending(Task_err&& on_done_func)
992{
993 return impl()->async_end_sending(std::move(on_done_func));
994}
995
996template<typename Task_err_sz>
998 const util::Blob_mutable& target_meta_blob,
999 Task_err_sz&& on_done_func)
1000{
1001 /* Perf note: In all cases, as of this writing, Native_socket_stream_impl would wrap the various handler
1002 * parameterized args in concrete Function<>s anyway for its own impl ease; so we change nothing by
1003 * (implicitly, if needed) constructing flow::async::Task_asio_err_sz higher up in the call stack in this
1004 * template and its siblings below. */
1005
1006 return impl()->async_receive_native_handle(target_hndl, target_meta_blob, std::move(on_done_func));
1007}
1008
1009template<typename Task_err_sz>
1010bool Native_socket_stream::async_receive_blob(const util::Blob_mutable& target_blob, Task_err_sz&& on_done_func)
1011{
1012 return impl()->async_receive_blob(target_blob, std::move(on_done_func));
1013}
1014
1015template<typename Msg_resource, typename Task_err>
1017 bool assume_would_block, Task_err&& on_done_func)
1018{
1019 return impl()->async_receive_native_handle_batch<Msg_resource>(batch, assume_would_block, std::move(on_done_func));
1020}
1021
1022template<typename Msg_resource, typename Task_err>
1024 bool assume_would_block, Task_err&& on_done_func)
1025{
1026 return impl()->async_receive_blob_batch<Msg_resource>(batch, assume_would_block, std::move(on_done_func));
1027}
1028
1029} // namespace ipc::transport
Internal, non-movable pImpl-lite implementation of Native_socket_stream class.
Implements both Native_handle_sender and Native_handle_receiver concepts by using a Unix domain socke...
Blob_rcv_stats native_handle_receive_stats() const
Returns the same stats as blob_receive_stats(): since this transport uses a single Unix-domain-socket...
Impl_ptr & impl() const
Helper that simply returns m_impl while guaranteeing that m_impl is non-null upon return.
Native_socket_stream(const Native_socket_stream &)=delete
Copy construction is disallowed.
Native_socket_stream & operator=(const Native_socket_stream &)=delete
Copy assignment is disallowed.
size_t send_blob_max_size() const
Implements Blob_sender API per contract.
bool async_receive_native_handle_batch(Native_handle_batch_in< Msg_resource > *batch, bool assume_would_block, Task_err &&on_done_func)
Implements Native_handle_receiver API per contract.
~Native_socket_stream()
Implements Native_handle_sender and Native_handle_receiver APIs at the same time, per their concept c...
const std::string & nickname() const
Returns nickname, a brief string suitable for logging.
bool async_receive_native_handle(Native_handle *target_hndl, const util::Blob_mutable &target_meta_blob, Task_err_sz &&on_done_func)
Implements Native_handle_receiver API per contract.
Native_socket_stream(Native_socket_stream &&src)
Move-constructs from src; src becomes as-if default-cted (therefore in NULL state).
bool sync_connect(const Shared_name &absolute_name, Error_code *err_code=nullptr)
To be invoked in NULL state only, it synchronously and non-blockingly attempts to connect to an oppos...
static constexpr bool S_BLOB_UNDERFLOW_ALLOWED
Implements concept API; namely it is true: async_receive_blob() with smaller-than-receive_blob_max_si...
Blob_snd_stats native_handle_send_stats() const
Returns the same stats as blob_send_stats(): since this transport uses a single Unix-domain-socket pi...
static const Shared_name & S_RESOURCE_TYPE_ID
Implements concept API.
void native_handle_receive_stats_reset()
Equivalent to blob_receive_stats_reset(); see blob_receive_stats() rationale.
bool async_receive_blob_batch(Blob_batch_in< Msg_resource > *batch, bool assume_would_block, Task_err &&on_done_func)
Implements Blob_receiver API per contract.
transport::stat::Blob_rcv_stats Blob_rcv_stats
Implements Blob_receiver concept API.
bool idle_timer_run(util::Fine_duration timeout=boost::chrono::seconds{5})
Implements Native_handle_receiver, Blob_receiver API per contract.
transport::stat::Blob_snd_stats Blob_snd_stats
Implements Blob_sender concept API.
bool send_native_handle(Native_handle hndl_or_null, const util::Blob_const &meta_blob, Error_code *err_code=nullptr)
Implements Native_handle_sender API per contract.
void blob_receive_stats_reset()
Implements Blob_receiver API per contract.
size_t send_meta_blob_max_size() const
Implements Native_handle_sender API per contract.
static constexpr bool S_META_BLOB_UNDERFLOW_ALLOWED
Implements concept API; namely it is true: async_receive_native_handle() with smaller-than-receive_me...
void blob_send_stats_reset()
Implements Blob_sender API per contract.
bool auto_ping(util::Fine_duration period=boost::chrono::seconds{2})
Implements Native_handle_sender, Blob_sender API per contract.
void native_handle_send_stats_reset()
Equivalent to blob_send_stats_reset(); see blob_send_stats() rationale.
const util::Process_credentials & remote_peer_process_credentials(Error_code *err_code=nullptr) const
OS-reported process credential (PID, etc.) info about the other connected peer's process,...
Native_socket_stream()
Default ctor (object is in NULL state).
std::experimental::propagate_const< boost::movelib::unique_ptr< Impl > > Impl_ptr
Short-hand for const-respecting wrapper around Native_socket_stream_impl for the pImpl-lite idiom.
Impl_ptr m_impl
The true implementation of this class.
bool async_end_sending(Task_err &&on_done_func)
Implements Native_handle_sender, Blob_sender API per contract.
bool async_receive_blob(const util::Blob_mutable &target_blob, Task_err_sz &&on_done_func)
Implements Blob_receiver API per contract.
friend std::ostream & operator<<(std::ostream &os, const Native_socket_stream &val)
Friend of Native_socket_stream.
size_t receive_meta_blob_max_size() const
Implements Native_handle_receiver API per contract.
bool end_sending()
Implements Native_handle_sender, Blob_sender API per contract.
Blob_snd_stats blob_send_stats() const
Implements Blob_sender API per contract.
size_t receive_blob_max_size() const
Implements Blob_receiver API per contract.
typename Impl::template Native_handle_batch_in< Msg_resource > Native_handle_batch_in
Implements Native_handle_receiver concept API.
typename Impl::template Blob_batch_in< Msg_resource > Blob_batch_in
Implements Blob_receiver concept API.
static constexpr size_t S_RCV_NATIVE_HANDLE_BATCH_SZ_RECOMMENDATION
Implements concept API.
static constexpr size_t S_RCV_BLOB_BATCH_SZ_RECOMMENDATION
Implements concept API. Same notes as for S_RCV_NATIVE_HANDLE_BATCH_SZ_RECOMMENDATION; the values are...
Native_socket_stream & operator=(Native_socket_stream &&src)
Move-assigns from src; *this acts as if destructed; src becomes as-if default-cted (therefore in NULL...
bool send_blob(const util::Blob_const &blob, Error_code *err_code=nullptr)
Implements Blob_sender API per contract.
Blob_rcv_stats blob_receive_stats() const
Implements Blob_receiver API per contract.
Dummy type for use as a template param to Channel when either the blobs pipe or handles pipe is disab...
Definition: channel.hpp:1137
Implements both sync_io::Native_handle_sender and sync_io::Native_handle_receiver concepts by using a...
static constexpr size_t S_RCV_NATIVE_HANDLE_BATCH_SZ_RECOMMENDATION
Implements concept API.
static constexpr size_t S_RCV_BLOB_BATCH_SZ_RECOMMENDATION
Implements concept API.
A process's credentials (PID, UID, GID as of this writing).
String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent entity fr...
Flow-IPC module providing transmission of structured messages and/or low-level blobs (and more) betwe...
boost::asio::mutable_buffer Blob_mutable
Short-hand for a mutable blob somewhere in memory, stored as exactly a void* and a size_t.
Definition: util_fwd.hpp:137
flow::Fine_duration Fine_duration
Short-hand for Flow's Fine_duration.
Definition: util_fwd.hpp:114
boost::asio::const_buffer Blob_const
Short-hand for an immutable blob somewhere in memory, stored as exactly a void const * and a size_t.
Definition: util_fwd.hpp:131
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
Cumulative stats from the incoming-direction (receive) pipe of a Blob_receiver or Native_handle_recei...
Cumulative stats from the outgoing-direction (send) pipe of a Blob_sender or Native_handle_sender con...
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.