30#include <flow/error/error.hpp>
31#include <boost/container/list.hpp>
71template<
typename Shm_arena>
74 public flow::log::Log_context
129 using Segments_in_shm = boost::container::list<Segment_in_shm, Allocator<Segment_in_shm>>;
150 size_t segment_sz_init_pre_growth
151 =
sizeof(::capnp::word) * ::capnp::SUGGESTED_FIRST_SEGMENT_WORDS,
208 template<
typename Session_t,
typename Capnp_root_builder>
209 bool lend(Capnp_root_builder* capnp_root,
Session_t* shm_session);
228 kj::ArrayPtr<::capnp::word>
allocateSegment(
unsigned int min_sz_words)
override;
281template<
typename Shm_arena>
284 public flow::log::Log_context,
285 private boost::noncopyable
327 = boost::container::list<Segment_in_shm_borrowed, Borrower_allocator<Segment_in_shm_borrowed>>;
376 template<
typename Session_t,
typename Capnp_root_reader>
396 kj::ArrayPtr<const ::capnp::word>
getSegment(
unsigned int id)
override;
426template<
typename Shm_arena>
428 (flow::log::Logger* logger_ptr,
Arena* arena,
size_t segment_sz_init_pre_growth,
431 flow::log::Log_context(logger_ptr,
Log_component::S_TRANSPORT),
433 m_segment_sz(flow::util::round_to_multiple(segment_sz_init_pre_growth, sizeof(::
capnp::word))),
435 m_serialization_segments(m_arena->template construct<
Segments_in_shm>()),
438 m_saw_big_leaf(false)
440 FLOW_LOG_TRACE(
"SHM builder [" << *
this <<
"]: Created; input segment-size-pre-growth = "
441 "[" << segment_sz_init_pre_growth <<
"] bytes => rounded next-size = [" <<
m_segment_sz <<
"] bytes.");
444template<
typename Shm_arena>
447 using flow::util::stat::fetch_sub;
449 FLOW_LOG_TRACE(
"SHM builder [" << *
this <<
"]: Destroyed. The following may SHM-dealloc the serialization, "
450 "if recipient was done with it before us, or if we hadn't done lend() yet.");
462 if ((!m_stats) || m_serialization_segments->empty())
469 const auto prev_alloc_outstanding_sz =
471 fetch_sub(&m_stats->m_alloc_outstanding_sz, m_alloc_sz);
472 assert((prev_alloc_outstanding_sz >= m_alloc_sz)
473 &&
"GAUGE underflow: dtor's per-msg subtraction exceeds the cumulative outstanding.");
476 const auto prev_msgs_outstanding =
478 fetch_sub(&m_stats->m_msgs_outstanding, 1);
479 assert((prev_msgs_outstanding >= 1)
480 &&
"GAUGE underflow: dtor decrement on already-zero msgs-outstanding.");
482 m_stats->m_histo_msg_alloc_sz.record_value(m_alloc_sz);
484 uint64_t used_bytes = 0;
485 for (
const auto& capnp_seg : getSegmentsForOutput())
487 used_bytes += capnp_seg.asBytes().size();
489 m_stats->m_histo_msg_used_sz.record_value(used_bytes);
491 m_stats->m_histo_segs_per_msg.record_value(m_serialization_segments->size());
496template<
typename Shm_arena>
497template<
typename Session_t,
typename Capnp_root_builder>
501 using flow::util::buffers_dump_string;
527 assert((!blobs.empty())
528 &&
"Should not be possible for serialization to be empty with our use cases. Investigate.");
530 const auto capnp_segs = getSegmentsForOutput();
531 assert((capnp_segs.size() == blobs.size())
532 &&
"Somehow our MessageBuilder created fewer or more segments than allocateSegment() was called?!");
535 typename Segments_in_shm::iterator blob_it;
536 for (idx = 0, blob_it = blobs.begin(); idx != capnp_segs.size(); ++idx, ++blob_it)
538 const auto capnp_seg = capnp_segs[idx].asBytes();
539 const auto seg_sz = capnp_seg.size();
541 auto& blob = *blob_it;
543 assert((capnp_seg.begin() == &(blob.front()))
544 &&
"Somehow capnp-returned segments are out of order to allocateSegment() calls; or something....");
546 &&
"capnp shouldn't be generating zero-sized segments.");
547 assert((seg_sz <= blob.capacity())
548 &&
"capnp somehow overflowed the area we gave it.");
570 flow::util::Blob::S_UNCHANGED,
573 FLOW_LOG_TRACE(
"SHM builder [" << *
this <<
"]: "
574 "Serialization segment [" << idx <<
"] (0-based, of [" << capnp_segs.size() <<
"], 1-based): "
575 "SHM-arena buffer @[" <<
static_cast<const void*
>(&(blob.front())) <<
"] "
576 "sized [" << seg_sz <<
"]: Serialization of segment complete.");
577 FLOW_LOG_DATA(
"Segment contents: "
578 "[\n" << buffers_dump_string(
Blob_const{&(blob.front()), blob.size()},
" ") <<
"].");
586 const auto handle_serialization_blob = shm_session->template lend_object<Segments_in_shm>(m_serialization_segments);
588 if (handle_serialization_blob.empty())
594 FLOW_LOG_WARNING(
"SHM builder [" << *
this <<
"]: "
595 "After finalizing capnp-serialization in a SHM arena, SHM-session failed to register "
596 "attempt to lend a SHM-handle to this serialization to the opposing process. "
597 "The data structure cannot be transmitted to the opposing process. Assuming no bugs "
598 "up to this point, the session is down (usually means opposing process is down).");
616template<
typename Shm_arena>
617kj::ArrayPtr<::capnp::word>
620 using flow::util::stat::fetch_add;
621 using flow::util::stat::update_hi_wmark;
622 using Word = ::capnp::word;
623 using Capnp_word_buf = kj::ArrayPtr<Word>;
624 using flow::util::ceil_div;
625 constexpr size_t WORD_SZ =
sizeof(Word);
626 const size_t min_sz =
size_t(min_sz_words) * WORD_SZ;
639 const bool big_leaf = min_sz > m_segment_sz;
640 const size_t seg_sz = big_leaf ? min_sz : m_segment_sz;
642 FLOW_LOG_TRACE(
"SHM builder [" << *
this <<
"]: allocateSegment request for >=[" << min_sz_words <<
"] words; "
643 "SHM-allocing ~max(that x sizeof(word), next-size=[" << m_segment_sz <<
"]) = [" << seg_sz <<
"] "
651 buf_ptr = &(m_serialization_segments->emplace_back
653 flow::util::CLEAR_ON_ALLOC,
655 get_logger()).front());
674 m_segment_sz += seg_sz;
678 FLOW_LOG_TRACE(
"SHM builder [" << *
this <<
"]: Next-size grew exponentially to [" << m_segment_sz <<
"] "
689 if (m_serialization_segments->size() == 1)
691 fetch_add(&m_stats->m_msgs, 1);
692 update_hi_wmark(&m_stats->m_msgs_outstanding_hi_wmark,
693 fetch_add(&m_stats->m_msgs_outstanding, 1) + 1);
696 fetch_add(&m_stats->m_alloc_lifetime_sz, seg_sz);
697 update_hi_wmark(&m_stats->m_alloc_outstanding_sz_hi_wmark,
698 fetch_add(&m_stats->m_alloc_outstanding_sz, seg_sz) + seg_sz);
699 m_alloc_sz += seg_sz;
703 fetch_add(&m_stats->m_big_leaf_alloc_count, 1);
704 m_stats->m_histo_big_leaf_sz.record_value(min_sz);
707 m_saw_big_leaf =
true;
708 fetch_add(&m_stats->m_msgs_with_big_leaf, 1);
713 return Capnp_word_buf{
reinterpret_cast<Word*
>(buf_ptr),
714 reinterpret_cast<Word*
>(buf_ptr + seg_sz)};
717template<
typename Shm_arena>
730 ::
capnp::ReaderOptions{}.nestingLimit }),
731 flow::log::Log_context(logger_ptr,
Log_component::S_TRANSPORT)
733 FLOW_LOG_TRACE(
"SHM reader [" << *
this <<
"]: Created.");
736template<
typename Shm_arena>
739 FLOW_LOG_TRACE(
"SHM reader [" << *
this <<
"]: Destroyed. The following may SHM-dealloc the serialization.");
743template<
typename Shm_arena>
744template<
typename Session_t,
typename Capnp_root_reader>
748 using Blob = flow::util::Blob_sans_log_context;
751 using flow::util::buffers_dump_string;
754 if (flow::error::exec_void_and_throw_on_error
755 ([&](
Error_code* actual_err_code) { borrow(capnp_root, shm_session, actual_err_code); },
756 err_code,
"Capnp_message_reader::borrow()"))
768 FLOW_LOG_TRACE(
"SHM reader [" << *
this <<
"]: About to deserialize; hence dropping existing structure. "
769 "The following may SHM-dealloc the serialization.");
770 m_btm_capnp_segments.clear();
771 m_btm_serialization_shm_handle.reset();
779 Blob handle_serialization_blob;
785 m_btm_serialization_shm_handle
786 = shm_session->template borrow_object<Segments_in_shm_borrowed>(handle_serialization_blob);
788 if (!m_btm_serialization_shm_handle)
794 FLOW_LOG_WARNING(
"SHM reader [" << *
this <<
"]: "
795 "After receiving SHM-handle to capnp-serialization in a SHM arena, SHM-session failed "
796 "to interpret that SHM-handle. "
797 "The data structure cannot be accepted from the opposing process. Assuming no bugs "
798 "up to this point, the session is down (usually means opposing process is down).");
849 if (serialization_segments.empty())
851 FLOW_LOG_WARNING(
"SHM reader [" << *
this <<
"]: The top serialization was valid; and the SHM handle "
852 "therein does point to a list of segments; but that list is empty. Emitting error. "
853 "Other side misbehaved?");
855 m_btm_serialization_shm_handle.reset();
856 assert(m_btm_capnp_segments.empty() &&
"We should not have set this up yet.");
861 auto& capnp_segs = m_btm_capnp_segments;
862 assert(capnp_segs.empty());
863 capnp_segs.reserve(serialization_segments.size());
866 for (
const auto& serialization_segment : serialization_segments)
868 const void* data_ptr = &(serialization_segment.front());
869 const size_t seg_size = serialization_segment.size();
871 if (((
reinterpret_cast<uintptr_t
>(data_ptr) %
sizeof(word)) != 0) || ((seg_size %
sizeof(word)) != 0))
873 FLOW_LOG_WARNING(
"SHM reader [" << *
this <<
"]: "
874 "Serialization segment [" << idx <<
"] "
875 "(0-based, of [" << serialization_segments.size() <<
"], 1-based): "
876 "SHM-heap buffer @[" << data_ptr <<
"] sized [" << seg_size <<
"]: "
877 "Starting pointer is not capnp-word-aligned, and/or size is not a capnp-word-multiple. Bug? "
878 "Misuse of Capnp_message_reader? Other side misbehaved? "
879 "Either is against the API use requirements; capnp would complain and fail.");
882 m_btm_serialization_shm_handle.reset();
887 FLOW_LOG_TRACE(
"SHM reader [" << *
this <<
"]: "
888 "Serialization segment [" << idx <<
"] "
889 "(0-based, of [" << serialization_segments.size() <<
"], 1-based): "
890 "SHM-heap buffer @[" << data_ptr <<
"] sized [" << seg_size <<
"]: "
891 "Feeding into capnp deserialization engine.");
892 FLOW_LOG_DATA(
"Segment contents: "
893 "[\n" << buffers_dump_string(
Blob_const{data_ptr, seg_size},
" ") <<
"].");
895 capnp_segs.emplace_back(
static_cast<const word*
>(data_ptr),
896 seg_size /
sizeof(word));
904template<
typename Shm_arena>
907 return !m_btm_serialization_shm_handle;
910template<
typename Shm_arena>
913 assert((!empty()) &&
"Are you trying to access via MessageReader before loading SHM-handle into borrow()?");
914 return (
id < m_btm_capnp_segments.size()) ? m_btm_capnp_segments[id] :
nullptr;
917template<
typename Shm_arena>
920 return os <<
'@' << &val;
923template<
typename Shm_arena>
926 return os <<
'@' << &val;
RAII-style class operating a stack-like notion of the given thread's currently active SHM-aware Arena...
Stateless allocator usable with STL-compliant containers to store (or merely read) them directly in S...
A capnp::MessageBuilder used by shm::Builder and for general capnp users: similar to a MallocMessageB...
~Capnp_message_builder()
Decrements owner-process count by 1; if current count is 1 deallocates SHM-stored data.
bool m_saw_big_leaf
Gate: false until the first big-leaf event happens; then flipped to true, and m_msgs_with_big_leaf st...
flow::util::Basic_blob< Allocator< uint8_t > > Segment_in_shm
The inner data structure stored in SHM representing one capnp-requested segment storing all or part o...
uint64_t m_alloc_sz
Running tally: sum of seg_sz chosen for each allocateSegment() call.
size_t m_segment_sz
Minimum size of the next segment allocated by allocateSegment(); a word-multiple.
Arena::template Handle< Segments_in_shm > m_serialization_segments
Outer SHM handle to the data structure in SHM that stores the capnp-requested serialization segments.
Capnp_message_builder(flow::log::Logger *logger_ptr, Arena *arena, size_t segment_sz_init_pre_growth=sizeof(::capnp::word) *::capnp::SUGGESTED_FIRST_SEGMENT_WORDS, struc::stat::Serializer_stats::Snd *stats=nullptr)
Constructs the message-builder, memorizing the SHM engine it shall use to construct/allocate data int...
boost::container::list< Segment_in_shm, Allocator< Segment_in_shm > > Segments_in_shm
The outer data structure stored in SHM representing the entire list of capnp-requested segments Segme...
kj::ArrayPtr<::capnp::word > allocateSegment(unsigned int min_sz_words) override
Implements MessageBuilder API.
Shm_arena Arena
Short-hand for, you know.
struc::stat::Serializer_stats::Snd *const m_stats
Cumulative stats target, or null if no stats recording is configured.
bool lend(Capnp_root_builder *capnp_root, Session_t *shm_session)
To be called after being done mutating underlying structured data, increments owner-process count by ...
A capnp::MessageReader used by shm::Reader and for general capnp users: able to access capnp-serializ...
Arena::template Handle< Segments_in_shm_borrowed > m_btm_serialization_shm_handle
The outer SHM handle to the Segments_in_shm_borrowed containing the serialization yielded by e....
bool empty() const
Return false if and only if borrow() has been invoked at least once, and the last time it was success...
void borrow(const Capnp_root_reader &capnp_root, Session_t *shm_session, Error_code *err_code=0)
Upon dropping structure loaded by the preceding borrow() call (if any), loads the SHM-stored structur...
Capnp_message_reader(flow::log::Logger *logger_ptr)
Boring constructor.
boost::container::list< Segment_in_shm_borrowed, Borrower_allocator< Segment_in_shm_borrowed > > Segments_in_shm_borrowed
For easier outside generic programming, this is the read-only-borrower counterpart to Capnp_message_b...
~Capnp_message_reader()
Any SHM-stored structure currently held as a result of the last successful borrow() is no longer held...
flow::util::Basic_blob< Borrower_allocator< uint8_t > > Segment_in_shm_borrowed
For easier outside generic programming, this is the read-only-borrower counterpart to Capnp_message_b...
kj::ArrayPtr< const ::capnp::word > getSegment(unsigned int id) override
Implements MessageReader API.
std::vector< Capnp_word_array_ptr > m_btm_capnp_segments
Analogous to Heap_reader::m_capnp_segments; just points into SHM.
Shm_arena Arena
Short-hand for, you know.
kj::ArrayPtr< const ::capnp::word > Capnp_word_array_ptr
Similar to Heap_reader::Capnp_word_array_ptr.
Small group of miscellaneous utilities to ease work with capnp (Cap'n Proto), joining its capnp names...
@ S_DESERIALIZE_FAILED_INSUFFICIENT_SEGMENTS
Structured message deserialization: Tried to deserialize without enough input segments (e....
@ S_DESERIALIZE_FAILED_SEGMENT_MISALIGNED
Structured message deserialization: An input segment is not aligned as required.
@ S_DESERIALIZE_FAILED_SESSION_HOSED
Structured message deserialization: SHM-session borrow_object() indicated failure when interpreting a...
Segregates zero-copy/SHM implementations of concepts residing in parent namespace ipc::transport::str...
std::ostream & operator<<(std::ostream &os, const Capnp_message_builder< Shm_arena > &val)
Prints string representation of the given Capnp_message_builder to the given ostream.
void capnp_get_shm_handle_to_borrow(const Capnp_root_reader &shm_handle_root, flow::util::Blob_sans_log_context *arg_to_borrow)
Utility that's the reverse of capnp_set_lent_shm_handle() to be invoked on the deserializing side.
void capnp_set_lent_shm_handle(Capnp_root_builder *shm_handle_root, const flow::util::Blob_sans_log_context &lend_result)
Utility that saves the result of a Shm_session1::lend_object<T>(const shared_ptr<T>&) into the given ...
::capnp::MessageReader Capnp_msg_reader_interface
Alias for capnp's MessageReader interface. Rationale: as part of our API, we use our identifier style...
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.
Log_component
The flow::log::Component payload enumeration containing various log components used by Flow-IPC inter...
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
Send-side (during-serialization) stats, captured at hooks within the Struct_builder impl: typically e...