Skip to content

feat(ipc-runtime): extract bb::ipc, add transport helpers + codegen serve() wrapper#23478

Open
charlielye wants to merge 12 commits into
cl/ipc-codegenfrom
cl/ipc-runtime
Open

feat(ipc-runtime): extract bb::ipc, add transport helpers + codegen serve() wrapper#23478
charlielye wants to merge 12 commits into
cl/ipc-codegenfrom
cl/ipc-runtime

Conversation

@charlielye
Copy link
Copy Markdown
Contributor

Summary

Lifts barretenberg's bb::ipc transport runtime out into a new top-level package /ipc-runtime/, peer to /ipc-codegen/. Three logical chunks landed as separate commits in this PR.

Stacks between #23314 (ipc-codegen package) and #23316 (bb.js + wsdb consumer migration). #23316 needs rebasing on top.

A. Extract bb::ipc/ipc-runtime/cpp/

3,449 LOC moved verbatim from barretenberg/cpp/src/barretenberg/ipc/ (incl. shm/ subdir, shm.test.cpp, README) to /ipc-runtime/cpp/ipc_runtime/. Audit confirmed the module had one non-trivial external coupling — a stale throw_or_abort.hpp include in shm_server.hpp that was never actually called — which is dropped here.

The lib is consumed by barretenberg's CMake via add_subdirectory(${CMAKE_SOURCE_DIR}/../../ipc-runtime/cpp ...). 6 in-tree consumers retargeted (path rewrite + CMake link name ipcipc_runtime):

  • nodejs_module/msgpack_client/
  • api/api_msgpack.{hpp,cpp} + CMake
  • bb/CMakeLists.txt (bb + bb-avm link lines)
  • wsdb/{CMakeLists.txt, wsdb_ipc_server.cpp} + wsdb_client/CMakeLists.txt
  • benchmark/ipc_bench/

Namespace rename bb::ipc::ipc:: throughout — the runtime no longer lives under barretenberg, so the bb:: prefix was historical baggage.

shm.test.cpp runs as ipc_runtime_tests under barretenberg's CTest, all 2 tests pass.

B. Transport helpers

  • ipc::make_server(input_path, opts) — picks UDS vs MPSC-SHM by path suffix (.sock / .shm), matching today's hand-rolled logic in wsdb_ipc_server.cpp. Defaults match aztec-wsdb's production config (2 SHM clients, 4 MiB rings).
  • ipc::install_default_signal_handlers(server) — SIGTERM/SIGINT → graceful shutdown, SIGBUS/SIGSEGV → close+exit, prctl PR_SET_PDEATHSIG on Linux, kqueue NOTE_EXIT watcher on macOS. Lifted from wsdb_ipc_server.cpp lifecycle code.

Both pure additions; no current consumer changes.

C. Codegen serve() wrapper

New CLI flag --cpp-runtime-include <prefix> (e.g. ipc_runtime) tells the C++ generator to emit a serve() that uses ipc-runtime:

template <typename Ctx>
void serve(const std::string& input_path, Ctx& ctx) {
    auto server = ipc::make_server(input_path);
    ipc::install_default_signal_handlers(*server);
    server->listen();
    auto handler = make_<service>_handler(ctx);
    server->run([&handler](int, std::span<const uint8_t> raw) {
        return handler(std::vector<uint8_t>(raw.begin(), raw.end()));
    });
}

When the flag is unset, the lightweight header-only ipc_server.hpp template path is unchanged — verified by the 18-test cross-language echo matrix still passing.

Also rename make_<service>_handler's return type from ::ipc::Handler to a service-local DispatchHandler typedef; the legacy ::ipc::Handler only existed in the lightweight template, the runtime defines a differently-shaped IpcServer::Handler, so a stable service-namespace name decouples the two backends.

Out of scope (future work)

  • Truly standalone-buildable /ipc-runtime/cpp/ (its own CMakeLists without barretenberg_module_with_sources, FetchContent gtest, standalone bootstrap.sh). Today the build still relies on barretenberg's CMake tree for convenience.
  • refactor(wsdb): use codegen-emitted serve() — collapses wsdb_ipc_server.cpp to ~30 LOC. Belongs after Stack B PR_B6 (wire/domain split).
  • C-ABI shim + Rust/Zig wrappers (/ipc-runtime/{rust,zig}/). Defer until a second-language server consumer materialises.

Test plan

  • cd ipc-codegen && ./bootstrap.sh test — 18/18 echo matrix green (default header-only path)
  • Smoke-compile a generated echo server with --cpp-runtime-include ipc_runtime against ipc-runtime — compiles
  • ninja ipc_runtime ipc_runtime_tests + ./bin/ipc_runtime_tests — 2/2 passing
  • ninja bb bb-avm aztec-wsdb — all build with retargeted link names
  • CI: full build green
  • Rebase refactor(codegen): migrate bb.js BB API codegen to ipc-codegen #23316 on this branch; CI on rebased PR2 green

charlielye added 12 commits May 21, 2026 16:21
Lift the UDS + MPSC-SHM server/client primitives out of barretenberg into
a new top-level package /ipc-runtime/, parallel to /ipc-codegen/. The
runtime was already self-contained (one stale throw_or_abort.hpp include
in shm_server.hpp, never called) so this is a pure relocation.

Layout:

  /ipc-runtime/
    bootstrap.sh           # delegates to barretenberg/cpp/'s build for now
    .rebuild_patterns
    cpp/
      CMakeLists.txt       # ipc_runtime static lib + ipc_runtime_tests
      ipc_runtime/         # public headers + impl, matches include prefix
        ipc_{client,server}.{hpp,cpp}
        socket_{client,server}.{hpp,cpp}
        mpsc_shm_{client,server}.hpp
        shm_{client,server,common}.hpp
        shm.test.cpp
        shm/{futex,mpsc_shm,spsc_shm,utilities}.{hpp,cpp}

barretenberg/cpp/src/CMakeLists.txt picks the lib up via
add_subdirectory(${CMAKE_SOURCE_DIR}/../../ipc-runtime/cpp ...) so
in-tree consumers (bb, bb-avm, aztec-wsdb, nodejs_module, api,
ipc_bench) just retarget `#include "barretenberg/ipc/..."` to
`#include "ipc_runtime/..."` and `target_link_libraries(... ipc)` to
`... ipc_runtime`.

The shm.test.cpp gtest binary is now ipc_runtime_tests, runs as part of
barretenberg's CTest discovery, all 2 tests pass.

The next step (truly standalone build via /ipc-runtime/bootstrap.sh,
without barretenberg's CMake tree) is a follow-up: the bootstrap.sh
currently delegates to barretenberg's build, and ipc-runtime/cpp/
CMakeLists.txt still uses barretenberg_module_with_sources for gtest +
msgpack convenience. Files + namespace boundary are correct; the build
plumbing tidies up later.
Two transport-adjacent helpers lifted from aztec-wsdb's wsdb_ipc_server.cpp:

- make_server(input_path, opts): picks UDS vs MPSC-SHM by path suffix
  (.sock → IpcServer::create_socket, .shm → create_mpsc_shm). Defaults
  match aztec-wsdb's production config (2 SHM clients, 4 MiB rings).

- install_default_signal_handlers(server): wires SIGTERM/SIGINT to
  request_shutdown(), SIGBUS/SIGSEGV to close()+exit(1), plus
  parent-death monitoring (prctl PR_SET_PDEATHSIG on Linux, kqueue
  NOTE_EXIT watcher on macOS). File-scope std::atomic<IpcServer*>
  keeps signal handlers thread-safe.

Also rename the runtime namespace bb::ipc → ipc throughout. The runtime
no longer lives under barretenberg, so the bb:: prefix was historical
baggage. 6 in-tree consumers updated to use the new namespace; mechanical
sed.
…e-include

New CLI flag --cpp-runtime-include <prefix> tells the C++ generator to emit
a serve() wrapper that uses ipc-runtime (make_server + install_default_signal_handlers
+ IpcServer::run) instead of the lightweight header-only ipc_server.hpp template.

Concretely, with --cpp-runtime-include ipc_runtime the generated <service>_ipc_server.hpp:
- #includes ipc_runtime/{ipc_server,serve_helper,signal_handlers}.hpp instead
  of the bundled ipc_server.hpp template
- emits serve(input_path, ctx) that calls ipc::make_server() (UDS vs MPSC-SHM
  by path suffix), installs default lifecycle signal handlers, and runs
  IpcServer::run() with a lambda that adapts the codegen's DispatchHandler
  (vector-of-bytes single-arg signature) to ipc::IpcServer::Handler's
  (client_id, span) signature.

When --cpp-runtime-include is unset, the default header-only fallback path
is unchanged — verified by the 18-test cross-language echo matrix still
passing.

Also rename make_<service>_handler's return type from ::ipc::Handler to a
service-local DispatchHandler typedef. The legacy ::ipc::Handler only
existed in the lightweight template; the runtime defines a differently-
shaped IpcServer::Handler, so emitting a stable service-namespace name
decouples the two backends.
barretenberg_module's bench codepath links MODULE_DEPENDENCIES only to the
final executable, not to the *_bench_objects OBJECT library that holds the
compiled .cpp.o. Existing bench deps all lived under barretenberg/cpp/src/,
which is already on -I project-wide, so they didn't trip on this. ipc_runtime
is the first bench dep with headers outside that root (at /ipc-runtime/cpp/),
so its `target_include_directories` only reaches consumers that link it.

Add an explicit target_link_libraries(ipc_bench_objects PRIVATE ipc_runtime)
to thread the include directory in. Doesn't touch the general module macro
since fixing it for all modules has broader blast radius than this PR warrants.
The bb::ipc → ipc_runtime path rewrite in PR-RUNTIME's first commit
swept .cpp/.hpp files but missed the .ts emitter that produces wsdb's
autogenerated client header. The tracked wsdb_ipc_client_generated.hpp
got the new include path; cpp_codegen.ts kept the old one. CI's noir
contracts build runs \`yarn generate\` mid-run, which rewrote the
tracked header back to the old path, tripping cache_content_hash's
\"changes during CI run\" guard.

Update cpp_codegen.ts to match. Regenerating now produces a header
identical to what's tracked.
…plain CMake

The ipc-runtime library has no barretenberg dependency (POSIX + std + a
pthread find_package), but the previous CMakeLists used barretenberg's
module macro, which dragged in Tracy/TBB/lmdb plumbing and made the
package non-standalone-buildable. Replace with plain CMake.

The new CMakeLists works two ways:
- Top-level standalone: \`cmake -B build -S cpp && cmake --build build\`
  (declares its own project, sets C++20 + PIC, fetches gtest v1.13.0 via
  FetchContent only when tests aren't already in scope).
- As an add_subdirectory() from a larger tree (e.g. barretenberg/cpp):
  skips project() declaration so it inherits the parent's project name
  and compile options; reuses the parent's GTest::gtest_main target if
  one is already defined.

ipc-runtime/bootstrap.sh now invokes cmake directly instead of
delegating to barretenberg's build. Cross-compile is via standard CMake
knobs (CXX, CXXFLAGS, CMAKE_TOOLCHAIN_FILE) — e.g. zig-c++.sh +
-target aarch64-linux-gnu works without extra plumbing.

Verified:
- Under barretenberg/cpp/'s tree: ninja ipc_runtime ipc_runtime_tests
  bb aztec-wsdb all build; ipc_runtime_tests 2/2 passing.
- Standalone: ./bootstrap.sh produces libipc_runtime.a + ipc_runtime_tests
  from a clean checkout; 2/2 passing. gtest is fetched on first run.
…y templates

Codegen no longer carries its own minimal UDS-only transport for C++.
Generated client and server both depend on ipc-runtime headers and pick
UDS vs MPSC-SHM by path suffix (".sock" / ".shm").

- `ipc::make_client(path, shm_client_id)` added in serve_helper, mirror
  of make_server.
- Generated <Service>IpcClient constructor takes a path, calls
  ipc::make_client(path), throws on unrecognised suffix.
- Generated client's send<Cmd, Resp> uses IpcClient::{send, receive,
  release} instead of the lightweight template's call(). Copies bytes
  out before release for clean lifetime management.
- Generated <service>_ipc_server.hpp unconditionally uses ipc::make_server
  + install_default_signal_handlers + IpcServer::run. Dropped the
  --cpp-runtime-include flag + the runtimeInclude option + the conditional
  emission.
- Deleted templates/cpp/{ipc_client,ipc_server}.hpp; copyTemplate calls
  removed from generate.ts cpp path.
- Echo example's C++ build (ipc-codegen/bootstrap.sh) links libipc_runtime.a;
  THROW/RETHROW defined so barretenberg's patched msgpack-c compiles
  outside its own tree.

Verified: ipc-codegen cross-language matrix 18/18 green via the shared
runtime; barretenberg's bb, bb-avm, aztec-wsdb, nodejs_module, ipc_runtime_tests
all build.
Plain-C extern "C" surface that wraps IpcServer and IpcClient (plus the
make_server/make_client/install_default_signal_handlers helpers) so
non-C++ consumers — Rust and Zig bindings landed next — can drive the
same UDS + MPSC-SHM transport.

The header (c_abi.h) is includable from any C99+ compiler: opaque
ipc_server_t* / ipc_client_t* handles, status codes instead of exceptions,
(ptr, len) pairs instead of std::span, free function pointers instead of
std::function. The .cpp owns C++ <-> C marshalling.

Surface mirrors the C++ API closely; see header for lifetime contracts
(handler-owned response buffers, runtime-owned receive views released
explicitly by the caller).
Safe wrappers around c_abi.h. Drop-impl release for both IpcServer and
IpcClient, idiomatic Result<_, Error> for fallible ops, FnMut closure
bridging in run() via a static shim that re-casts a *mut c_void back to
the closure pointer.

The build script honours IPC_RUNTIME_LIB_DIR (default: ../cpp/build),
links libipc_runtime.a + libstdc++ (linux) / libc++ (macos) + pthread.
Per-target archives produced by the C++ build feed the right one for
cross-compile without changes to the crate.

Verified by a manual smoke (UDS server + client, single round-trip with
byte reversal) — checked in via cargo's example mechanism during
bring-up, removed after passing. The cross-language matrix becomes the
permanent regression net in the next commit.

No external Rust deps; only thing the crate touches outside std is the
C archive.
… template

Rust generated client/server now talks to the shared ipc-runtime
transport instead of its own per-service UDS templates.

- `templates/rust/backend.rs` keeps the `Backend` trait (the FFI backend
  still uses it), but gains a single impl block bridging
  `ipc_runtime::IpcClient` so the codegen-emitted `<Service>Api` can be
  constructed directly with a runtime client: `Api::new(IpcClient::from_path(p))`.
- Deleted `templates/rust/{ipc_server.rs, uds_backend.rs}`; copyTemplate
  calls for them removed from `generate.ts`.
- Echo example rewritten: `echo_server.rs` calls `IpcServer::from_path` +
  `listen` + `run(|client_id, payload| { ... })`; `echo_client.rs`
  constructs `IpcClient::from_path` and hands it to `EchoApi::new`.
- Echo `Cargo.toml` depends on the local `ipc-runtime` crate.
- `ipc-codegen/bootstrap.sh` builds ipc-runtime up front (once) and
  exports `IPC_RUNTIME_LIB_DIR` for cargo to find the archive.

18-test cross-language matrix passes; all Rust pairs now go through the
shared C ABI runtime end-to-end.
… impl

The generated <Service>IpcClient .cpp transitively includes msgpack-c via
msgpack_struct_map_impl.hpp. Barretenberg's patched msgpack-c expects
THROW/RETHROW to be defined (it uses them in place of bare `throw`).
The server header already guards with #ifndef THROW; mirror it in the
client impl so any consumer that compiles outside barretenberg's project-
wide -DTHROW=throw still builds.
…hive

Each consumer (Rust crate, Zig package, ipc-codegen's echo C++ build)
now compiles ipc-runtime/cpp/ipc_runtime/*.cpp directly with its own
toolchain. This eliminates the libstdc++/libc++ ABI mismatch that made
a shared libipc_runtime.a unusable across language ecosystems, and
removes the IPC_RUNTIME_LIB_DIR coordination requirement.

- ipc-runtime/rust: build.rs uses cc::Build to compile the C++ sources
  into the crate's archive (cargo picks the stdlib).
- ipc-runtime/zig: new package; build.zig compiles the same sources via
  Zig's bundled clang + libc++.
- ipc-codegen/bootstrap.sh: C++ echo binaries inline the .cpp sources
  into each clang invocation.

Cross-process IPC interop is stdlib-agnostic — wire format is bytes;
SHM uses raw atomics + uint64 offsets, not std::-typed structs.

Verified: full 18-test cross-language matrix (4x4 + 2 golden) passes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant