Flow-IPC 3.0.0
Flow-IPC project: Public API.
Todo List
Page Asynchronicity and Integrating with Your Event Loop
We may supply an alternative API wherein Flow-IPC objects can be boost.asio I/O objects themselves, similarly to boost::asio::ip::tcp::socket. Similarly, async-ops could be made available as C++26 std::execution senders. (The sync_io cores would make either adapter fairly natural to build.)
Class boost::has_trivial_destructor_after_move<::ipc::shm::stl::Stateless_allocator< T, Arena_t > >
Once our minimum supported Boost (per our build script(s)) version includes the fix for the bug worked-around by boost::has_trivial_destructor_after_move<ipc::shm::stl::Stateless_allocator> specialization: remove that specialization. As of Boost-1.92 the bug is there.
Member ipc::session::Client_session_mv< Client_session_impl_t >::sync_connect (Error_code *err_code=nullptr)
Consider adding an optional mode/feature to allow to wait through the condition wherein CNS (PID file) does not exist, or it does but the initial session-open connect op would be refused; instead it detects these relatively common conditions (server not yet up and/or is restarting and/or is operationally suspended for now, etc.) as normal and waits until the condition is cleared. Without this mode, a typical user would probably do something like: oh, sync_connect() failed; let's sleep 1 sec and try again (rinse/repeat). It's not awful, but we might as well make it easier and more responsive out of the box (optionally). Upon resolving this to-do please update the Manual section Sessions: Setting Up an IPC Context accordingly.
Class ipc::session::Session_cfg
Session_cfg is the nucleus of a more general overhaul of ipc::session configuration (e.g., grouping various Session / Session_server template parameters into a struct; possibly per-Client_app knobs). Its constants may then become run-time and/or per-session-configurable.
Member ipc::session::Session_mv< Session_impl_t >::open_channel (Channel_obj *target_channel, const Mdt_builder_ptr &mdt, Error_code *err_code=nullptr)
Server_session::open_channel() and Client_session::open_channel() both emit error::Code::S_SESSION_OPEN_CHANNEL_SERVER_CANNOT_PROCEED_RESOURCE_UNAVAILABLE when unable to acquire the resources (as of this writing likely MQ-related ones) underpinning the would-be channel; but server-side the actual original Error_code could be emitted instead. (Client-side, due to the IPC-mechanics involved, this is less straightforward, as then: (1) it would be ambiguous as to whether it was the server-side-user-yanked-channel-immediately scenario (user's choice, not an environmental problem), or the current-RESOURCE_UNAVAILABLE scenario; and (2) transmitting an Error_code over IPC is a potentially subtle impl task. Hence the to-do is probably aimed at Server_session::open_channel() et al, not the client-side method family.)
Member ipc::session::shm::arena_lend::jemalloc::Shm_session::create (flow::log::Logger *logger, Shm_channel &&shm_channel, const transport::struc::Session_token &session_token_non_nil, flow::async::Task_asio_err &&shm_channel_error_handler)
It'd be nice for Shm_session::create() to become a template with parameterized functor type instead of flow::async::Task_asio_err for shm_channel_error_handler. There is zero functional difference, as internally it is stored as a Task_asio_err (which a Function<>) anyway; but for consistency and maintainability it'd be nice.
Class ipc::session::sync_io::Client_session_adapter< Session >
Make all of Server_session_adapter, Client_session_adapter move-ctible/assignable like their adapted counterparts. It is not of utmost importance practically, unlike for the adapted guys, but at least for consistency it would be good; and of course it never hurts usability even if not critical. (Internally: This is not difficult to implement; the async-I/O guys being movable was really the hard part.)
Member ipc::shm::arena_lend::collection_id_t
Determine proper size of identifier.
Class ipc::shm::arena_lend::jemalloc::Ipc_arena
ipc::shm::arena_lend::jemalloc::Ipc_arena has public methods for end users but also an API, including (e.g.) add_shm_pool_listener(), used by Flow-IPC internals to make things work properly; these should be made non-public (accessible by said internals via a facade/attorney/etc. pattern of choice). A careful effort is required to clean up the API and then classify what's left as either public-facing or otherwise. Suggest also converting currently-used protected-for-unit-tests pattern to the facade/attorney/etc. pattern of choice (if necessary a separate facade/whatever could be used for internal-code consumers versus test-code consumers). E.g., start() is such a test-accessed protected-only-for-that-reason item. / Lastly: the public super-class Owner_shm_pool_collection, and its super-class(es), likely expose some public things that too should be gated-off from end-users.
Member ipc::shm::arena_lend::jemalloc::Ipc_arena::create (flow::log::Logger *logger, const std::shared_ptr< Memory_manager > &memory_manager, Shared_name &&pool_name_base, const util::Permissions &permissions)

Ipc_arena::create() would be nice to emit a specific fitting error, as opposed to the somewhat ambiguous resource_unavailable_try_again-or-similar, when pool_name_base is malformed. The message could even suggest Shared_name::sanitize() and/or Shared_name::sanitized().

Ipc_arena::create() should follow normalized Flow error emission semantics (take Error_code*, set to success or specific failure, if null then throw on failure, encapsulating the truthy Error_code).

Class ipc::shm::arena_lend::jemalloc::Memory_manager
While the current SHM-jemalloc impl is thought to play nicely with other parallel jemalloc use (such as for vanilla heap allocation) – with all allocation segregated into separately maintained jemalloc-arenas and with only very few enforced global option-setting requirements – we should methodically explore and document what exactly SHM-jemalloc requires of the jemalloc config (both compiled-in and dynamically settable options). Things are OK given a standard jemalloc build, within a fairly wide (calendar-wise) version range, and we check some (a small # of) global settings we know we require, but a methodical analysis in the guts of SHM-jemalloc might yield more and save users some pain.
Member ipc::shm::arena_lend::jemalloc::Thread_cache::flush_tcache (tcache_id_t id)
jemalloc::Thread_cache internals are as of this writing intentionally intolerant of failed jemalloc calls, such as due to getting a bad ID, essentially because in practice it was not important to contend with such scenarios; but it'd be nice to be more resilient about it nevertheless. As of this writing jemalloc errors within Thread_cache cause a logged message, assertion-trip if enabled, and finally a hard std::abort() if the assertion-trip attempt did not do it.
Class ipc::shm::stl::Stateless_allocator< T, Arena_t >
Currently Arena::Pointer shall be a fancy-pointer, but we could support raw pointers also. Suppose Arena is set up in such a way as to map all processes' locally-dereferenceable pointers to the same SHM location to the same numeric value (by specifying each pool's start as some predetermined numerical value in the huge 64-bit vaddr space) in all processes sharing that SHM pool. Now no address translation is needed, and Arena::Pointer could be simply T*. As of this writing some inner impl details assume it being a fancy-pointer, and it's the only active need we have; but that could be tweaked with a bit of effort.
Namespace ipc::transport::asio_local_stream_socket

asio_local_stream_socket additional feature: APIs that can read and write native sockets together with accompanying binary blobs can be extended to handle an arbitrary number of native handles (per call) as opposed to only 0 or 1. The main difficulty here is designing a convenient and stylish, yet performant, API. Having achieved this we could also similarly extend the Native_handle_sender, Native_handle_receiver concepts and their impls.

Analogously to how Msg_batch_in supports batch-receipt, we could add API(s) for batch-send. Having achieved this we could also similarly extend the Blob_sender, Native_handle_sender concepts and their impls. Without getting into details: sending and receiving, especially in batches, are not really mirror images of each other conceptually. Hence lacking one but not the other is not necessarily an omission due to lack of time; the need for it is not as clear (we feel); and the API(s) would probably be designed fairly differently.

Class ipc::transport::Native_handle_sender

Comparing Blob_sender::send_blob_max_size() (and similar checks in that family of concepts) to test whether the object is in PEER state is easy enough, but perhaps we can have a utility that would more expressively describe this check: in_peer_state() free function or something? It can still use the same technique internally.

In C++20, if/when we upgrade to that, Native_handle_sender (and other such doc-only classes) can become an actual concept formally implemented by class(es) that, today, implement it via the "honor system." Currently it is a class #ifdef-ed out from actual compilation but still participating in doc generation. Note that Doxygen (current version as of this writing: 1.9.3) claims to support doc generation from formal C++20 concepts.

Class ipc::transport::Native_socket_stream_acceptor
At the moment, if one decides to use a Native_socket_stream_acceptor directly – not really necessary given ipc::session Channel-opening capabilities – the user must come up with their own naming scheme that avoids name clashes; we could supply an ipc::session-facilitated system for providing this service instead. I.e., ipc::session could either expose a facility for generating the Shared_name absolute_name arg to the Native_socket_stream_acceptor ctor (and opposing Native_socket_stream::sync_connect() call). Alternatively it could provide some kind of Native_socket_stream_acceptor factory and corresponding opposing facility. Long story short, within the ipc::session way of life literally only one acceptor exists, and it is set up (and named) internally to ipc::session. We could provide a way to facilitate the creation of more acceptors if desired by helping to choose their Shared_names. (An original "paper" design did specify a naming scheme for this.)
Member ipc::transport::Native_socket_stream_cfg::low_lvl_payload_blob_length_t
Native_socket_stream_cfg::low_lvl_payload_blob_length_t should arguably be renamed, since it represents a length (when not representing a message-type) only if S_USE_OS_DGRAM_SUPPORT is false; otherwise it always represents a message-type only. We have kept the existing name arguably form legacy inertia and/or because a better name was not immediately forthcoming.
Member ipc::transport::Native_socket_stream_cfg::S_USE_OS_DGRAM_SUPPORT
Native_socket_stream_cfg::S_USE_OS_DGRAM_SUPPORT and Native_socket_stream_cfg::S_USE_OS_DGRAM_BATCH_SUPPORT is each currently determined based on availability in the compiling host OS; but for various reasons (both flexibility/testing and compatibility with other target platforms and versions) it might be helpful to make each configurable via the Flow-IPC build script(s). It could also be configured per individual Native_socket_stream at compile-time (a template parameter).
Member ipc::transport::stat::Blob_snd_stats::m_histo_payload_sz
Consider replacing m_histo_payload_sz's uniform low end with log-scale buckets (64, 128, ..., 1Ki), if field experience keeps wanting better resolution among small messages: bucket 0 currently swallows all sub-1Ki traffic. (The latter includes ~all outer messages of SHM-backed structured channels; conceivably it would be nice to capture those, for applications that use that feature; which to be clear means (1) using struc::Channel or struc::sync_io::Channel in ther first place; and given that also (2) using the SHM-backed variety as opposed to merely heap-backed.)
Class ipc::transport::struc::Channel< Owned_channel_t, Msg_body_t, Builder_config_t, Reader_config_t >
struc::Channel should probably be made move-constructible and move-assignable. No concept requires this, unlike with many other classes and class templates in ipc, so it is less essential; but both for consistency and usability it would be good. It would also make some APIs possible that currently would require the user to explicitly wrap this class in a unique_ptr. For example, imagine a Session::Structured_channel Session::structured_channel_upgrade(Owned_channel_t&& channel, ...) that constructs a suitably-typed struc::Channel, subsuming the raw channel just opened in that session::Session, and returns that guy. Currently it would need to return unique_ptr<Session::Structured_channel> or something.
Member ipc::transport::struc::Channel< Owned_channel_t, Msg_body_t, Builder_config_t, Reader_config_t >::async_end_sending (Task_err &&on_done_func)
Consider adding blocking struc::Channel::sync_end_sending(), possibly with timeout, since async_end_sending() to completion is recommended (but not required) to execute at EOL of any struc::Channel. The "blocking" part is good because it's convenient to not have to worry about handling completion with async semantics boiler-plate. The "timeout" part is good because it's a best-effort operation, when in a bad/slow environment, and blocking – even during deinit – would be important to avoid. For example, the Session impls' dtors perform this op; we don't want those to block for any significant time if at all avoidable. The reason it's a mere to-do currently is that a bug-free opposing struc::Channel should not let would-block occur for any real length of time; so blocking is presumably unlikely. Nevertheless.
Member ipc::transport::struc::Channel< Owned_channel_t, Msg_body_t, Builder_config_t, Reader_config_t >::owned_channel_mutable ()
Consider adding struc::Channel::auto_ping() and struc::Channel::idle_timer_run() and, for safety, removing struc::Channel::owned_channel_mutable().
Member ipc::transport::struc::Channel< Owned_channel_t, Msg_body_t, Builder_config_t, Reader_config_t >::sync_request (Msg_out *msg, const Msg_in *originating_msg_or_null, util::Fine_duration timeout, Error_code *err_code)

Consider treating a response to a timed-out struc::Channel::sync_request() as an unexpected response: firing the set_unexpected_response_handler() handler locally (perhaps with an added flag indicating expected-but-timed-out) and informing the opposing peer, so that its set_remote_unexpected_response_handler() handler fires. As of this writing such a late response is simply dropped.

Improve ipc::transport::struc::Channel::sync_request(), so that 2 such calls on the same Channel can execute without one having to fully block the other. Note this may be seen as a breaking change of sorts for the purposes of release notes (even though it would presumably be seen as an improvement).

Member ipc::transport::struc::Segment_ptrs
As per nearby Rationale comment, Segment_ptrs element type would ideally be a view into Blob-or-similar instead of actual Basic_blob*, to decouple impl from API more cleanly. One approach might be to develop a flow::util class/template, a peer of the flow::util::Blob hierarchy, that specifically represents a view into a Blob-type-thing (perhaps 1 variant for mutable, 1 variant for immutable), perhaps analogously to how span<uint8_t> + span<const uint8_t> is each a view into vector<uint8_t> (minus the latter's capacity). It would therefore store a pointer, a size, a capacity, and a start-index – representing a main buffer (size() in size), N-byte header/prefix (N = start()), and M-byte postfix (M = capacity() - start() - size()). (A more economical buffer+header version – directly applicable here – might store all of those minus the capacity, meaning there is no postfix area.) Possible names might be Blob_view_const (read-only view into buffer plus header plus postfix), Blob_view_mutable (same plus writable access), Prefixed_byte_span_const (Blob_view_const minus postfix area), Prefixed_byte_span_mutable (Blob_view_mutable minus postfix area). Segment_ptrs would likely be vector<Prefixed_byte_span_mutable>.
Class ipc::transport::struc::shm::Builder< Shm_arena >
In shm::Builder some comments and/or logging and/or identifiers refer to "top/bottom" serialization; in others to "outer/inner" serialization; we should consistently use the latter terminology there and all over Flow-IPC.
Member ipc::transport::struc::shm::Capnp_message_builder< Shm_arena >::lend (Capnp_root_builder *capnp_root, Session_t *shm_session)
Would be nice to provide a more-general counterpart to existing Capnp_message_builder::lend() (in addition to that one which outputs into a capnp structure), such as one that outputs a mere Blob. The existing one is suitable for the main use-case which is internally by shm::Builder; but Capnp_message_builder is also usable as a capnp::MessageBuilder directly. If a user were to indeed leverage it in that latter capacity, they may want to transmit/store the SHM-handle some other way.
Member ipc::transport::struc::shm::rpc::Context_server< Session_server_t >::accept (bool enable_hndl_transport, N_init_channels_by_srv_req_func &&n_init_channels_by_srv_req_func, Transport_method_func &&transport_method_func)
For the rpc::Context_server::accept() advanced overload(s) that take an n_init_channels_by_srv_req_func arg, it would be a nice improvement to the contract to guarantee issuing the call to that user-function from the KJ-event-loop thread, like the rest of the async action. (E.g., the following async parts execute in the KJ style: promise fulfillment; transport_method_func() call if applicable.) This API change would not be a breaking change. The reason it is not already done is that the Flow-IPC capnp-RPC-support layer treats the non-RPC-aware ipc::session layer (normally used directly by user but not here) as a black box; and the session::Session_server::async_accept() contract – in this particular detail – did not need to be KJ-friendly (or even KJ-aware) in its design, so it was not. (capnp-RPC support did not exist at the time.) So the way session::Session_server invokes n_init_channels_by_srv_req_func() is inverted versus what the KJ flow needs. To implement this improvement, session::Session_server would need an added async hook for the give-me-the-desired-channel-count step, and Context_server::accept() impl would make use of it.
Class ipc::transport::struc::shm::rpc::Session_vat_network< Shm_lender_borrower_t, Shm_arena_t >
rpc::Session_vat_network and probably some of the layers built on top of it should make capnp::ReaderOptions configurable – analogously to the vanilla capnp::TwoPartyVatNetwork. (Be sure to apply this judiciously to the correct layer of serialization; and document thoughtfully, as the practical perf implications here are different from the vanilla/non-zero-copy use case.)
Member ipc::transport::struc::stat::Heap_serializer_global_stats
Consider changing definition of alias struc::stat::Heap_serializer_global_stats from using N == 1 to something larger, so that the user can optionally – while still using this global stats-holder – further categorize stats according to their custom needs. Also consider similar steps in such alias(es) in struc::shm::stat. (Note various relevant APIs allow one to simply pass-in a Serializer_stats* (et al) directly – and the user can maintain their own Global_stats or static Stat_set_list and lightning-quickly obtain the aforementioned * therefrom. So flexibility already exists on that front; but here we use a Global_stats for a reason already; can make use of it more fully this way.) One tactical idea is that factories like struc::Channel::heap_fixed_builder_config() can take an optional arg, defaulting to 0, which would cause such a factory to select the Global_stats slot. Slot 0 would continue to, by convention be the default/catch-all one; and the user can designate other slots for their own purposes.
Member ipc::transport::sync_io::Native_socket_stream::S_TRANSMIT_NATIVE_HANDLES
Shouldn't Native_socket_stream::S_TRANSMIT_NATIVE_HANDLES and its peer concept impls be formally generalized in the appopriate concept? E.g., as of this writing the Async_adapter_receiver impl relies on it, so trying to use that guy to implement a custom ..._receiver in terms of a custom sync_io::..._receiver would cause a build error, unless the writer of the latter hypothetical custom guy added a S_TRANSMIT_NATIVE_HANDLES member. In practice they'd probably know to do so upon seeing the error and the present code; but formally a relevant concept should specify it, so the custom-guy writer would've known to do so from the start.
Member ipc::util::operator<< (std::ostream &os, const Shared_name &val)
Does Shared_name operator>> and operator<< being asymmetrical get one into trouble when using Shared_name with boost.program_options (or flow::cfg which is built on top of it)? Look into it. It may be necessary to make operator<< equal to that of ostream << string after all; though the added niceties of the current << semantics may still at least be available via some explicit accessor.
Class ipc::util::Shared_name

Consider adding a Shared_name_view which would be to Shared_name as String_view is to string; and that could itself be constructed from a String_view (and therefore from const char* [+ length], etc.). The main work would arguably not be the creation of that class itself but rather (1) ensuring proper implicit conversion between the main class and the new one and (2) conversion of the many Flow-IPC APIs that take a const Shared_name& that would (hopefully in essentially non-user-code-breaking ways) instead take Shared_name_view.

It would be good to constexpr-ify Shared_name to the extent possible; it might even make a dent in perf in some contexts. Internally this is annoying until C++20, where std::string itself got constexpr-ified, as we use std::string internally; best left alone until then.

Member ipc::util::Shared_name::S_MAX_LENGTH
Shared_name::S_MAX_LENGTH currently applies to all shared resource types, but it'd be a useful feature to have different limits depending on OS/whatever limitations for particular resources types such as SHM object names versus queue names versus whatever.
Namespace ipc::util::sync_io
Write an example of sync_io-pattern use with an old-school reactor-pattern event loop, using poll() and/or epoll_*().