v3.0.0 #166
ygoldfeld
announced in
Announcements
v3.0.0
#166
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Highlights
struc::ChannelandNative_socket_stream.recvmmsg()).struc::Channelmessages with large leaves (list, blob, string) now transmit without problems. They're internally auto-split/reassembled when needed.ipc::transport::struc::shm::rpc): ipc#121struc::Channel, serialization, sessions, SHM.flow::util::stat, a new general stats API in Flow (a dependency of Flow-IPC).Pool_arena::construct<T>()could take a few bytes too few for certain object sizes. ipc_shm#29Upgrade notes
Native_socket_streamconnect) fails; astruc::Channelset up outside a session reports a protocol-version error.ipc::transport::struc::[sync_io::]Channel::send(),[a]sync_request()now takeMsg_out*instead ofconst Msg_out&: please usech.send(&msg), notch.send(msg).All changes
Across modules
structs and access to recorded stats. ipc#164structmember (counter/histogram, gauge, high-water mark) is documented in the Reference.flow::util::stat:cout << flow::util::stat::print(channel.stats()).channel.stats_reset().session.session_shm()->info_dump(&dump); cout << dump.Logger* logger_ptr = nullptrAPI arg value could cause a crash.gdbet al). flow#71 ipc#85ipc::utilNative_handlefacilities:close(),is_open(),dup()(close-on-exec); RAII/auto-closing ownerOwn_native_handlewithduped_native_handle(),disowned_native_handle();swap().constexpr+noexceptadded all over.Shared_name::ct_from_int()(efficient integer => name).NULL_PROCESS_CREDENTIALS; as-if-default-constructedProcess_credentialsprints asnull.Info_dump_format: Common knobs (verbose, multi-line) for info-dump/stats output elsewhere in Flow-IPC.Use_counted_object. MoveDefault_init_allocatorto Flow (flow::util::Default_init_allocator).ipc::util::process_running(): ipc_core#41EPERMoutcome (process exists, but signaling it is not permitted) was treated as impossible (assertion trip; or yields "not running"). Now: yields "running."set_resource_permissions(): A concurrent fork/exec could leak its temporary FD into the child (O_CLOEXEC).Native_handlemove-assignment: Source was not nullified if it held the same raw handle value as the target.Shared_name::sanitize(),sanitized(): With a non-default locale, characters outside[A-Za-z0-9_]could pass.Shared_name/=operators when the argument overlaps the target (s /= s,s /= s.str(), etc.).istream >> Shared_name: On failure of the underlying string-read it loaded an empty name; now leaves it as-is.Process_credentials::process_invoked_as(): Non-throwing form did not clear*err_codeon success.ipc::transport(unstructured)[sync_io::]Native_socket_stream:SOCK_SEQPACKET) sockets instead of stream (SOCK_STREAM) sockets: OS maintains message boundaries => much less per-message work happens in user-land.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...._{send|receive}_stats(),..._stats_reset(). (SeeBlob_{snd|rcv}_statsbelow.)send_native_handle()contract: You may close handle (FD), as soon as the call returns.Native_handle::dup()if deferral required.Native_socket_stream_cfg: Compile-time constants governingNative_socket_stream(socket type, batch-receive mode, max message size, stats logging).Native_socket_streamtemplate parameters per-object. ipc#156async_end_sending()), while the outgoing direction was in would-block state.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.remote_peer_process_credentials()mutator: Sets what the accessor returns, for socket-streams created more manually (asipc::sessiondoes internally). ipc#148Native_socket_stream,Blob_stream_mq_*, and anything built on them):auto_ping()oridle_timer_run(). Many objects never use those, so no thread is started. ipc#161Channel(and its aliases and subclasses, such asSocket_stream_channel):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.blob_snd(),hndl_snd()(etc.): The advertised check for an uninitialized pipe was missing (undefined behavior).init_blob_pipe(),init_native_handle_pipe(): The advertised check for a duplicate call was missing (silently overwrote).initialized(), with its pipes in NULL state).remote_peer_process_credentials(): Did not clear*err_codeif the channel had no socket pipe.[sync_io::]Blob_stream_mq_sender,Blob_stream_mq_receiver:async_receive_blob_batch()(emulated via one-message MQ receives; implements extended conceptBlob_receiver).blob_{send|receive}_stats(),..._stats_reset().*err_codeon success.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.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.).*_stats()accessors above; reset via*_stats_reset().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):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.timed_send(),timed_receive()(both classes): If woken up repeatedly without success, could report timeout before the specified time elapsed.Posix_mq_handle::interrupt_*(): Could fail to take effect (wrong array length given toepoll_wait()).Posix_mq_handlemove-assignment: Leaked the MQ handle and 2epollFDs of the target.Posix_mq_handlectors: Non-throwing form did not clear*err_codeon success.swap()ofBipc_mq_handles: Bad state if one was mid-interrupt_*().[sync_io::]Native_socket_stream_acceptor: Minor fixes (none affect Flow-IPC's own use of these APIs):*err_codeon success.sync_io::form'sasync_accept()when called beforestart_accept_ops()(undefined behavior): Now logs and returnsfalse.sync_io::form's ctor: Logged a WARNING on success.Protocol_negotiator::compute_negotiated_proto_ver(): Fixes (none affect Flow-IPC's own use of this API):*err_codeon success.ipc::transport::asio_local_stream_socket(low-level Unix-domain-socket utilities beneathNative_socket_stream; also usable directly):SOCK_SEQPACKET) support alongside existing stream (SOCK_STREAM) support: Protocol is now a template parameter, particularly ofnb_{read|write}_some_with_native_handle().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.async_write_with_native_handle(),async_read_with_target_func(),async_read_interruptible().release_native_peer_socket()(useutil::Native_handle::close()). ipc_core#40ipc::transport(structured)struc::[sync_io::]Channel:Major optimization of how we encode/transmit/decode a structured message (
Msg_out) given to a send API (send(),[a]sync_request()).send(),[a]sync_request(): They now takeMsg_out*instead ofconst Msg_out&. A send sets certain internal metadata.Native_socket_streams.[a]sync_request().transport::Channel, the smaller-max-message-size pipe can receive into a smaller target blob (reduces allocation).Native_socket_stream); further segments grow as needed. Also, allocate-and-zero-fill effectively usescalloc()instead ofmalloc()+memset()(~25% faster in our tests).Add knobs to set the size of a message's first (often, only) serialization segment. A decent estimate can help performance under load:
Channeldefault: Optionalsegment1_szarg to the tag ctors (Serialize_via_heap,Serialize_via_session_shm,Serialize_via_app_shm) and toheap_fixed_builder_config().create_msg()overload taking a message-size estimate.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.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#61S_INTERNAL_ERROR_SERIALIZE_LEAF_TOO_BIG) on the send attempt. Instead it is automatically split across low-level messages and reassembled on receipt.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.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.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.Channelbecoming hosed, forget all registered handlers (including those fromexpect_msg*(),[a]sync_request()), which also releases their captures.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.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.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).error::Code::S_STRUCT_CHANNEL_INTERNAL_PROTOCOL_MISMATCH_GOT_NO_HNDLas an error.false, as promised, but further use of theChannelwas undefined behavior.send(),[a]sync_request()afterasync_end_sending(): Throwing form threw, instead of returningfalseas documented.undo_expect_msgs(),undo_expect_responses(): Could undo a 1-off expectation (against documented contract).[un]set_[remote_]unexpected_response_handler()on a hosedChannel: Now always returnfalseand no-op.struc::Msg_in,Msg_out: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.Msg_in::serialization_size().Msg_outmove-assignment to itself is now undefined behavior (it was a no-op).Msg_out::store_native_handle_or_null(): Source was not nullified if it held the same raw handle value as the target.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()).Heap_fixed_builder,Heap_reader, and theStruct_builder,Struct_readerconcepts):ostreamable_capnp_full(),ostreamable_capnp_brief(): Print any capnpReaderto anostream.Heap_fixed_builder::emit_serialization(): non-throwing form did not clear*err_codeon success.ipc::sessionipc::session::[shm::...::]Session_server).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.Client_session::sync_connect(): If the opposing process went down at just the wrong moment, behavior could be undefined.Session::open_channel()(on-demand channel opening; cf. init-channels ready at session start):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#38Client_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.Server_session::open_channel()on an already-hosed session: It emitted the non-fatal resources-unavailable error, instead of returningfalseas documented.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.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.Session_server:~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.async_accept(): On certain failures it modified the out-args, though documented to leave them untouched.Session_serverdestruction.Session_serverand Manual): What to destroy in which order; which departures work, though not recommended.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).Session_cfg.Channelcreation, on unlikely errors involving an MQ pipe (if so configured): 1 on the server side, 1 on the client side.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#148Session::info_collector()gives access to the (internal) session master channel's stats (structured and transport layers).Session::remote_peer_process_credentials()(UID, GID, PID).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#145Sessions it is small (512 bytes), enough for the SHM handles sent by a SHM-backedstruc::Channel.struc::Channel, or lower-level transmission of blobs larger than 512 bytes.Native_socket_streampipes instead of MQ pipes.Session_server<...>only.S_IS_SRV_ELSE_CLI,Sync_io_obj,Async_io_objtoSessions, and the latter 2 toSession_server.ensure_resource_owner_is_app()logged a misleading WARNING on mismatch, implying it was unable to check. The emitted error was correct.sync_io::Session_server_adapter::async_accept()beforestart_ops(): Undefined behavior, instead of returningfalseas documented.sync_io::Server_session_adapter::init_handlers(): Erratic if called before successfulasync_accept(), or after a hosed-error was emitted.get_logger(),get_log_component()on a moved-fromSession: Could crash.Structured_msg_reader_configof non-SHMSessions: Aliased the builder config by mistake, a compile error if used.Session_server::async_accept(): A missing lambdamutablecaused a compile error ifn_init_channels_by_srv_req_funcormdt_load_funchad a non-constcall operator.ipc::transport::struc::shm::rpc(new)Zero-copy-enabled capnp-RPC. ipc#121
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.struc::Channelas well as capnp-RPC. ipc#160test/suite/rpc_demoreplicates capnp's Calculator sample.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.Session_vat_network: The core. It implementscapnp::VatNetwork, replacingcapnp::TwoPartyVatNetwork, acting similarly to it except with messages stored in SHM, never copied.RpcSystemand the KJ event loop.Ez_rpc_client,Ez_rpc_server: Counterparts ofcapnp::EzRpcClient,EzRpcServer. They set up everything, including the event loop, in a few lines.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}()).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).Ez_rpc_{client|server},Context_server, etc.ipc::session::shm::{classic|arena_lend::jemalloc}::rpccontains the suitable aliases.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)structs, documented in detail, common to SHM-classic, SHM-jemalloc, and SHM-providers in general.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.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)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#29struc::Channel;ipc::session-created or by directPool_arenaconstruction.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.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.Pool_arena::close_shm_object_handle(): Release the SHM-pool handle (FD) kept forcommit(), if it will not be needed subsequently.ENOSPCto the (sparse) pool size.Pool_arena::construct<T>(): If theTctor threw, its allocation was not undone.construct<T>()and later destruction, whenTis trivially-destructible.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#31ipc::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.Pool_arena::allocate()(and soconstruct<T>()) throwsstd::bad_alloc, notbipc::bad_alloc(consistently with SHM-jemalloc).Pool_arena::Activator,Allocator<T>(replacingPool_arena_activator,Pool_arena_allocator<T>).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.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).ipc/shm/classic(was erroneouslyipc/shm).ipc::shm::stlStateless_allocator: Erasing from avector(orflat_{map|set}) ofbasic_string,deque, orstable_vectorelements leaked the memory of moved-from elements.std::allocator, it remains through Boost 1.92 (fixed upstream since).Stateless_allocator: Adddifference_type,size_typealiases. Certain containers (at leastboost::container::deque) failed to compile without them, when used with SHM-jemalloc.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 forstruc::Channeltransmission)Capnp_message_reader, counterpart ofCapnp_message_builder, for those working directly with capnp serialization in SHM.shm::Reader(from which it was split out) and capnp-RPC (new module; see above) use it.Capnp_message_builder:lend()works with any capnpstructhaving ashmHandleSerializationfield, not only the one internal type; so it can be used with your own schema.capnp_set_lent_shm_handle(),capnp_get_shm_handle_to_borrow(): Likewise.stat::serializer_info_dump(): Grab a printable snapshot of all of it (plus the heap-backed counterpart).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.ipc::session::shm::classic(SHM-classic-enabled sessions)Session::borrow_object()rejects (returning null) a wrong-size blob or an invalid internal scope-ID. The latter could trip an assertion.Server_sessioncan outlive theSession_serverthat generated it, even if using app-scope arenas (app_shm()). It was previously undefined behavior.Session_serveralive while any IPC is ongoing.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.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)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.struc::Channelmessages), analyzing flame graphs and eliminating hot spots. This resulted in major (internal) changes.arena.construct()ed objects now internally involves incrementing/decrementing a per-object atomic use-count stored in SHM.shm_session.borrow_object()call per object, resulting in potentially massive processor churn. (This is now replaced by a single atomic decrement.)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.Ipc_arena::construct(),allocate(),deallocate()use a jemalloc thread cache, automatically and correctly per thread and arena.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.)Shm_session::lend_object()(so for each SHM-jemalloc-backedstruc::Channelmessage, among other uses), logged an INFO-level message.Thread_cache: Manages one jemalloc tcache per thread per arena.Ipc_arena::info_dump(),Shm_session::info_dump(): All of the below, ready to print. The former optionally includes jemalloc's native stats dump.struct, and most can be reset:Ipc_arena::this_thread_gc(): See "Behavior changes" under "API notes."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#61Ipc_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.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.)Ipc_arena::Pointerto address, fails normally (std::bad_alloc).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).Ipc_arena,Shm_session, as well asThread_cache,Memory_manager.ipc::session::shm::arena_lend::jemalloc(SHM-jemallocipc::sessionsupport)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.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
fmtv10.0.0 to 11.0.2. flow#104ipc_shmnow linkslibcapnp-rpc, which is part of a capnp install.boost::interprocesscontainer aliases recently deprecated by Boost, which caused verbose build messages.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.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.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.Ipc_arenaerrors and stats,Thread_cache,Memory_manager,Shm_pool_offset_ptroperators,Shm_sessionlifetime and stress, lend and borrow across processes.transport_testscripted mode, including batch-receive and graceful-close scenarios.rpc_demo(capnp-RPC).GitHub CI pipeline
Guided Manual
Full Changelog: v2.0.0...v3.0.0
Submodule repository
flowAPI notes
ipc::utilipc::util::Native_handle:close(),is_open(),dup();make_default(),is_allocated().constexpr+noexceptto ctors, assignment, comparisons,null(),swap().constexprifyS_NULL_HANDLE.Own_native_handleowns/auto-closes aNative_handle.duped_native_handle()makes one from a.dup();disowned_native_handle()releases it.Shared_name:Shared_name::ct_from_int<Integer>().constexprifyShared_name::S_MAX_LENGTH,S_SEPARATOR.NULL_PROCESS_CREDENTIALS.process_running(pid):EPERM=>true;pid <= 0=>false.ipc::util::Use_counted_object. (Not IPC-related; Flow-IPC no longer uses it.)ipc::util::Default_init_allocatortoflow::util::Default_init_allocator(Flow). It gainedrebindin the move.ipc::transport(unstructured)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).Protocol_pkt_stream: Detect truncation if target buffer too small => emiterror::Code::S_MESSAGE_SIZE_EXCEEDS_USER_STORAGE.[sync_io::]Native_socket_stream:async_receive_{blob|native_handle}_batch(),{Blob|Native_handle}_batch_in(batch type),S_RCV_{BLOB|NATIVE_HANDLE}_BATCH_SZ_RECOMMENDATION.{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.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]().[sync_io::]Blob_sender,Blob_receiver,Native_handle_sender,Native_handle_receiver:send_native_handle()contract as above.structs:stat::Blob_snd_stats,Blob_rcv_stats.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.asio_local_stream_socket::ProtocoltoProtocol_byte_stream; make Boost.Asio aliasesAcceptor,Peer_socket,Endpointinto alias templates.Peer_socket<Protocol_byte_stream>for the previous meaning.asio_local_stream_socket::async_write_with_native_handle(),async_read_with_target_func(),async_read_interruptible().nb_*()functions with your own async-wait (asNative_socket_streamdoes).asio_local_stream_socket::release_native_peer_socket().util::Native_handle::close().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).Blob_*/Native_handle_*concepts: Must add the new batch-receive and stats APIs (and honor the upgradedsend_native_handle()contract).ipc::transport(structured)struc::[sync_io::]Channel:remote_peer_process_credentials(),remote_peer_process_liveness_check().create_msg(size_t msg_sz_cap_estimate_or_zero, Native_handle&& hndl_or_null = {}).stats(),stats_reset(),stats_configure_rcv_one_off_request_rtt_histogram().heap_fixed_builder_config(): Add optionalsegment1_szarg.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:emit_native_handle_or_null()(replacesnative_handle_or_null() const).serialization_size().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.Heap_fixed_builder::emit_serialization()with auto-splitting disabled.struc::[sync_io::]Channel:send(),[a]sync_request(): TakeMsg_out*, notconst Msg_out&.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.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.emit_native_handle_or_null().struc::Msg_out: Self-move-assignment is now undefined behavior.struc::error::Code: RemoveS_INTERNAL_ERROR_DESERIALIZE_TARGET_ALLOC_FAILED.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.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.Channel::heap_fixed_builder_config()rather than filling out fields manually.Heap_reader::Config:m_statsadded (pointer to target stat-set, or null).Heap_fixed_builder_capnp_message_builder: Ctor args changed.ipc::sessionSession(concept and all impls):info_collector(),Info_collector.remote_peer_process_credentials().S_IS_SRV_ELSE_CLI,Sync_io_obj,Async_io_obj.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).Session_mv: Removeheap_fixed_builder_config()(both overloads). The max message size it assumed is no longer constant (seeSession_server::mq_msg_size_limit()).struc::Channel::heap_fixed_builder_config(), which requires only an unstructuredChannel; or fill outHeap_fixed_builder::Configvalues directly.Session: RenameS_SHM_TYPEtoS_SHM_TYPE_OR_NONE.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.fooat startup could remove kernel-persistent resources (MQs, SHM-classic pools) of a running session-serverfoo_bar.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::rpcAll new (see above):
Session_vat_network,Ez_rpc_client,Ez_rpc_server,Client_context,Context_server,Server_context.ipc::shm::statAdd
Owner_obj_stats,Lender_obj_stats,Borrower_obj_stats,Live_obj_stats,Shared_arena_obj_stats.ipc::shm::classicPool_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.stat::Arena_stats,Local_stats,Arena_info_dump.error::Code: AddS_SHM_POOL_OPEN_READ_ONLY_FOUND_BUT_UNINIT.Pool_arena_activator,Pool_arena_allocator<T>.Pool_arena::Activator,Pool_arena::Allocator<T>.Pool_arena: Removeconstruct_at().flow::util::construct_at()(Flow).Pool_arena::allocate(),construct<T>(): Out of space throwsstd::bad_alloc, notbipc::bad_alloc.SIGBUS).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()).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.ipc::shm::stlStateless_allocator,Arena_activator: RenameArena_objalias toArena.Stateless_allocator:alignof(T)greater thanalignof(std::max_align_t)is a compile error.ipc::transport::struc::shmCapnp_message_reader.Capnp_message_builderctor: Add optionalsegment_sz_init_pre_growth,statsargs.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.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::classicSession,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_sessionThread_cache.Ipc_arena: AddAllocator<T>,Activator,this_thread_gc(),info_dump(),Info_dump,sample_hi_wmarks().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().Shm_session: AddArena,Handle<T>,Borrower_arena_allocator<T>,info_dump(),Info_dump.borrower_pool_stats[_reset](),borrower_pool_stats_process_wide(),borrower_pool_lookup_global_stats(),global_stats_reset(),borrowed_shm_pool_live_info().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).Ipc_arena:construct_thread_cached().construct(); tcache use is automatic.allocate()throws on failure instead of returning null (see above).get_arena_id()toget_jemalloc_arena_id().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().ipc::shm::arena_lend::Memory_manager, its base class.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.Shm_session: Removeset_disconnected().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#71this_thread_gc()from such a thread as it goes idle.construct()ed objects can be live per constructing-thread per arena. Beyond thatconstruct()throwsstd::bad_alloc.ipc::session::shm::arena_lend::jemallocSession,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.This discussion was created from the release v3.0.0.
All reactions