Repository navigation
Highlights
- SHM-jemalloc performance deep-dive: ipc#143
- Load-test of large multi-threaded application, generate flame-graphs, optimize hot spots; repeat.
- Before optimization: collapse under heavy SHM-jemalloc use; several prominent flame-graph hot spots.
- After optimization: performs well; no SHM-jemalloc hot spots remain.
- No change to the essential APIs.
- Transport performance deep-dive: ipc#143
- Profile the same application across the IPC-transport layers, most notably
struc::ChannelandNative_socket_stream. - Resulting optimizations improved overall application throughput by another ~10-20%.
- For direct users of the low-level transports: Add batch-receive, getting many messages per system call (
recvmmsg()). - Bonus: Heap-backed
struc::Channelmessages with large leaves (list, blob, string) now transmit without problems. They're internally auto-split/reassembled when needed.
- Profile the same application across the IPC-transport layers, most notably
- Zero-copy-enabled Cap'n Proto RPC (new module
ipc::transport::struc::shm::rpc): ipc#121- Use capnp-RPC as usual.
- Underneath, messages live in SHM and are never copied, so payload size no longer matters.
- Stats: Counters, gauges, histograms, and high-water marks at every layer: ipc#164
- Covers low-level transports,
struc::Channel, serialization, sessions, SHM.- SHM-jemalloc: Includes curated selection of jemalloc's own stats.
- These build on
flow::util::stat, a new general stats API in Flow (a dependency of Flow-IPC).- You can use this for your own stats too.
- Covers low-level transports,
- Hardening and stability: Numerous fixes, notably around session opening and session end, covered by a greatly expanded test suite.
- SHM-classic major bug fix:
Pool_arena::construct<T>()could take a few bytes too few for certain object sizes. ipc_shm#29 - The new tests join the existing sanitizer runs in the CI pipeline (ASAN/LSAN, TSAN, UBSAN).
- SHM-classic major bug fix:
Upgrade notes
- Compatibility: Both processes engaging in IPC must use this release (or later). Internal protocols changed, as described below.
- A mismatch is handled cleanly: session opening (or a direct
Native_socket_streamconnect) fails; astruc::Channelset up outside a session reports a protocol-version error.
- A mismatch is handled cleanly: session opening (or a direct
- Breaking API changes mostly avoid commonly used APIs.
- Notable exception:
ipc::transport::struc::[sync_io::]Channel::send(),[a]sync_request()now takeMsg_out*instead ofconst Msg_out&: please usech.send(&msg), notch.send(msg). - See "API notes" below for the full list.
- Notable exception:
All changes
Across modules
- Stats: Each module adds stat-set
structs and access to recorded stats. ipc#164- Each stat-set is a snapshot. Every
structmember (counter/histogram, gauge, high-water mark) is documented in the Reference. - Print one via
flow::util::stat:cout << flow::util::stat::print(channel.stats()). - Reset (zero counters and histograms, etc.) anytime:
channel.stats_reset(). - Where there is a lot of detail (notably SHM-jemalloc), print a whole bundle in one shot:
session.session_shm()->info_dump(&dump); cout << dump. - Guided Manual: The API overview has a Stats section, with a table of where to find what.
- Each stat-set is a snapshot. Every
- Fix (bug class): The formally allowed
Logger* logger_ptr = nullptrAPI arg value could cause a crash. - Internal worker threads:
ipc::util
- Add
Native_handlefacilities:close(),is_open(),dup()(close-on-exec); RAII/auto-closing ownerOwn_native_handlewithduped_native_handle(),disowned_native_handle();swap().constexpr+noexceptadded all over.
- Add
Shared_name::ct_from_int()(efficient integer => name). - Add
NULL_PROCESS_CREDENTIALS; as-if-default-constructedProcess_credentialsprints asnull. - Add
Info_dump_format: Common knobs (verbose, multi-line) for info-dump/stats output elsewhere in Flow-IPC. - Remove
Use_counted_object. MoveDefault_init_allocatorto Flow (flow::util::Default_init_allocator). - Fix
ipc::util::process_running(): ipc_core#41- The POSIX
EPERMoutcome (process exists, but signaling it is not permitted) was treated as impossible (assertion trip; or yields "not running"). Now: yields "running." - A non-positive PID now yields "not running" (before: essentially undefined behavior).
- The POSIX
- Fix
set_resource_permissions(): A concurrent fork/exec could leak its temporary FD into the child (O_CLOEXEC). - Minor fixes (none affect Flow-IPC's own use of these APIs):
- Fix
Native_handlemove-assignment: Source was not nullified if it held the same raw handle value as the target. - Fix
Shared_name::sanitize(),sanitized(): With a non-default locale, characters outside[A-Za-z0-9_]could pass. - Fix
Shared_name/=operators when the argument overlaps the target (s /= s,s /= s.str(), etc.). - Fix
istream >> Shared_name: On failure of the underlying string-read it loaded an empty name; now leaves it as-is. - Fix
Process_credentials::process_invoked_as(): Non-throwing form did not clear*err_codeon success.
- Fix
ipc::transport (unstructured)
[sync_io::]Native_socket_stream:- Performance: Internally use connected-datagram (
SOCK_SEQPACKET) sockets instead of stream (SOCK_STREAM) sockets: OS maintains message boundaries => much less per-message work happens in user-land.- API contracts are unchanged.
- Add batch-receive:
async_receive_{blob|native_handle}_batch()receive up to N in-messages in one call, with one native batch-read (recvmmsg()). See alsoS_RCV_*_BATCH_SZ_RECOMMENDATION. - Add stats:
..._{send|receive}_stats(),..._stats_reset(). (SeeBlob_{snd|rcv}_statsbelow.) - Fix/upgrade the
send_native_handle()contract: You may close handle (FD), as soon as the call returns.- Previously this was unsafe if the socket happened to be in would-block state: the deferred send could then transmit a closed (or reused) descriptor.
- Internally we
Native_handle::dup()if deferral required.
- Add
Native_socket_stream_cfg: Compile-time constants governingNative_socket_stream(socket type, batch-receive mode, max message size, stats logging).- The stream-socket code path, and an emulation of batch-receive via one-message-receive OS-calls, are available but dormant (not selected by the shipped configuration).
- A future release may make these selectable, perhaps via
Native_socket_streamtemplate parameters per-object. ipc#156
- A future release may make these selectable, perhaps via
- The stream-socket code path, and an emulation of batch-receive via one-message-receive OS-calls, are available but dormant (not selected by the shipped configuration).
- Fix: An assertion could trip when sending graceful-close (
async_end_sending()), while the outgoing direction was in would-block state. - Fix: A received native handle could leak, if an error occurred on the stream very close to it (e.g., the accompanying blob was too big for the user's target buffer).
- Fix: Retrying
sync_io::Native_socket_stream::sync_connect()after a failed attempt could trip an assertion. remote_peer_process_credentials()may now be called concurrently with one send-op and/or one receive-op.- Add
remote_peer_process_credentials()mutator: Sets what the accessor returns, for socket-streams created more manually (asipc::sessiondoes internally). ipc#148 - Send operations always log a WARNING upon encountering a socket-hosing error (previously: no WARNING if no native handle was being sent).
- Reject protocol-violating messages from the opposing peer more stringently.
- Performance: Internally use connected-datagram (
- All sender/receiver implementations (
Native_socket_stream,Blob_stream_mq_*, and anything built on them):- Performance: Each object's timer thread now starts only upon first use of
auto_ping()oridle_timer_run(). Many objects never use those, so no thread is started. ipc#161 - Fix: A very-low-probability use-after-free in the internal timer facility.
- Performance: Each object's timer thread now starts only upon first use of
Channel(and its aliases and subclasses, such asSocket_stream_channel):- Add batch-receive and stats APIs, forwarding to the underlying pipes.
- Fix
async_end_sending()(sync_ioform, 2-pipe channels only): A would-block on a pipe was reported as synchronous success. At worst one could close the channel before trailing messages were flushed.struc::Channel::async_end_sending()is affected indirectly. - Minor fixes (none affect Flow-IPC's own use of these APIs):
- Fix
blob_snd(),hndl_snd()(etc.): The advertised check for an uninitialized pipe was missing (undefined behavior). - Fix 2-arg
init_blob_pipe(),init_native_handle_pipe(): The advertised check for a duplicate call was missing (silently overwrote). - Fix move-ctor, move-assignment: Moved-from object was not as-if-default-constructed (it remained
initialized(), with its pipes in NULL state). - Fix
remote_peer_process_credentials(): Did not clear*err_codeif the channel had no socket pipe.
- Fix
[sync_io::]Blob_stream_mq_sender,Blob_stream_mq_receiver:- Add batch-receive
async_receive_blob_batch()(emulated via one-message MQ receives; implements extended conceptBlob_receiver). - Add stats:
blob_{send|receive}_stats(),..._stats_reset(). - Minor fix: Ctors (non-throwing form) did not clear
*err_codeon success.
- Add batch-receive
- Concepts
Blob_sender,Blob_receiver,Native_handle_sender,Native_handle_receiver(andsync_io::counterparts) gain batch-receive and stats APIs, plus thesend_native_handle()contract fix/upgrade. Hence the concrete implementations (seeNative_socket_stream,Blob_stream_mq_*above) follow these contracts. - Add transport stat
structsipc::transport::stat::Blob_snd_stats,Blob_rcv_stats: Detailed per-direction counters (including a histogram), gauges, high-water marks (re. messages, bytes, handles, auto-pings, etc.).- Obtain via the
*_stats()accessors above; reset via*_stats_reset().
- Obtain via the
- Add (make public)
sync_io::Async_adapter_{sender|receiver}: Adapt async_io::core sender/receiver into its async-I/O counterpart, as Flow-IPC does forNative_socket_streamandBlob_stream_mq_*. Useful for implementing a custom transport (advanced). Posix_mq_handle,Bipc_mq_handle(Persistent_mq_handleconcept):- Minor fix:
Posix_mq_handlectors: certain unlikely errors (such as running out of FDs) could leave a newly created MQ's name in the file system, or (rarer) leak its handle. - Minor fixes (none affect Flow-IPC's own use of these APIs):
- Fix
timed_send(),timed_receive()(both classes): If woken up repeatedly without success, could report timeout before the specified time elapsed. - Fix
Posix_mq_handle::interrupt_*(): Could fail to take effect (wrong array length given toepoll_wait()). - Fix
Posix_mq_handlemove-assignment: Leaked the MQ handle and 2epollFDs of the target. - Fix
Posix_mq_handlectors: Non-throwing form did not clear*err_codeon success. - Fix
swap()ofBipc_mq_handles: Bad state if one was mid-interrupt_*().
- Fix
- Minor fix:
[sync_io::]Native_socket_stream_acceptor: Minor fixes (none affect Flow-IPC's own use of these APIs):- Fix ctor: Non-throwing form did not clear
*err_codeon success. - Fix
sync_io::form'sasync_accept()when called beforestart_accept_ops()(undefined behavior): Now logs and returnsfalse. - Fix
sync_io::form's ctor: Logged a WARNING on success.
- Fix ctor: Non-throwing form did not clear
Protocol_negotiator::compute_negotiated_proto_ver(): Fixes (none affect Flow-IPC's own use of this API):- Fix: If the opposing side speaks an older protocol that this side is configured to be backward-compatible with, this side nevertheless chose to speak the newer one (now: the older one).
- Flow-IPC itself currently does not exercise backward-compatibility.
- Minor fix: Non-throwing form did not clear
*err_codeon success.
- Fix: If the opposing side speaks an older protocol that this side is configured to be backward-compatible with, this side nevertheless chose to speak the newer one (now: the older one).
ipc::transport::asio_local_stream_socket(low-level Unix-domain-socket utilities beneathNative_socket_stream; also usable directly):- Add connected-datagram (
SOCK_SEQPACKET) support alongside existing stream (SOCK_STREAM) support: Protocol is now a template parameter, particularly ofnb_{read|write}_some_with_native_handle(). - Add class
Msg_batch_in: Batch-receive (via nativerecvmmsg()) of datagrams, each with an optional native handle (FD). It keeps its OS-call data structures from read to read, so repeated batch-reads cost ~nothing to set up. (Cf.: naively one would set up the full array before each read.) nb_{read|write}_some_with_native_handle()accept buffer sequences (scatter/gather), not just one buffer.- Remove Boost.Asio extensions
async_write_with_native_handle(),async_read_with_target_func(),async_read_interruptible(). - Remove
release_native_peer_socket()(useutil::Native_handle::close()). ipc_core#40
- Add connected-datagram (
ipc::transport (structured)
struc::[sync_io::]Channel:- Performance improvement:
Major optimization of how we encode/transmit/decode a structured message (Msg_out) given to a send API (send(),[a]sync_request()).- Profiling a large, multi-threaded server application under load showed this path as a substantial cost in processor cycles, significantly reduced by this change.
- Associated breaking change in APIs
send(),[a]sync_request(): They now takeMsg_out*instead ofconst Msg_out&. A send sets certain internal metadata.- Your message contents are not modified, same as before.
- Performance improvements (internal):
- Receive lower-layer messages/blobs in batches, automatically, when the underlying pipe recommends it; in practice for
Native_socket_streams. - Routing of in-messages (by message type; by request/response) uses quicker data structures. Less heap-allocating when registering a request via
[a]sync_request(). - With a 2-pipe underlying
transport::Channel, the smaller-max-message-size pipe can receive into a smaller target blob (reduces allocation). - Heap-backed messages: The first (often, only) serialization segment, allocated and zero-filled for each message, now defaults to ~8KiB instead of the pipe's max message size (~64KiB with
Native_socket_stream); further segments grow as needed. Also, allocate-and-zero-fill effectively usescalloc()instead ofmalloc()+memset()(~25% faster in our tests).
- Receive lower-layer messages/blobs in batches, automatically, when the underlying pipe recommends it; in practice for
- Optional performance tuning:
Add knobs to set the size of a message's first (often, only) serialization segment. A decent estimate can help performance under load:- Per-
Channeldefault: Optionalsegment1_szarg to the tag ctors (Serialize_via_heap,Serialize_via_session_shm,Serialize_via_app_shm) and toheap_fixed_builder_config(). - Per-message: New
create_msg()overload taking a message-size estimate.
- Per-
- (
struc::Channelonly) Concurrency: All methods are now formally safe for mutually concurrent execution on the same object (previously only certain accessors andcreate_msg()).sync_request()s execute fully serially, even if a blocking-wait is necessary; during such a wait any other (non-blocking) method executes immediately.- If such a concurrent
send()orasync_request()hoses the channel, the waitingsync_request()wakes and emits newerror::Code::S_SYNC_OP_INTERRUPTED_BY_CONCURRENT_NB_ERROR. ipc_shm_arena_lend#61
- For heap-backed messages: A capnp leaf (list, blob, string) too big for 1 low-level message no longer yields error (
S_INTERNAL_ERROR_SERIALIZE_LEAF_TOO_BIG) on the send attempt. Instead it is automatically split across low-level messages and reassembled on receipt. - Add stats:
stats(),stats_reset(), and histogram-configuring methods. More info on stats below. - (
struc::Channelonly)owned_channel(),owned_channel_mutable()now return a proxy that holds a lock for the duration of the expression, so access such as.owned_channel()->blob_send_stats()is thread-safe. - Add
remote_peer_process_credentials(): Available for anyChannel, whether or not it includes aNative_socket_stream. The opposing process's information is exchanged internally ahead of any user message. sync_request(): While waiting, periodically (S_SYNC_OP_PEER_PROCESS_LIVENESS_CHECK_PERIOD, 500 ms) check whether the opposing process is still running, and stop waiting if not. Protects against a hang in case the IPC-transport does not signal the opposing process's death.- Add
remote_peer_process_liveness_check(): If the opposing process is no longer running, hoses the channel withtransport::error::Code::S_PEER_PROCESS_NO_LONGER_EXISTS.
- Add
- On
Channelbecoming hosed, forget all registered handlers (including those fromexpect_msg*(),[a]sync_request()), which also releases their captures. - Fix: If a handle-bearing
Msg_outwas destroyed while its send was deferred by would-block, a closed (or reused) descriptor could be transmitted. Rare: requires both a full kernel out-buffer and unlucky timing. - Fix (2-pipe
Channels): Graceful-close arriving on one in-pipe immediately hosed theChannel(as if it were an ungraceful error), so messages still in flight on the other pipe could be silently dropped. Now delivery continues until both in-pipes close. - Fix
Channel::start(),sync_io::Channel::start_and_poll()given very early low-level errors: With 2 pipes, setup proceeded to pipe 2 after an error on pipe 1; and an error sending protocol negotiation could go unreported (the on-error handler did not fire). - Hardening: Against a buggy opposing Flow-IPC or corrupt data: avoid leaking received native handles; check bounds, alignment, and size of serialized data; fail more loudly on protocol violations during channel setup; and properly report
error::Code::S_STRUCT_CHANNEL_INTERNAL_PROTOCOL_MISMATCH_GOT_NO_HNDLas an error. - Minor fixes (none affect Flow-IPC's own use of these APIs):
- Fix send APIs during log-in phase: Certain improper inputs returned
false, as promised, but further use of theChannelwas undefined behavior. - Fix
send(),[a]sync_request()afterasync_end_sending(): Throwing form threw, instead of returningfalseas documented. - Fix
undo_expect_msgs(),undo_expect_responses(): Could undo a 1-off expectation (against documented contract). - Fix
[un]set_[remote_]unexpected_response_handler()on a hosedChannel: Now always returnfalseand no-op.
- Fix send APIs during log-in phase: Certain improper inputs returned
- Performance improvement:
struc::Msg_in,Msg_out:- Breaking change: Replace
Msg_in::native_handle_or_null() constwithemit_native_handle_or_null(), which gives ownership to the caller. If it is not called,~Msg_in()closes the native handle. This also fixes a (possible) leak: a received handle that the user ignored was never closed. - Add
Msg_in::serialization_size(). - Breaking change (tiny):
Msg_outmove-assignment to itself is now undefined behavior (it was a no-op). - Fix
Msg_out::store_native_handle_or_null(): Source was not nullified if it held the same raw handle value as the target.
- Breaking change: Replace
- Add stats
structs (counters, gauges, histograms):struc::stat::Channel_stats,struc::sync_io::stat::Channel_stats: Per-Channel; obtain viaChannel::stats().struc::stat::Serializer_stats: Serialization allocations and sizes, including big leaves; process-wide for heap-backed messages viaHeap_serializer_global_stats(see alsoheap_serializer_info_dump()).
- capnp-serialization layer, direct use (
Heap_fixed_builder,Heap_reader, and theStruct_builder,Struct_readerconcepts):- Add
ostreamable_capnp_full(),ostreamable_capnp_brief(): Print any capnpReaderto anostream. - Remove frame-postfix capability (frame-prefix remains). Nothing in Flow-IPC used it.
- Minor fix (does not affect Flow-IPC's own use of this API):
Heap_fixed_builder::emit_serialization(): non-throwing form did not clear*err_codeon success.
- Add
ipc::session
- Concepts and main APIs are unchanged. This is mostly internal stability work. See "API notes" for the few items that may need attention on upgrade.
- Note: The items below apply equally to SHM-enabled variants (e.g.:
ipc::session::[shm::...::]Session_server). - Session setup:
- Add safety checks:
Client_session::sync_connect()checks the OS-reported identity of the opposing process (PID against CNS (PID file); UID, GID, executable path againstServer_app), asSession_serverhas always checked the client. On mismatch it emits newerror::Code::S_CLIENT_MASTER_LOG_IN_SERVER_APP_INCONSISTENT_CREDS.Session_serverctor checks own executable path againstServer_app::m_exec_path, in addition to UID, GID. On mismatch it emits newerror::Code::S_SERVER_APP_EXEC_PATH_INCONSISTENT.
Session_serverctor: AnApp::m_namethat is empty or contains theShared_nameseparator (underscore) now emitserror::Code::S_INVALID_ARGUMENT.- Fix
Client_session::sync_connect(): If the opposing process went down at just the wrong moment, behavior could be undefined. - Log-in: Add an internal timeout in case of buggy opposing-process behavior.
- Add safety checks:
Session::open_channel()(on-demand channel opening; cf. init-channels ready at session start):- Fix: If both sides called
open_channel()at about the same time, an internal cross-process deadlock made each call block for 60 seconds, then fail with a timeout error. Now both complete normally. ipc_session#38 - Fix
Client_session::open_channel()(MQ-enabledClient_sessiononly): If the opposing user closed the newChannelimmediately in its passive-open handler, behavior was undefined (could trip an assertion). Now it emits a non-fatal system error, leaving the session intact. - Fix
Server_session::open_channel()on an already-hosed session: It emitted the non-fatal resources-unavailable error, instead of returningfalseas documented. - Fix (session-server side; socket-stream-enabled
Sessiononly): If the session's internal socket went down (e.g., the opposing process died) just as aChannelwas being created, it could trip an assertion. This could occur inasync_accept()opening init-channels,Server_session::open_channel(), or the opposingClient_session::open_channel(). Now that channel-opening fails normally, and the session-hosing is reported as usual. - Hardening: A bad
open_channel()response from a buggy opposing Flow-IPC (or corrupt data) now yields the non-fatalerror::Code::S_SESSION_OPEN_CHANNEL_REMOTE_PEER_REJECTED_PASSIVE_OPENerror, instead of tripping an assertion or being taken as success.
- Fix: If both sides called
Session_server:- Fix
~Session_server(): Could crash, if session-open(s) were in progress at that exact moment. Now all accept activity is stopped, and any on-done handler has finished, before destruction proceeds. async_accept(&S): First makesSas-if default-constructed, synchronously. It was formally allowed to target a session already in PEER state, but that case was not designed for properly.- Fix
async_accept(): On certain failures it modified the out-args, though documented to leave them untouched. - Fix throwing ctor form: The resources of failed log-ins (memory, an internal thread) were not released until
Session_serverdestruction. - Add lifetime tenets to the docs (
Session_serverand Manual): What to destroy in which order; which departures work, though not recommended.
- Fix
- Session end, liveness:
- Fix (SHM-jemalloc-enabled
Session): Depending on timing, the on-error handler could fire witherror::Code::S_SESSION_FINISHEDupon entering theSessiondtor, though the user was the one ending the session (formally allowed but unhelpful). - Increase the idle timeout that detects an unresponsive opposing process from 5s to 15s, to be more forgiving in very slow or overloaded environments. (This is not based on field observations.) Auto-ping period remains 2s.
- Both values (timeout, auto-ping period) are now public constants in new
Session_cfg.
- Both values (timeout, auto-ping period) are now public constants in new
- Fix (SHM-jemalloc-enabled
- Fix native handle leaks during
Channelcreation, on unlikely errors involving an MQ pipe (if so configured): 1 on the server side, 1 on the client side. - Fix
Native_socket_stream::remote_peer_process_credentials()on aChannelopened viaipc::session: It returned values for the calling process, not the opposing one, due to the internal use ofsocketpair(). ipc#148 - Add stats:
Session::info_collector()gives access to the (internal) session master channel's stats (structured and transport layers).- These can confirm that this background traffic (initial setup, channel opening, liveness pings) is negligible; and show how many channels are being opened over time.
- Add
Session::remote_peer_process_credentials()(UID, GID, PID). - Add
Session_server::mq_msg_size_limit(), and a mutator of the same name, to override the max message size of session-created MQ pipes. ipc#145- The default is unchanged. For SHM-enabled
Sessions it is small (512 bytes), enough for the SHM handles sent by a SHM-backedstruc::Channel.- However, this is ill-suited if you prefer to use a heap-backed
struc::Channel, or lower-level transmission of blobs larger than 512 bytes. - If so, recommendation: Configure session for
Native_socket_streampipes instead of MQ pipes.- If staying with MQ pipes, use the mutator to set this higher. (8KiB is typically the maximum for POSIX MQs; no such cap for bipc MQs.)
- However, this is ill-suited if you prefer to use a heap-backed
- Code generated for MQ-enabled
Session_server<...>only.
- The default is unchanged. For SHM-enabled
- For generic programming: Add
S_IS_SRV_ELSE_CLI,Sync_io_obj,Async_io_objtoSessions, and the latter 2 toSession_server. - Minor fix:
ensure_resource_owner_is_app()logged a misleading WARNING on mismatch, implying it was unable to check. The emitted error was correct. - Minor fixes (none affect Flow-IPC's own use of these APIs):
- Fix
sync_io::Session_server_adapter::async_accept()beforestart_ops(): Undefined behavior, instead of returningfalseas documented. - Fix
sync_io::Server_session_adapter::init_handlers(): Erratic if called before successfulasync_accept(), or after a hosed-error was emitted. - Fix
get_logger(),get_log_component()on a moved-fromSession: Could crash. - Fix
Structured_msg_reader_configof non-SHMSessions: Aliased the builder config by mistake, a compile error if used. - Fix
Session_server::async_accept(): A missing lambdamutablecaused a compile error ifn_init_channels_by_srv_req_funcormdt_load_funchad a non-constcall operator.
- Fix
ipc::transport::struc::shm::rpc (new)
Zero-copy-enabled capnp-RPC. ipc#121
- Background: capnp-RPC, a Remote Procedure Call framework, sits atop capnp serialization: You define
interfaces in a schema; one process implements them; another calls their methods, receiving promises for the results. In Flow-IPC context, it is an alternative tostruc::Channelmessaging. Its conversations consist of capnp messages.- Vanilla capnp-RPC copies each capnp message, including large ones, into and out of a socket.
- This Flow-IPC module allows ~identical use of capnp-RPC, but with zero-copy underneath: The capnp messages are stored in SHM and never copied.
- Performance: Unlike with vanilla capnp-RPC, message size no longer matters. For small messages expect performance similar to vanilla capnp-RPC.
- Planned: Adaptive hybrid heap/SHM-backed serializer. Small messages would be heap-backed (copied, which is cheap at that size), larger ones SHM-backed, getting the better performance at each size. It would apply to
struc::Channelas well as capnp-RPC. ipc#160
- Planned: Adaptive hybrid heap/SHM-backed serializer. Small messages would be heap-backed (copied, which is cheap at that size), larger ones SHM-backed, getting the better performance at each size. It would apply to
- Performance: Unlike with vanilla capnp-RPC, message size no longer matters. For small messages expect performance similar to vanilla capnp-RPC.
- See the new Manual chapter, "Zero-copy capnp-RPC." Example application
test/suite/rpc_demoreplicates capnp's Calculator sample.- For perf comparisons, or as a fallback, zero-copy can be disabled at run time, invoking vanilla capnp-RPC underneath. For example:
Ez_rpc_clientctor argsans_shm_transport,Ez_rpc_serverctor argtransport_method_func.rpc_demo --no-zcexercises this. The lower-level APIs below offer the equivalent.
- For perf comparisons, or as a fallback, zero-copy can be disabled at run time, invoking vanilla capnp-RPC underneath. For example:
- Key APIs (each has a vanilla capnp-RPC counterpart, so use code stays similar; see Manual chapter for full mapping):
Session_vat_network: The core. It implementscapnp::VatNetwork, replacingcapnp::TwoPartyVatNetwork, acting similarly to it except with messages stored in SHM, never copied.- All other core capnp-RPC facilities remain in use, including
RpcSystemand the KJ event loop.
- All other core capnp-RPC facilities remain in use, including
Ez_rpc_client,Ez_rpc_server: Counterparts ofcapnp::EzRpcClient,EzRpcServer. They set up everything, including the event loop, in a few lines.- Client connects once.
- Server automatically serves any incoming such connections, including concurrent and overlapping ones.
- For finer event-loop control -- as with vanilla capnp-RPC -- use the
RpcSystem-level approach (next bullet).
Client_context,Context_server(yieldingServer_contexts): Counterparts of connecting or accepting a socket and wrapping it in aTwoPartyVatNetwork, for when you maintain the KJ event loop yourself. They establish the Flow-IPC session and supply theVatNetwork(.vat_network()); you create and drivecapnp::RpcSystemover it as usual (capnp::makeRpc{Client|Server}()).
- Addressing: As with all Flow-IPC sessions, the server is identified via
ipc::session::App, a simple abstraction, as opposed to a socket address as in vanilla capnp-RPC. This brings safety checks: Only the specified client applications may connect, and each side verifies the other's identity (including executable path, UID, GID). - Works with SHM-classic and SHM-jemalloc: Provide the suitable session type as template parameters to
Ez_rpc_{client|server},Context_server, etc.ipc::session::shm::{classic|arena_lend::jemalloc}::rpccontains the suitable aliases.
- Access to the rest of Flow-IPC:
Client_context,Server_contextprovide.session(), and through it SHM arenas (for placing C++ data structures directly in SHM) and additional channels alongside the RPC conversation. -> streammethods (capnp-RPC streaming): The flow-control window is a knob,Session_vat_network::streaming_flow_window_ki().
ipc::shm::stat (new)
- Namespace containing stat-set
structs, documented in detail, common to SHM-classic, SHM-jemalloc, and SHM-providers in general. - The
structs (Owner_obj_stats,Lender_obj_stats,Borrower_obj_stats,Live_obj_stats,Shared_arena_obj_stats): Objects constructed, lent, borrowed, destroyed; live object counts and relevant high-water marks. - They are accessible as part of each SHM-provider's stats, on its arena (reachable via
Session::session_shm(),app_shm()): SHM-classicPool_arena::arena_stats(),local_stats(); SHM-jemallocIpc_arena::sharded_stats(), and more.info_dump()on the arena grabs everything, ready to print.
ipc::shm::classic (SHM-classic)
- Fix
Pool_arena::construct<T>(): For certain object sizes, it took a few bytes fewer from the SHM-pool than necessary, leading to unpredictable behavior. ipc_shm#29- This affects all consumers of SHM-classic: direct native storage or SHM-classic-backed
struc::Channel;ipc::session-created or by directPool_arenaconstruction.
- This affects all consumers of SHM-classic: direct native storage or SHM-classic-backed
- Fix: SHM-classic pools were not sparse; now they are sparse by default, as documented. Sparse here means RAM is committed only when a page is first touched (e.g., by allocation); until then it is only vaddr space. ipc_shm#34
- Add
Pool_arena::commit(): Take the RAM for the entire arena (<=> pool), at any time; a shortage (if any) is reported as an error at that point.- In effect, this gives the choice to work in either fashion. Initially any
Pool_arena(aliasipc::session::shm::classic::Session::Arena) is sparse, taking only a small amount of RAM (even at theipc::session-default size, 2Gi bytes). Callingcommit()at any time makes it non-sparse.
- In effect, this gives the choice to work in either fashion. Initially any
- Add
Pool_arena::close_shm_object_handle(): Release the SHM-pool handle (FD) kept forcommit(), if it will not be needed subsequently. - Correct the docs that related
ENOSPCto the (sparse) pool size. - Guided Manual: Explain how the Linux SHM-use limit works, what happens on hitting it, and the trade-off between working with sparse versus non-sparse SHM-arenas.
- Add
- Fix
Pool_arena::construct<T>(): If theTctor threw, its allocation was not undone. - Performance improvements:
- Skip certain internal steps of
construct<T>()and later destruction, whenTis trivially-destructible. - Use less strict atomic operations on the object reference count.
- Omit an unnecessary Boost.Interprocess index; avoid a certain heap allocation.
- Skip certain internal steps of
- Hardening:
Pool_arena::borrow_object()rejects a corrupt or erroneous input blob, returning null and logging a WARNING: wrong size; pointee outside the SHM-pool vaddr range; or pointee plussizeof(T)and internal metadata beyond the end of that range. Reported by Sebastián Alba Vives (@Sebasteuo). ipc_shm#31- Also: Defensively handle unusually-aligned inputs and atypical vaddrs, avoiding processor exceptions or exotic code generation.
- Add stats (for what is tracked, see
ipc::shm::statabove):Pool_arena::arena_stats[_reset](): Per-arena/pool, kept in SHM, so readable from any process having the pool open. A read-onlyPool_arenais enough, so a monitoring process can observe a pool with no ability to corrupt it.Pool_arena::local_stats[_reset](): Per-Pool_arenaobject.Pool_arena::arena_size(),arena_stat_free_size(): Arena/pool size; how much of it is available now.Pool_arena::info_dump(): All of the above.
- Out of pool space:
Pool_arena::allocate()(and soconstruct<T>()) throwsstd::bad_alloc, notbipc::bad_alloc(consistently with SHM-jemalloc). - Add aliases
Pool_arena::Activator,Allocator<T>(replacingPool_arena_activator,Pool_arena_allocator<T>). - Minor fixes:
Pool_arena::is_handle_in_arena()undefined behavior if the ctor had failed to open or create the SHM-pool. Now returnsfalse, matching otherPool_arenaAPIs.- Rare
error::Code::S_SHM_BIPC_MISC_LIBRARY_ERROR, emitted when Boost.Interprocess reports a failure with no system error code (such as finding a corrupt pool on open), had value0, so the failure erroneously read as success; now it has an appropriate non-zero value. Pool_arenalogs using proper log-componentSHM(was erroneouslyTRANSPORT).- The SHM-classic error category is named
ipc/shm/classic(was erroneouslyipc/shm).
ipc::shm::stl
- Work around a Boost.Container bug, for those storing containers in SHM via
Stateless_allocator: Erasing from avector(orflat_{map|set}) ofbasic_string,deque, orstable_vectorelements leaked the memory of moved-from elements.- Attn: The worked-around bug is in Boost; and it is not limited to SHM/this allocator. With other allocators, including
std::allocator, it remains through Boost 1.92 (fixed upstream since). - What happened: A new unit test of in-SHM container combinations reproduced this. We reported it upstream (boostorg/container#351) and added this work-around for Flow-IPC SHM users.
- Attn: The worked-around bug is in Boost; and it is not limited to SHM/this allocator. With other allocators, including
Stateless_allocator: Adddifference_type,size_typealiases. Certain containers (at leastboost::container::deque) failed to compile without them, when used with SHM-jemalloc.- Hardening:
Stateless_allocatorwithalignof(T)greater thanalignof(std::max_align_t)is now a compile error, rather than risking a misaligned object. Arenas (from SHM-classic, SHM-jemalloc) guarantee only the latter, likemalloc().
ipc::transport::struc::shm (SHM-backed structured messages, notably for struc::Channel transmission)
- Add
Capnp_message_reader, counterpart ofCapnp_message_builder, for those working directly with capnp serialization in SHM.- Internally
shm::Reader(from which it was split out) and capnp-RPC (new module; see above) use it.
- Internally
Capnp_message_builder:- Add optional initial-segment-size ctor arg.
lend()works with any capnpstructhaving ashmHandleSerializationfield, not only the one internal type; so it can be used with your own schema.- Free functions
capnp_set_lent_shm_handle(),capnp_get_shm_handle_to_borrow(): Likewise.
- Free functions
- Add stats: SHM-backed message building and reading, process-wide per SHM-provider (messages built and outstanding, capnp-segment allocation sizes and counts, oversized leaves, and more).
stat::serializer_info_dump(): Grab a printable snapshot of all of it (plus the heap-backed counterpart).- Stat-set
structsstat::Core_serializer_stats(message payload in SHM),stat::Outer_serializer_stats(small heap-side envelope storing the SHM-handle to that payload). stat::{Core|Outer}_serializer_global_stats<Arena>: The underlying singletons.reset()is accessible here.
- Performance: Reduce by a few bytes the per-message payload sent over low-level IPC.
ipc::session::shm::classic (SHM-classic-enabled sessions)
- Hardening:
Session::borrow_object()rejects (returning null) a wrong-size blob or an invalid internal scope-ID. The latter could trip an assertion. - Improvement: A PEER-state
Server_sessioncan outlive theSession_serverthat generated it, even if using app-scope arenas (app_shm()). It was previously undefined behavior.- We still recommend, here and in docs, keeping
Session_serveralive while any IPC is ongoing.
- We still recommend, here and in docs, keeping
- Fix
Client_session::sync_connect(): On failure to open a SHM-pool created by the opposing process, behavior was undefined (at best an assertion trip). Now it emits the error. - Fix: If a
Sessionwas destroyed just as it was starting up, a data race could occur in its dtor. Found by a sanitizer, with errors contrived by a stress test. session_shm_builder_config(),app_shm_builder_config(): Add optionalsegment1_szarg (cf.struc::Channel::heap_fixed_builder_config()).
ipc::shm::arena_lend::jemalloc, Shm_session (SHM-jemalloc)
- Note: Normal use of
Ipc_arenaandShm_session(construct(),lend_object(),borrow_object()), and of SHM-jemalloc throughipc::session, is unchanged in this release. See "API notes" for the items that may need attention on upgrade. - Performance deep-dive. ipc#143
- Goal in short: Ensure SHM-jemalloc performs viably under heavy multi-threaded load.
- Background: jemalloc is a commercial-grade memory manager, by default working in regular heap. SHM-jemalloc hooks into and wraps jemalloc to achieve a safe, garbage-collecting cross-process SHM system that benefits from jemalloc's efficiencies as a mature memory manager. The design required to achieve this integration is complex, and SHM-jemalloc's performance has been an open topic. Here we aimed to test SHM-jemalloc's speed under load and improve it as needed.
- This project: We executed a detailed load-testing/profiling/optimization dive into SHM-jemalloc. We ran load tests on a large multi-threaded server application (split into two with heavy use of SHM-jemalloc, transmitting both native structures and
struc::Channelmessages), analyzing flame graphs and eliminating hot spots. This resulted in major (internal) changes.- Before the changes, throughput under load quickly collapsed.
- After the changes -- with no change to the essential APIs -- the application performed well overall, and no particular perf hot spots remained.
- A summary of these internal changes follows.
- Cross-process garbage collection (GC) of
arena.construct()ed objects now internally involves incrementing/decrementing a per-object atomic use-count stored in SHM.- Previously: GC involved an (internal) IPC borrower=>owner message per
shm_session.borrow_object()call per object, resulting in potentially massive processor churn. (This is now replaced by a single atomic decrement.)
- Previously: GC involved an (internal) IPC borrower=>owner message per
- Pointer lookups (pointer=>vaddr, vaddr=>pointer) do not involve thread contention. All relevant (internal) data structures are thread-local.
- Previously: Every translation involved a map lookup behind a central per-process lock. Despite the brevity of the lookup, under load this bogged the system down.
Ipc_arena::Pointer(alias toShm_pool_offset_ptr): Comparison operators and pointer-minus-pointer work from the pointers' internal representation, avoiding lookups in most cases. These operations execute frequently, e.g., in iterator-based loops over SHM-stored STL-compliant containers.- Thread caching:
Ipc_arena::construct(),allocate(),deallocate()use a jemalloc thread cache, automatically and correctly per thread and arena.- Remove
Ipc_arena::construct_thread_cached()API. (It was experimental: it required opt-in and did not apply toIpc_arena::allocate(), hence to STL-allocator allocations.)
- Remove
- Eliminate overzealous logging: Every
Shm_session::lend_object()(so for each SHM-jemalloc-backedstruc::Channelmessage, among other uses), logged an INFO-level message.
- Add
Thread_cache: Manages one jemalloc tcache per thread per arena.- SHM-jemalloc uses it (internally) automatically.
- It can also be used directly by jemalloc users unrelated to SHM.
- Its docs explain how jemalloc tcaches work, including how these relate to jemalloc arenas.
- Add stats. (Visibility into SHM-jemalloc was a key goal: See the stats section of the Manual's SHM chapter.)
Ipc_arena::info_dump(),Shm_session::info_dump(): All of the below, ready to print. The former optionally includes jemalloc's native stats dump.- Each stat-set can also be consumed individually as a
struct, and most can be reset:- Per arena, object-level activity: Owner, lender, live objects, deallocation internals.
- Per arena, SHM-pools: Stats, as well as a listing of live pools.
- Per arena, jemalloc: A curated, documented selection of jemalloc's own stats relevant to this arena.
- jemalloc API's full stats output is enormous and not straightforward to consume safely.
- Process-wide, owner side: Pointer lookups, use-count SHM-pools.
- Borrower side, per session and process-wide: SHM-pools (including a live listing), pointer lookups.
- Add
Ipc_arena::this_thread_gc(): See "Behavior changes" under "API notes." - Fix
Shm_session: Could trip an assertion and/or become unstable under intense concurrent allocation load, internally due to an unprotected concurrent access. ipc_shm_arena_lend#61 Ipc_arena::allocate()(hence alsoconstruct<T>()) throwsstd::bad_allocon failure, instead ofallocate()returning null (which is not a valid way for an STL allocator to report failure).Ipc_arena::construct<T>(): Gracefully handle potential errors (most notablyTctor throwing), undoing any preceding steps as needed.Shm_session: Enable liveness checks on its internal channel, so that an opposing process that is stuck (not dead) cannot block SHM-jemalloc APIs (even potentially across threads) indefinitely.- Hardening:
deallocate()methods arenoexcept, as allocators formally require.Shm_session::borrow_object(): Defensively handle unusually-aligned inputs.Ipc_arena::create()throws if jemalloc'sopt.retainis disabled. (It is enabled by default in 64-bit Linux.)- A very large allocation, which would require a SHM-pool too large for
Ipc_arena::Pointerto address, fails normally (std::bad_alloc).
- Fix: For each destroyed
Ipc_arena, avoid leaking certain resources (not RAM) associated with jemalloc's metadata block: a native handle (FD) and a mapped vaddr area (until process exit); a pool name in/dev/shm(until the application next executes). - Documentation: Most of SHM-jemalloc code is now in the generated Reference.
- Includes key user-facing items:
Ipc_arena,Shm_session, as well asThread_cache,Memory_manager.
- Includes key user-facing items:
ipc::session::shm::arena_lend::jemalloc (SHM-jemalloc ipc::session support)
- Fix session opening, on either side: If an internal socket error occurred at just the wrong moment, behavior was undefined (at best an assertion trip).
- Add
error::Code::S_MASTER_SHM_UNEXPECTED_TRANSPORT_ERROR, emitted when the session's own channel was found to be hosed while setting up internal resources. - A related internal error during session opening is detected as early as possible.
- Add
- Fix
Session_server::async_accept(): On certain unlikely errors in session setup, it could leak a native handle (FD). session_shm_builder_config(),app_shm_builder_config(): Add optionalsegment1_szarg (cf.struc::Channel::heap_fixed_builder_config()).rpc: Add aliases for zero-copy capnp-RPC. (Seeipc::transport::struc::shm::rpc.)
Build/dependencies
- Dependency bump: Flow v2.0.0 to v3.0.0.
- Transitive dependency bump of
fmtv10.0.0 to 11.0.2. flow#104
- Transitive dependency bump of
- Dependency bump: capnp v1.0.1 to 1.0.2. ipc#135
- Dependency bump: jemalloc v5.2.1 to v5.3.0.
- v5.2.1 (from 2019) has a locking bug in arena destruction, which can cause a crash when SHM-jemalloc stats are read while an arena is being destroyed.
ipc_shmnow linkslibcapnp-rpc, which is part of a capnp install.- Compatibility with Boost 1.91.
- Avoid
boost::interprocesscontainer aliases recently deprecated by Boost, which caused verbose build messages. - Eliminate issues in CMake builds of test/demo programs which made them unreliable in parallelized builds. flow#106
Tests
unit_test: In v2.0.0 only SHM-jemalloc had test cases (transport_testet al covered the rest); now every module does (450+ test cases): ipc#83ipc::util, unstructured transport: IncludingChannel,Native_socket_stream_acceptor,Persistent_mq_handleimplementations,Protocol_negotiator, transport stats,Native_handle,Shared_name.- Structured transport: Including
struc::[sync_io::]Channel(log-in phase,sync_request()with timeout/concurrency/closure, hosing and end-sending corner cases, unexpected-response machinery, adversarial headers, stats),Msg_out,Msg_in,Heap_fixed_builder,Heap_reader, serializer stats. ipc::session: Including session connect and identity checks (stale or corrupt CNS, rejected identities, server self-check),open_channel()(crossing, peer closes immediately),Session_serverdestroyed mid-accept, session end during connect, concurrent accepts,sync_ioadapter guards, graceful end, and kernel-persistent resource cleanup after civilized exit and crash.- SHM-classic and general SHM: Including
Pool_arena(sparseness andcommit(), read-only open, out of space, ctor that throws),Pool_arenastats,ipc::shm::stl(allocator and containers, with each SHM-provider),borrow_object()hardening, capnp-RPC streaming flow control. - SHM-jemalloc: Including use-count tracking, thread-local object database,
Ipc_arenaerrors and stats,Thread_cache,Memory_manager,Shm_pool_offset_ptroperators,Shm_sessionlifetime and stress, lend and borrow across processes.
- Integration tests:
- Expand
transport_testscripted mode, including batch-receive and graceful-close scenarios. - Add test/demo program
rpc_demo(capnp-RPC).
- Expand
GitHub CI pipeline
- Conan 1 => 2 migration. flow#115
- Reduce pipeline run time: Dependency caching; parallelized core build step.
- Add "early-warning" jobs against newest (as opposed to only min-supported) dependency versions: Boost, capnp, jemalloc.
- Core-dump stack traces, as artifacts next to the logs; refinements to the sanitizer configurations; job filter for manually-triggered runs.
Guided Manual
- Add chapter "Zero-copy capnp-RPC" (new module).
- Add Stats sections to the relevant chapters and a stats overview (new cross-module feature).
- Add chapter on multi-split IPC universes (was a stub chapter).
- Recommend the proper session teardown order.
- Broad review, including fixes to example code.
- Unix domain sockets are the default transport in examples; stated to be a reasonable default choice.
Full Changelog: v2.0.0...v3.0.0
Submodule repository flow
- Release v3.0.0: release notes.
- Previously: Release v2.0.0.
API notes
ipc::util
- New/improved APIs:
ipc::util::Native_handle:- Add
close(),is_open(),dup();make_default(),is_allocated(). - Add
constexpr+noexceptto ctors, assignment, comparisons,null(),swap(). constexprifyS_NULL_HANDLE.
- Add
Own_native_handleowns/auto-closes aNative_handle.duped_native_handle()makes one from a.dup();disowned_native_handle()releases it.Shared_name:- Add
Shared_name::ct_from_int<Integer>(). constexprifyShared_name::S_MAX_LENGTH,S_SEPARATOR.
- Add
NULL_PROCESS_CREDENTIALS.process_running(pid):EPERM=>true;pid <= 0=>false.
- Breaking changes:
- Remove
ipc::util::Use_counted_object. (Not IPC-related; Flow-IPC no longer uses it.) - Move
ipc::util::Default_init_allocatortoflow::util::Default_init_allocator(Flow). It gainedrebindin the move.
- Remove
ipc::transport (unstructured)
- New/improved APIs:
asio_local_stream_socket:Protocol_byte_stream(stream,SOCK_STREAM),Protocol_pkt_stream(connected-datagram,SOCK_SEQPACKET).Msg_batch_in(centered aroundMsg_batch_in::nb_read()).
nb_{read|write}_some_with_native_handle(): Work with either protocol (deduced fromPeer_socket<Protocol>*arg); allow buffer sequences (scatter/gather).- With
Protocol_pkt_stream: Detect truncation if target buffer too small => emiterror::Code::S_MESSAGE_SIZE_EXCEEDS_USER_STORAGE.
- With
[sync_io::]Native_socket_stream:- Add
async_receive_{blob|native_handle}_batch(),{Blob|Native_handle}_batch_in(batch type),S_RCV_{BLOB|NATIVE_HANDLE}_BATCH_SZ_RECOMMENDATION. - Add
{blob|native_handle}_send_stats[_reset](),{blob|native_handle}_receive_stats[_reset](). send_native_handle(): Handle may be closed immediately after the call returns.remote_peer_process_credentials(): Callable concurrently with one send-op and/or one receive-op.remote_peer_process_credentials(const util::Process_credentials&): Mutator.
- Add
Native_socket_stream_cfg(compile-time knobs, includingS_MAX_META_BLOB_LENGTH).[sync_io::]Blob_stream_mq_receiver: Addasync_receive_blob_batch(),Blob_batch_in,S_RCV_BLOB_BATCH_SZ_RECOMMENDATION;blob_receive_stats[_reset]().[sync_io::]Blob_stream_mq_sender: Addblob_send_stats[_reset]().Channel: Addasync_receive_{blob|native_handle}_batch(),{Blob|Native_handle}_batch_in,S_RCV_{BLOB|NATIVE_HANDLE}_BATCH_SZ_RECOMMENDATION;{blob|native_handle}_send_stats[_reset](),{blob|native_handle}_receive_stats[_reset]().- Concepts
[sync_io::]Blob_sender,Blob_receiver,Native_handle_sender,Native_handle_receiver:send_native_handle()contract as above.- The above batch-receive and stats APIs.
- Concrete
structs:stat::Blob_snd_stats,Blob_rcv_stats.
- Concrete
sync_io::Async_adapter_sender,sync_io::Async_adapter_receiver(was internal-use; now public).sync_io::Native_socket_stream_acceptor::async_accept(): Returnsfalseifstart_accept_ops()not yet called.Protocol_negotiator::S_VER_ALREADY_SENT(alias for clarity).transport::error::Code: AddS_LOW_LVL_INTERNAL_PROTOCOL_INVALID_HEADER,S_PEER_PROCESS_NO_LONGER_EXISTS,S_SHM_BIPC_MISC_LIBRARY_ERROR.
- Breaking changes:
- Rename
asio_local_stream_socket::ProtocoltoProtocol_byte_stream; make Boost.Asio aliasesAcceptor,Peer_socket,Endpointinto alias templates.- Use (e.g.)
Peer_socket<Protocol_byte_stream>for the previous meaning.
- Use (e.g.)
- Remove
asio_local_stream_socket::async_write_with_native_handle(),async_read_with_target_func(),async_read_interruptible().- Use the
nb_*()functions with your own async-wait (asNative_socket_streamdoes).
- Use the
- Remove
asio_local_stream_socket::release_native_peer_socket().- Use
util::Native_handle::close().
- Use
- Move
sync_io::Native_socket_stream::S_MAX_META_BLOB_LENGTHtoNative_socket_stream_cfg::S_MAX_META_BLOB_LENGTH(nowconstexpr; value 64Ki, was 64Ki minus 2). [sync_io::]Native_socket_stream::remote_peer_process_credentials()returnsconst&(was by-value).- Custom implementations of the
Blob_*/Native_handle_*concepts: Must add the new batch-receive and stats APIs (and honor the upgradedsend_native_handle()contract).
- Rename
ipc::transport (structured)
- New/improved APIs:
struc::[sync_io::]Channel:- Add
remote_peer_process_credentials(),remote_peer_process_liveness_check(). - Add
create_msg(size_t msg_sz_cap_estimate_or_zero, Native_handle&& hndl_or_null = {}). - Add
stats(),stats_reset(),stats_configure_rcv_one_off_request_rtt_histogram(). - Tag ctors,
heap_fixed_builder_config(): Add optionalsegment1_szarg.
- Add
struc::Channel: Addstats_configure_sync_req_latency_histogram();Owned_channel_proxy,Owned_channel_proxy_mutable.struc::Channel: All methods may execute concurrently on the same object, includingsync_request()(see above).struc::Channel_base: AddS_SYNC_OP_PEER_PROCESS_LIVENESS_CHECK_PERIOD,S_PROTOCOL_MIN_VERSION,S_PROTOCOL_MAX_VERSION.struc::Msg_in:- Add
emit_native_handle_or_null()(replacesnative_handle_or_null() const). - Add
serialization_size().
- Add
struc::ostreamable_capnp_full(),ostreamable_capnp_brief().struc::Capped_sz_capnp_message_builder,Capped_sz_capnp_message_reader: Capnp builder, reader over 1 user-supplied memory area.struc::[sync_io::]stat::Channel_stats,struc::Channel_sync_req_stats,Serializer_stats,Heap_serializer_stats,Heap_serializer_global_stats,heap_serializer_info_dump().struc::error::Code: AddS_INVALID_ARGUMENT,S_SYNC_OP_INTERRUPTED_BY_CONCURRENT_NB_ERROR,S_DESERIALIZE_FAILED_REASSEMBLY_FAILED,S_SERIALIZE_FAILED_SPLIT_ENCODING_TOO_BIG,S_SERIALIZE_FAILED_SPLIT_SEGS_TOO_MANY.S_SERIALIZE_FAILED_SPLIT_*: Fromsend()(etc.) if a message needs too many splits (limit ~20).S_INTERNAL_ERROR_SERIALIZE_LEAF_TOO_BIGis no longer emitted bystruc::[sync_io::]Channel.- Remains for direct use of
Heap_fixed_builder::emit_serialization()with auto-splitting disabled.
- Remains for direct use of
- Breaking changes:
struc::[sync_io::]Channel:send(),[a]sync_request(): TakeMsg_out*, notconst Msg_out&.- Use
ch.send(&msg), notch.send(msg). The encoded message is not modified, only internal-use metadata. So do not send the sameMsg_outconcurrently, even via differentChannels; sequentially remains fine.
- Use
struc::Channel::owned_channel(),owned_channel_mutable(): Return locking proxies, notconst Owned_channel&andOwned_channel*.owned_channel(): Use->, not..owned_channel_mutable():->continues to work.
struc::Msg_in: Removenative_handle_or_null() const.- Use
emit_native_handle_or_null().
- Use
struc::Msg_out: Self-move-assignment is now undefined behavior.struc::error::Code: RemoveS_INTERNAL_ERROR_DESERIALIZE_TARGET_ALLOC_FAILED.- A particular internal redesign means this condition is not conceptually possible.
- Relevant only to custom implementations of
Struct_builder,Struct_readerconcepts (advanced):emit_serialization()signature changes (Segment_bufs*, header blob,Split_segments*).Struct_reader:add_serialization_segment(Segment_blob_in&&);deserialization()takesconst Split_segments*; no longer movable or default-constructible.- Frame-postfix configuration removed.
- Direct use of
Heap_fixed_builder,Heap_reader:Heap_fixed_builder::Config:m_segment_szreplaced bym_seg_and_frame_sz_cap,m_segment_sz_init;m_frame_postfix_szremoved;m_statsadded.- Recommend (as before)
Channel::heap_fixed_builder_config()rather than filling out fields manually.
- Recommend (as before)
Heap_reader::Config:m_statsadded (pointer to target stat-set, or null).Heap_fixed_builder_capnp_message_builder: Ctor args changed.
ipc::session
- New/improved APIs:
Session(concept and all impls):- Add
info_collector(),Info_collector. - Add
remote_peer_process_credentials(). - Add
S_IS_SRV_ELSE_CLI,Sync_io_obj,Async_io_obj.
- Add
Info_collector: Addmaster_channel_stats[_reset](),master_channel_transport_{snd|rcv}_stats[_reset](),master_channel_live().Session_server: Addmq_msg_size_limit()(accessor, mutator; each newSessionuses the value as of itsasync_accept()),Sync_io_obj,Async_io_obj.Session_cfg: AddS_PING_PERIOD,S_IDLE_TIMEOUT.error::Code: AddS_CLIENT_MASTER_LOG_IN_SERVER_APP_INCONSISTENT_CREDS,S_SERVER_APP_EXEC_PATH_INCONSISTENT.Client_session::open_channel(): Can emit a non-fatal system error (see above).
- Breaking changes:
Session_mv: Removeheap_fixed_builder_config()(both overloads). The max message size it assumed is no longer constant (seeSession_server::mq_msg_size_limit()).- Use
struc::Channel::heap_fixed_builder_config(), which requires only an unstructuredChannel; or fill outHeap_fixed_builder::Configvalues directly.
- Use
Session: RenameS_SHM_TYPEtoS_SHM_TYPE_OR_NONE.
- Behavior changes that may need attention:
Server_app::m_exec_pathmust now exactly match how the session-server was invoked (as already required ofClient_app::m_exec_path). A wrong value previously had no effect; whereas now:Session_serverctor emits an error (and will not open sessions) if theServer_apparg, describing the server itself, has the mismatch.Client_session::sync_connect()emits an error if the connect-targetServer_apparg has this mismatch.
Server_app::m_user_id,m_group_id:Client_session::sync_connect()now likewise emits an error on a mismatch in the connect-targetServer_apparg.App::m_namemust be non-empty and not contain theShared_nameseparator (underscore); otherwiseSession_serverctor now emits an error.- Notably: Underscores are disallowed, even though earlier docs, alongside the separator rule, allowed them by mistake. Such a name could cause trouble, as it interferes with resource naming. For example, a session-server
fooat startup could remove kernel-persistent resources (MQs, SHM-classic pools) of a running session-serverfoo_bar.
- Notably: Underscores are disallowed, even though earlier docs, alongside the separator rule, allowed them by mistake. Such a name could cause trouble, as it interferes with resource naming. For example, a session-server
async_accept(&S):Sis made empty immediately. If it held an open session (unlikely but formally allowed), that session ends at that point.
ipc::transport::struc::shm::rpc
All new (see above): Session_vat_network, Ez_rpc_client, Ez_rpc_server, Client_context, Context_server, Server_context.
ipc::shm::stat
Add Owner_obj_stats, Lender_obj_stats, Borrower_obj_stats, Live_obj_stats, Shared_arena_obj_stats.
ipc::shm::classic
- New/improved APIs:
Pool_arena: Addcommit(),close_shm_object_handle(),arena_size(),arena_stat_free_size(),arena_stats[_reset](),local_stats[_reset](),info_dump(),Info_dump,Activator,Allocator<T>.Pool_arena::is_handle_in_arena(): Returnsfalse, not undefined behavior, if the SHM-pool was not opened or created.- Add
stat::Arena_stats,Local_stats,Arena_info_dump. error::Code: AddS_SHM_POOL_OPEN_READ_ONLY_FOUND_BUT_UNINIT.
- Breaking changes:
- Remove
Pool_arena_activator,Pool_arena_allocator<T>.- Use
Pool_arena::Activator,Pool_arena::Allocator<T>.
- Use
Pool_arena: Removeconstruct_at().- Use
flow::util::construct_at()(Flow).
- Use
Pool_arena::allocate(),construct<T>(): Out of space throwsstd::bad_alloc, notbipc::bad_alloc.
- Remove
- Behavior changes that may need attention:
- Pools are sparse, as documented (see above). If the system is out of RAM for SHM (Linux: the size limit of the tmpfs underlying POSIX SHM) when a page is first touched, there is no opportunity to report an error: The touching process is killed (
SIGBUS).- If you prefer to learn of a shortage up-front, as an error, call
commit(). In this case be mindful of pool size: All of it is then taken from general availability (withipc::session, 2Gi bytes per pool by default; seepool_size_limit_mi()).
- If you prefer to learn of a shortage up-front, as an error, call
- Each
Pool_arenakeeps an SHM-pool handle (FD) open, forcommit(), untilclose_shm_object_handle()or destruction. Pool_arenaread-only ctor (read_only = true): Do not use until a creating ctor has completed. Doing so can cause undefined behavior, orS_SHM_POOL_OPEN_READ_ONLY_FOUND_BUT_UNINIT; do not rely on the latter.- Other ctor forms are unaffected, and typically a read-only opener learns of a pool only after its creation anyway.
- Pools are sparse, as documented (see above). If the system is out of RAM for SHM (Linux: the size limit of the tmpfs underlying POSIX SHM) when a page is first touched, there is no opportunity to report an error: The touching process is killed (
ipc::shm::stl
- Breaking changes:
Stateless_allocator,Arena_activator: RenameArena_objalias toArena.Stateless_allocator:alignof(T)greater thanalignof(std::max_align_t)is a compile error.
ipc::transport::struc::shm
- New/improved APIs:
- Add
Capnp_message_reader. Capnp_message_builderctor: Add optionalsegment_sz_init_pre_growth,statsargs.- Add
stat::Core_serializer_stats,Outer_serializer_stats,Core_serializer_global_stats,Outer_serializer_global_stats,Serializer_info_dump,serializer_info_dump(). Builder::Config: Addm_segment_sz_init,m_core_stats,m_stats.
- Add
- Breaking changes:
schema::ShmHandle: Rename fieldserializationtoshmHandleSerialization. Encoding is unaffected.Capnp_message_builder: MoveBorrower_allocator,Segment_in_shm_borrowed,Segments_in_shm_borrowedtoCapnp_message_reader.Capnp_message_builder::lend(): Now a template, taking the capnp rootstructandSessiontypes.capnp_set_lent_shm_handle(),capnp_get_shm_handle_to_borrow(): Now templates, on the capnp rootstructtype.Builder::Config: Renamem_top_builder_frame_prefix_sztom_frame_prefix_sz; removem_top_builder_frame_postfix_sz.Builder::emit_serialization(),Reader::add_serialization_segment(),Reader::deserialization(): Signatures changed (cf.Struct_builder,Struct_readerabove).
ipc::session::shm::classic
- New/improved APIs:
Session,Server_session: AddVat_network.session_shm_builder_config(),app_shm_builder_config(): Add optionalsegment1_szarg.rpc(new namespace): AddClient_session,Server_session,Session_serveraliases.
ipc::shm::arena_lend::jemalloc, ipc::session::shm::arena_lend::jemalloc::Shm_session
- New/improved APIs:
- Add
Thread_cache. Ipc_arena: AddAllocator<T>,Activator,this_thread_gc(),info_dump(),Info_dump,sample_hi_wmarks().- Stats:
sharded_stats[_reset](),pool_stats[_reset](),shm_pool_live_info(),memory_manager_stats[_reset](),owner_pool_lookup_global_stats(),obj_db_aux_pool_global_stats(),global_stats_reset().
- Stats:
Shm_session: AddArena,Handle<T>,Borrower_arena_allocator<T>,info_dump(),Info_dump.- Stats:
borrower_pool_stats[_reset](),borrower_pool_stats_process_wide(),borrower_pool_lookup_global_stats(),global_stats_reset(),borrowed_shm_pool_live_info().
- Stats:
ipc::shm::arena_lend::stat,ipc::shm::arena_lend::jemalloc::stat: Add stat-sets and info-dumpstructs.ipc::shm::statholds those common to the SHM-providers (SHM-jemalloc, SHM-classic).
- Add
- Breaking changes:
Ipc_arena:- Remove
construct_thread_cached().- Use
construct(); tcache use is automatic.
- Use
allocate()throws on failure instead of returning null (see above).- Rename
get_arena_id()toget_jemalloc_arena_id().
- Remove
Memory_manager: Its API is replaced; please ignore what was there previously. The default ctor (noLogger*) remains whatIpc_arena::create()needs. It now offers, for jemalloc power users,allocate[_no_tcache](),deallocate[_no_tcache](),create_arena(),destroy_arena(),stats_dump_to_ostream(),stats_dump_to_string(),log_stats_dump().- Remove
ipc::shm::arena_lend::Memory_manager, its base class.
- Remove
- Breaking changes, only if setting up SHM-jemalloc directly, instead of via
ipc::session:Ipc_arena::create(): TakesShared_name pool_name_base, not a name-generator function.Shm_session::create(): Takes, and owns, a plain unstructured channel (Shm_channel) and a session token. Remove the borrower-repository, channel-timeout args.- Channel-related error reporting is simplified: The first disconnection is reported via the handler, uniformly.
Shm_session: Removeset_disconnected().
- Behavior changes that may need attention:
- If a process crashes while holding
app_shm()objects borrowed from a session-server, those objects stay allocated until thatSession_serveris destroyed. Previously, ending that session released them. Session-scope arenas are unaffected. We plan to address this. ipc_shm_arena_lend#71 - If the last reference to an object is dropped in a thread or process other than the one that constructed it, then the object is destroyed and deallocated by the constructing thread: during its subsequent SHM-jemalloc activity, or after its exit. So while that thread is alive but idle, the object remains allocated.
- If that matters to you, call
this_thread_gc()from such a thread as it goes idle.
- If that matters to you, call
- Up to about 1 million
construct()ed objects can be live per constructing-thread per arena. Beyond thatconstruct()throwsstd::bad_alloc.
- If a process crashes while holding
ipc::session::shm::arena_lend::jemalloc
- New/improved APIs:
Session,Server_session: AddVat_network.session_shm_builder_config(),app_shm_builder_config(): Add optionalsegment1_szarg.error::Code: AddS_MASTER_SHM_UNEXPECTED_TRANSPORT_ERROR.rpc: AddClient_session,Server_session,Session_serveraliases.