Flow-IPC 3.0.0
Flow-IPC project: Full implementation reference.
blob_stream_mq_rcv.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
24#include <boost/move/make_unique.hpp>
25#include <experimental/propagate_const>
26#include <ostream>
27#include <string>
28#include <utility>
29
30namespace ipc::transport
31{
32
33// Types.
34
35/**
36 * Implements Blob_receiver concept by using an adopted Persistent_mq_handle MQ handle to an MQ (message queue)
37 * of that type, such as a POSIX or bipc MQ. This allows for high-performance, potentially zero-copy (except
38 * for copying out of the transport MQ) of discrete messages, each containing a binary blob.
39 * This is a low-level (core) transport mechanism; higher-level (structured)
40 * transport mechanisms may use Blob_stream_mq_receiver (and Blob_stream_mq_sender) to enable their work.
41 *
42 * Beyond that: see Blob_stream_mq_sender doc header. We are just the other side of this.
43 *
44 * ### Cleanup; reusing same absolute_name() ###
45 * See Blob_stream_mq_sender doc header. The same applies symmetrically.
46 *
47 * ### Performance, responsiveness ###
48 * See Blob_stream_mq_sender doc header. The same applies symmetrically. Be aware specifically that the same
49 * responsiveness-of-destructor caveat applies to Blob_stream_mq_receiver.
50 *
51 * ### Thread safety ###
52 * We add no more thread safety guarantees than those mandated by the main concepts. To wit:
53 * - Concurrent access to 2 separate Blob_stream_mq_receiver objects is safe.
54 * - After construction, concurrent access to the main transmission API methods and the dtor is not safe for a given
55 * `*this`.
56 *
57 * Methods and situations not covered by that text should have their thread safety explained in their individual doc
58 * headers.
59 *
60 * @internal
61 * ### Implementation design/rationale ###
62 * See pImpl-lite notes in Blob_stream_mq_sender doc header. The exact same applies here.
63 *
64 * @see Blob_stream_mq_receiver_impl doc header.
65 *
66 * @endinternal
67 *
68 * @tparam Persistent_mq_handle
69 * See Persistent_mq_handle concept doc header.
70 *
71 * @see Blob_receiver: implemented concept.
72 */
73template<typename Persistent_mq_handle>
75 public Blob_stream_mq_base<Persistent_mq_handle>,
77{
78private:
79 // Types.
80
81 /// Short-hand for the impl type we're wrapping. Cannot simply forward-declare as in pImpl; we do pImpl-lite.
83
84public:
85 // Types.
86
87 /// Short-hand for template arg for underlying MQ handle type.
88 using Mq = typename Impl::Mq;
89
90 /// Useful for generic programming, the `sync_io`-pattern counterpart to `*this` type.
92 /// You may disregard.
94
95 /// Implements Blob_receiver concept API.
96 template<typename Msg_resource>
98
99 /// Implements Blob_receiver concept API.
101
102 // Constants.
103
104 /// Implements concept API. Equals `Mq::S_RESOURCE_TYPE_ID`.
106
107 /**
108 * Implements concept API; namely it is `false`.
109 *
110 * @see Native_handle_receiver::S_BLOB_UNDERFLOW_ALLOWED: implemented concept. Accordingly also see
111 * "Blob underflow semantics" in transport::Native_handle_receiver doc header.
112 */
113 static constexpr bool S_BLOB_UNDERFLOW_ALLOWED = false;
114
115 /**
116 * Implements concept API; namely it is `1` as of this writing (no native batch-receiving in
117 * Persistent_mq_handle concept or known impls' OS facilities => little to no
118 * benefit from batching; possibly slight cost instead).
119 */
121
122 // Constructors/destructor.
123
124 /**
125 * Constructs the receiver by taking over an already-opened MQ handle.
126 * Note that this op does not implement any concept; Blob_receiver concept does not define how a Blob_receiver
127 * is created in this explicit fashion.
128 *
129 * No traffic must have occurred on `mq_moved` up to this call. Otherwise behavior is undefined.
130 *
131 * If this fails (sets `*err_code` to truthy if not null; throws if null), all transmission calls on `*this`
132 * will fail with the post-value in `*err_code` emitted. In particular, error::Code::S_BLOB_STREAM_MQ_RECEIVER_EXISTS
133 * is that code, if the reason for failure was that another Blob_stream_mq_receiver to
134 * `mq_moved.absolute_name()` has already been created in this or other process. See Blob_stream_mq_sender class
135 * doc header for discussion of the relationship between Blob_stream_mq_sender, Blob_stream_mq_receiver, the
136 * underlying MQ at a given Shared_name, and that Shared_name as registered in the OS.
137 * In short: there is to be up to 1 Blob_stream_mq_sender and up to 1 Blob_stream_mq_receiver for a given
138 * named persistent MQ. In this way, it is one single-direction pipe with 2 peers, like half of
139 * Native_socket_stream pipe: it is not a MQ with
140 * back-and-forth traffic nor multiple senders or multiple receivers. The underlying MQ supports such things;
141 * but that is not what the Blob_sender/Blob_receiver concepts model.
142 *
143 * Along those same lines note that the dtor (at the latest -- which happens if no fatal error occurs throughout)
144 * will not only close the MQ handle acquired from `mq_moved` but will execute `Mq::remove_persistent(name)`,
145 * where `name == mq_moved.absolute_name()` pre-this-ctor.
146 *
147 * ### Leaks of persistent resources ###
148 * Notes from Blob_stream_mq_sender ctor doc header apply here.
149 *
150 * ### Performance ###
151 * The taking over of `mq_moved` should be thought of as light-weight.
152 *
153 * @param logger_ptr
154 * Logger to use for subsequently logging.
155 * @param mq_moved
156 * An MQ handle to an MQ with no traffic on it so far. Unless an error is emitted, `mq_moved` becomes
157 * nullified upon return from this ctor. `*this` owns the MQ handle from this point on and is reponsible
158 * for closing it.
159 * @param nickname_str
160 * Human-readable nickname of the new object, as of this writing for use in `operator<<(ostream)` and
161 * logging only.
162 * @param err_code
163 * See `flow::Error_code` docs for error reporting semantics. #Error_code generated:
164 * error::Code::S_BLOB_STREAM_MQ_RECEIVER_EXISTS (another Blob_stream_mq_receiver exists),
165 * system codes (other errors, all to do with the creation of a separate internally used tiny SHM pool
166 * used to prevent duplicate Blob_stream_mq_receiver in the system).
167 */
168 explicit Blob_stream_mq_receiver(flow::log::Logger* logger_ptr, util::String_view nickname_str,
169 Mq&& mq_moved, Error_code* err_code = nullptr);
170
171 /**
172 * Implements Blob_receiver API, per its concept contract.
173 * All the notes for that concept's core-adopting ctor apply.
174 *
175 * @param sync_io_core_in_peer_state_moved
176 * See above.
177 */
178 explicit Blob_stream_mq_receiver(Sync_io_obj&& sync_io_core_in_peer_state_moved);
179
180 /**
181 * Implements Blob_receiver API, per its concept contract.
182 * All the notes for that concept's default ctor apply.
183 */
185
186 /**
187 * Move-constructs from `src`; `src` becomes as-if default-cted (therefore in NULL state).
188 * Implements Blob_receiver API, per its concept contract.
189 *
190 * @param src
191 * See above.
192 */
194
195 /**
196 * Implements Blob_receiver API. All the notes for the concept's destructor apply but as a reminder:
197 *
198 * Destroys this peer endpoint which will end the one-direction pipe and cancel any pending
199 * completion handlers by invoking it ASAP with error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER.
200 * As of this writing these are the completion handlers that would therefore be called:
201 * - Any handler passed to async_receive_blob() that has not yet been invoked.
202 * There can be 0 or more of these.
203 *
204 * ### Fate of underlying MQ and its `.absolute_name()` ###
205 * Notes from Blob_stream_mq_sender dtor doc header apply here symmetrically.
206 */
208
209 // Methods.
210
211 /**
212 * Move-assigns from `src`; `*this` acts as if destructed; `src` becomes as-if default-cted (therefore in NULL state).
213 * No-op if `&src == this`.
214 * Implements Blob_receiver API, per its concept contract.
215 *
216 * @see ~Blob_stream_mq_receiver().
217 *
218 * @param src
219 * See above.
220 * @return `*this`.
221 */
223
224 /// Copy assignment is disallowed.
226
227 /**
228 * Implements Blob_receiver API per contract.
229 *
230 * @return See above.
231 */
232 size_t receive_blob_max_size() const;
233
234 /**
235 * Implements Blob_receiver API per contract. Reminder: You may call this directly from within a
236 * completion handler you supplied to an earlier async_receive_*(). Reminder: It's not thread-safe
237 * to call this concurrently with other transmission methods on the same `*this`.
238 *
239 * #Error_code generated and passed to `on_done_func()`:
240 * error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER (destructor called, canceling all pending ops;
241 * spiritually identical to `boost::asio::error::operation_aborted`),
242 * error::Code::S_INVALID_ARGUMENT (non-pipe-hosing error: `target_blob.size()` is smaller than
243 * Persistent_mq_handle::max_msg_size()),
244 * error::Code::S_RECEIVES_FINISHED_CANNOT_RECEIVE (peer gracefully closed pipe via `*end_sending()`),
245 * other system codes, indicating the underlying transport is hosed for that specific reason, as detected during
246 * incoming-direction processing.
247 *
248 * error::Code::S_MESSAGE_SIZE_EXCEEDS_USER_STORAGE shall never be emitted. See
249 * Blob_stream_mq_receiver::S_BLOB_UNDERFLOW_ALLOWED.
250 *
251 * @tparam Task_err_sz
252 * See above.
253 * @param target_blob
254 * See above. Reminder: The memory area described by this arg must be valid up to
255 * completion handler entry.
256 * @param on_done_func
257 * See above. Reminder: any moved/copied version of this callback's associated captured state will
258 * be freed soon after it returns.
259 * @return See above.
260 */
261 template<typename Task_err_sz>
262 bool async_receive_blob(const util::Blob_mutable& target_blob, Task_err_sz&& on_done_func);
263
264 /**
265 * Implements Blob_receiver API per contract. Reminder: You may call this directly from within a
266 * completion handler you supplied to an earlier `async_receive_*()`. Reminder: It's not thread-safe
267 * to call this concurrently with other transmission methods on the same `*this`.
268 *
269 * #Error_code generated and passed to `on_done_func()`: see async_receive_blob(),
270 * except error::Code::S_INVALID_ARGUMENT applies to `batch->target_payload_size()` instead of `target_blob.size()`;
271 * and also emitted if `!batch->initialized()` or `batch->full()`.
272 *
273 * @tparam Msg_resource
274 * See above.
275 * @tparam Task_err
276 * See above.
277 * @param batch
278 * See above.
279 * @param assume_would_block
280 * See above.
281 * @param on_done_func
282 * See above.
283 * @return See above.
284 */
285 template<typename Msg_resource, typename Task_err>
286 bool async_receive_blob_batch(Blob_batch_in<Msg_resource>* batch, bool assume_would_block, Task_err&& on_done_func);
287
288 /**
289 * Implements Blob_receiver API per contract. Reminder: You may call this directly from within a
290 * completion handler you supplied to an earlier async_receive_blob().
291 *
292 * @param timeout
293 * See above.
294 * @return See above.
295 */
296 bool idle_timer_run(util::Fine_duration timeout = boost::chrono::seconds{5});
297
298 /**
299 * Implements Blob_receiver API per contract.
300 *
301 * Thread-safe: can be called concurrently with any other method including `async_receive_*()`.
302 * The returned copy is a consistent snapshot.
303 *
304 * @return Stats snapshot by value.
305 */
307
308 /**
309 * Implements Blob_receiver API per contract.
310 *
311 * Thread-safe: can be called concurrently with any other method including `async_receive_*()`.
312 */
314
315 /**
316 * Returns nickname, a brief string suitable for logging. This is included in the output by the `ostream<<`
317 * operator as well. This method is thread-safe in that it always returns the same value.
318 *
319 * If this object is default-cted (or moved-from), this will return a value equal to "".
320 *
321 * @return See above.
322 */
323 const std::string& nickname() const;
324
325 /**
326 * Returns name equal to `mq.absolute_name()`, where `mq` was passed to ctor, at the time it was passed to ctor.
327 *
328 * If this object is default-cted (or moved-from), this will return Shared_name::S_EMPTY.
329 *
330 * @return See above. Always the same value.
331 */
332 const Shared_name& absolute_name() const;
333
334private:
335 // Types.
336
337 /// Short-hand for `const`-respecting wrapper around #Impl for the pImpl idiom.
338 using Impl_ptr = std::experimental::propagate_const<boost::movelib::unique_ptr<Impl>>;
339
340 // Friends.
341
342 /// Friend of Blob_stream_mq_sender.
343 template<typename Persistent_mq_handle2>
344 friend std::ostream& operator<<(std::ostream& os, const Blob_stream_mq_receiver<Persistent_mq_handle2>& val);
345
346 // Methods.
347
348 // Data.
349
350 /// The true implementation of this class. See also our class doc header.
352}; // class Blob_stream_mq_receiver
353
354// Free functions: in *_fwd.hpp.
355
356// Template initializers.
357
358template<typename Persistent_mq_handle>
360
361// Template implementations (strict pImpl-idiom style (albeit pImpl-lite due to template-ness)).
362
363// The performant move semantics we get delightfully free with pImpl; they'll just move-to/from the unique_ptr m_impl.
364
365template<typename Persistent_mq_handle>
367template<typename Persistent_mq_handle>
369 (Blob_stream_mq_receiver&&) = default;
370
371// The NULL state ctor comports with how null m_impl is treated all over below.
372template<typename Persistent_mq_handle>
374
375// The rest is strict forwarding to m_impl, once PEER state is established (non-null m_impl).
376
377template<typename Persistent_mq_handle>
379 (flow::log::Logger* logger_ptr, util::String_view nickname_str, Mq&& mq, Error_code* err_code) :
380 m_impl(boost::movelib::make_unique<Impl>
381 (logger_ptr, nickname_str, std::move(mq), err_code))
382{
383 // Yay.
384}
385
386template<typename Persistent_mq_handle>
388
389 m_impl(boost::movelib::make_unique<Impl>
390 (std::move(sync_io_core_in_peer_state_moved)))
391{
392 // Yay.
393}
394
395// It's only explicitly defined to formally document it.
396template<typename Persistent_mq_handle>
398
399template<typename Persistent_mq_handle>
401{
402 return m_impl ? m_impl->receive_blob_max_size() : 0;
403}
404
405template<typename Persistent_mq_handle>
406template<typename Task_err_sz>
408 (const util::Blob_mutable& target_blob, Task_err_sz&& on_done_func)
409{
410 return m_impl ? (m_impl->async_receive_blob(target_blob, std::move(on_done_func)), true)
411 : false;
412}
413
414template<typename Persistent_mq_handle>
415template<typename Msg_resource, typename Task_err>
417 (Blob_batch_in<Msg_resource>* batch, bool assume_would_block, Task_err&& on_done_func)
418{
419 return m_impl ? (m_impl->template async_receive_blob_batch<Msg_resource>
420 (batch, assume_would_block, std::move(on_done_func)),
421 true)
422 : false;
423}
424
425
426template<typename Persistent_mq_handle>
428{
429 return m_impl ? m_impl->idle_timer_run(timeout)
430 : false;
431}
432
433template<typename Persistent_mq_handle>
435{
436 return m_impl ? m_impl->blob_receive_stats()
437 /* The histogram's structure will be based on a silly msg max size, and that is okay.
438 * Contract is struct is filled with zeroes, and that will hold. */
439 : Blob_rcv_stats{1};
440}
441
442template<typename Persistent_mq_handle>
444{
445 if (m_impl) { m_impl->blob_receive_stats_reset(); }
446}
447
448template<typename Persistent_mq_handle>
450{
451 return m_impl ? m_impl->absolute_name() : Shared_name::S_EMPTY;
452}
453
454template<typename Persistent_mq_handle>
456{
457 return m_impl ? m_impl->nickname() : util::EMPTY_STRING;
458}
459
460// `friend`ship needed for this "non-method method":
461
462template<typename Persistent_mq_handle>
463std::ostream& operator<<(std::ostream& os, const Blob_stream_mq_receiver<Persistent_mq_handle>& val)
464{
465 return val.m_impl ? (os << *val.m_impl) : (os << "null");
466}
467
468} // namespace ipc::transport
Base of Blob_stream_mq_sender and Blob_stream_mq_receiver containing certain static facilities,...
Similar to Blob_stream_mq_base but contains non-class-template-parameter-dependent Blob_stream_mq_rec...
Internal, non-movable pImpl-lite implementation of Blob_stream_mq_receiver class template.
typename Base::Mq Mq
Short-hand for template arg for underlying MQ handle type.
Implements Blob_receiver concept by using an adopted Persistent_mq_handle MQ handle to an MQ (message...
bool idle_timer_run(util::Fine_duration timeout=boost::chrono::seconds{5})
Implements Blob_receiver API per contract.
Impl_ptr m_impl
The true implementation of this class. See also our class doc header.
const std::string & nickname() const
Returns nickname, a brief string suitable for logging.
static constexpr bool S_BLOB_UNDERFLOW_ALLOWED
Implements concept API; namely it is false.
const Shared_name & absolute_name() const
Returns name equal to mq.absolute_name(), where mq was passed to ctor, at the time it was passed to c...
Blob_stream_mq_receiver & operator=(Blob_stream_mq_receiver &&src)
Move-assigns from src; *this acts as if destructed; src becomes as-if default-cted (therefore in NULL...
Blob_stream_mq_receiver()
Implements Blob_receiver API, per its concept contract.
std::experimental::propagate_const< boost::movelib::unique_ptr< Impl > > Impl_ptr
Short-hand for const-respecting wrapper around Impl for the pImpl idiom.
~Blob_stream_mq_receiver()
Implements Blob_receiver API.
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.
typename Impl::Mq Mq
Short-hand for template arg for underlying MQ handle type.
Blob_rcv_stats blob_receive_stats() const
Implements Blob_receiver API per contract.
size_t receive_blob_max_size() const
Implements Blob_receiver API per contract.
Blob_stream_mq_receiver(Blob_stream_mq_receiver &&src)
Move-constructs from src; src becomes as-if default-cted (therefore in NULL state).
static constexpr size_t S_RCV_BLOB_BATCH_SZ_RECOMMENDATION
Implements concept API; namely it is 1 as of this writing (no native batch-receiving in Persistent_mq...
void blob_receive_stats_reset()
Implements Blob_receiver API per contract.
Blob_stream_mq_receiver & operator=(const Blob_stream_mq_receiver &)=delete
Copy assignment is disallowed.
bool async_receive_blob(const util::Blob_mutable &target_blob, Task_err_sz &&on_done_func)
Implements Blob_receiver API per contract.
transport::stat::Blob_rcv_stats Blob_rcv_stats
Implements Blob_receiver concept API.
static const Shared_name S_RESOURCE_TYPE_ID
Implements concept API. Equals Mq::S_RESOURCE_TYPE_ID.
friend std::ostream & operator<<(std::ostream &os, const Blob_stream_mq_receiver< Persistent_mq_handle2 > &val)
Friend of Blob_stream_mq_sender.
Straightforward Msg_batch_in concept implementation (as taken by API {Blob|Native_handle}_receiver::a...
Definition: batch.hpp:88
Dummy type for use as a template param to Channel when either the blobs pipe or handles pipe is disab...
Definition: channel.hpp:1137
static constexpr size_t S_RCV_BLOB_BATCH_SZ_RECOMMENDATION
Implements concept API; namely it is 1 as of this writing (no native batch-receiving in Persistent_mq...
String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent entity fr...
static const Shared_name S_EMPTY
A (default-cted) Shared_name. May be useful for functions returning const Shared_name&.
Flow-IPC module providing transmission of structured messages and/or low-level blobs (and more) betwe...
std::ostream & operator<<(std::ostream &os, const Generic_msg_batch_in< Msg_resource_t, NO_HNDLS > &val)
Prints string representation of the given Generic_msg_batch_in to the given ostream.
Definition: batch.hpp:431
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
const std::string EMPTY_STRING
A (default-cted) string. May be useful for functions returning const std::string&.
Definition: util.cpp:39
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...