Two L0 seams, and the question that picks (L0 substrate)

L0 has two injection points, not one, and reaching for the wrong one is the most common way a first embedding goes sideways. tr::mem::mem_backend_t vends a refcounted segment_t — bytes plus a control block plus an intrusive count — so many views can share one buffer and the last drop reclaims it. tr::mem::block_source_t vends raw bytes with a single owner and no header at all.

The question that picks between them is not “how big” or “how hot”. It is how many owners the bytes will have (reference 09 §the second L0 seam).

What to notice

  • Payload is shared, so payload is a segment. A decoded frame’s spans, a value published to several subscribers, a rope link handed to a transport: all of these are held by more than one holder at once, and the refcount is what makes a borrowed (zero-copy) view safe. The example copies a handle and watches use_count() go to 2 while the byte pointer stays identical — copy is a clone, never a second buffer.

  • A registered object has exactly one owner, so it is a source block. A vertex, a route label, a reassembly entry — a refcount on them is pure overhead, and it is measurable overhead: a segment_t is 20 B on rv32 / 40 B on x86-64 against an 80 B vertex_t.

  • “No header” is checkable, and the example checks it. A 64-byte slab serves exactly one 64-byte block and then nothing. Any per-block bookkeeping would show up as a short slab immediately.

  • Reclaim is a call on one seam and a consequence on the other. A source block comes back because its one owner said so; a segment comes back because the last handle dropped. Mixing the two models is what produces either a leak or a use-after-free, and the type system is what keeps them apart — there is no conversion between the seams in either direction.

  • They are injected independently. A node may point both at the same underlying store (“one slab, whole stack”) or split them — a bounded value backend for payload, a bounded control source for registration.

  • Nothing here is conditional — the target builds and runs under every CI leg.

Source

 1/*
 2 * SPDX-License-Identifier: Apache-2.0
 3 * SPDX-FileCopyrightText: Copyright 2026 avatarsd LLC
 4 */
 5
 6/**
 7 * @file
 8 * @brief ONE CONCEPT — L0 has TWO seams, and the number of owners picks between them.
 9 *
10 * `tr::mem::mem_backend_t` vends a refcounted `tr::view::segment_t`: real bytes plus a
11 * control block plus an intrusive count, so many views can share one buffer and the last
12 * drop reclaims it. That is the right shape for PAYLOAD — a decoded frame's spans, a value
13 * published to several subscribers.
14 *
15 * `tr::mem::block_source_t` vends raw bytes with a single owner and no header at all. That
16 * is the right shape for the objects a node builds when it REGISTERS something — a vertex,
17 * a route label, a reassembly entry — because a refcount on a thing with one owner is pure
18 * overhead (`docs/reference/09-memory-substrate.md` §the second L0 seam).
19 *
20 * The two are injected independently. A node may point both at the same slab ("one slab,
21 * whole stack") or split them; what it must not do is reach for a refcount it will never
22 * share, or share bytes that carry no count.
23 *
24 * Runs under ctest as `example_mem_source_vs_backend`; returns non-zero on any failed check.
25 */
26
27#include <array>
28#include <cstddef>
29#include <cstdio>
30#include <span>
31
32#include "libtracer/mem_heap.hpp"
33#include "libtracer/mem_source.hpp"
34#include "libtracer/segment.hpp"
35
36namespace {
37
38/** @brief Report expectation @p what and record a failure on @p ok. */
39void check(bool& ok, bool cond, const char* what) {
40    std::printf("  [%s] %s\n", cond ? "ok" : "FAIL", what);
41    ok = ok && cond;
42}
43
44}  // namespace
45
46int main() {
47    bool ok = true;
48
49    // Seam 1 — the BACKEND: bytes that will be shared. The handle is the ownership.
50    tr::view::segment_ptr_t payload = tr::view::segment_alloc(tr::mem::heap_backend(), 64);
51    check(ok, static_cast<bool>(payload), "the backend vends a refcounted segment");
52    check(ok, payload.use_count() == 1, "one handle, one reference");
53    {
54        const tr::view::segment_ptr_t shared = payload;  // copy == clone, not a byte copy
55        check(ok, payload.use_count() == 2, "a second holder is a count, never a second buffer");
56        check(ok, shared->bytes.data() == payload->bytes.data(), "both name the SAME bytes");
57    }
58    check(ok, payload.use_count() == 1, "and the reclaim waits for the LAST holder");
59    std::printf("segment: %zu shared bytes behind a %zu-byte control block\n",
60                payload->bytes.size(), sizeof(tr::view::segment_t));
61
62    // Seam 2 — the SOURCE: bytes with exactly one owner. No handle, no count, no header.
63    alignas(std::max_align_t) std::array<std::byte, 64> slab{};
64    std::array<tr::mem::size_class_t, 2> classes{};
65    tr::mem::pool_source_t<> source{slab, classes};
66    void* const object = source.try_alloc(64, 8);
67    check(ok, object != nullptr, "the source vends 64 raw bytes out of a 64-byte slab");
68    check(ok, source.used() == 64,
69          "exactly 64 — a source block carries NO header, so nothing was left over");
70    check(ok, source.try_alloc(8, 8) == nullptr, "the slab is the bound, and it is full");
71    source.release(object, 64, 8);  // the owner returns it explicitly; there is no count to fall
72
73    check(ok, source.try_alloc(64, 8) == object,
74          "one owner means reclaim is a call, not a consequence of the last drop");
75    return ok ? 0 : 1;
76}

See also: backends · segment module · memory substrate reference · the segment, and its refcount.