segment — refcounted bytes (L1)¶
In one paragraph
A tr::view::segment_t is real bytes owned by a backend, plus an intrusive
atomic refcount. The tr::view::segment_ptr_t handle threads that one
buffer’s lifetime through fan-out: copying a handle is a relaxed increment (a
clone), dropping the last one runs the backend’s reclaim. This is what lets
many views share one buffer with no copies, and what makes a decoded TLV safe to
hold past the receive call.
What it does¶
L0 is “real bytes in real memory owned by some real allocator”; L1 adds the
ownership. segment_t is the control block over one such buffer (the L0↔L1
boundary object); segment_ptr_t is the owning handle. The refcount lives
inside the segment (not in a side shared_ptr block) so a static MMIO
descriptor, a pool slot, and a heap allocation all carry their own count. When the
last segment_ptr_t drops, the segment’s backend->destroy(seg) returns the bytes
to wherever they came from — there is no separate release() step.
The atomic orderings are the canonical intrusive_ptr pattern, mandated by the
spec (reference/02 §required atomic operations): increment relaxed (the caller
already holds a reference), decrement acq_rel (publish writes before the count
drops; synchronize on reaching zero). For single-threaded / Cortex-M0 builds,
-DLIBTRACER_NO_ATOMIC swaps the atomic for a plain integer.
Interface¶
Generated from the
core/headers by Doxygen — these are the reference implementation’s own declarations, not a hand-maintained copy.
-
struct segment_t¶
A refcounted span of real bytes: the L0↔L1 boundary object.
Real bytes + the backend that reclaims them + an intrusive refcount. Never copied or moved (the atomic refcount pins it in place); always handled through segment_ptr_t.
Note
bytesis writable at the type level, but whether writes are legal is the backend’s contract — a const/ROM borrow must not be written through (see mem_borrowed.hpp).Public Functions
-
inline segment_t(mem::mem_backend_t *b, std::span<std::byte> by, std::uint_least32_t initial = 1) noexcept¶
Construct a segment over
by, reclaimed byb, withinitialrefcount.The address space and module-set tag are taken from the backend (
b->space()/b->tag()); aDEVICEsegment must not be CPU-dereferenced (docs/adr/0024).
Public Members
-
detail::ref_count_t refcount¶
Intrusive refcount (spec orderings).
-
mem::mem_backend_t *backend¶
Reclaimer; non-const (cache hooks mutate it).
-
std::span<std::byte> bytes¶
The backing bytes this segment holds a reference to.
-
inline segment_t(mem::mem_backend_t *b, std::span<std::byte> by, std::uint_least32_t initial = 1) noexcept¶
-
class segment_ptr_t¶
Intrusive owning handle for a segment_t.
Copy = clone (refcount bump, relaxed); destruction = release (acq_rel); the backend’s
destroyfires when the last handle drops. This is what makes a borrowed (zero-copy) view safe to hold: the spans in a decoded TLV stay valid as long as the view — and thus this handle — lives.Public Functions
-
inline segment_ptr_t(const segment_ptr_t &other) noexcept¶
Clone — a new shared reference to the same segment (relaxed increment).
-
inline segment_ptr_t(segment_ptr_t &&other) noexcept¶
Transfer ownership of
other'sreference, leaving it empty.
-
inline segment_ptr_t &operator=(segment_ptr_t other) noexcept¶
Copy-and-swap assignment — one operator covers copy- and move-assign.
-
inline void reset() noexcept¶
Drop this reference (acq_rel); fires the backend’s
destroyat zero.
-
inline explicit operator bool() const noexcept¶
True when this handle owns a segment.
-
inline std::uint_least32_t use_count() const noexcept¶
Current refcount — debug / metrics only (acquire load), NOT a sync primitive.
Public Static Functions
-
static inline segment_ptr_t adopt(segment_t *seg) noexcept¶
Adopt an existing reference (e.g. from
alloc, refcount = 1) WITHOUT bumping.
-
static inline segment_ptr_t retain(segment_t *seg) noexcept¶
Take a NEW shared reference to an already-live segment (bumps the count).
-
inline segment_ptr_t(const segment_ptr_t &other) noexcept¶
Refcount lifecycle (fan-out)¶
sequenceDiagram
participant TX as producer
participant V as views
participant S1 as subscriber 1
participant S2 as subscriber 2
participant B as backend
TX->>V: make segment (count=1)
V->>S1: clone (relaxed ++ → 2)
V->>S2: clone (relaxed ++ → 3)
TX->>V: drop producer ref (-- → 2)
S1->>V: release (-- → 1)
S2->>V: release (acq_rel -- → 0)
V->>B: backend->destroy(seg) — bytes reclaimed
Benefits¶
Zero-copy fan-out — N subscribers share one buffer; delivery is N relaxed increments, no
memcpy.Closes M1’s lifetime gap — a decoded
tlv_tborrows segment bytes via spans; thesegment_ptr_tkeeps them alive exactly as long as some view needs them.No hidden allocation — the count is in the segment, so MMIO/pool/borrowed segments need no separate control block.
Portable to MCUs —
LIBTRACER_NO_ATOMICdrops to a plain counter where there is no cross-thread sharing.
See: backends (who creates segments), views (who holds them), and the interface map.