Subscribe to one vertex (L4 graph)¶
The smallest subscription there is: one producer, one in-process callback, and the
delivery contract that follows from it. subscribe(src, callable) returns a
subscription_t handle; every write to src then invokes the callback with the written
rope value.
What to notice¶
subscribeis host-SDK sugar, not a wire verb. The wire data API stays read/write/await (ADR-0006); a subscription is a consumer-initiatedSUBSCRIBERfield-write into the producer’s:subscribers[](ADR-0026). The callback overload enters the same single admission door as that field-write — SUBSCRIBE gate, append, durability latch (ADR-0049) — it merely skips the parse a callback cannot ride.Delivery is inline, on the writing thread. The callback runs inside
write(), once per write; nothing is queued and no delivery thread exists. A slow callback is a slowwrite.The callable is bound by address, and it is the edge’s
ctx. The templated overload takes an lvalue reference (a temporary would dangle) and forwards{fn, &callable}to thesubscriber_fn_tform — a plain function-pointer pair, so the per-publish edge snapshot is a trivial copy rather than astd::functionclone. How long that address must stay alive is the subject of unsubscribe & the release hook.own_subscounts the vertex’s OWN slots. It is a sizing figure, not an “is anyone listening” test — subscribers on an ancestor are not in it (see one edge, a whole subtree), andhas_subscribersis the question a producer should ask.
Source¶
1/*
2 * SPDX-License-Identifier: Apache-2.0
3 * SPDX-FileCopyrightText: Copyright 2026 avatarsd LLC
4 */
5
6/**
7 * @file
8 * @brief Subscribe to ONE vertex — the delivery-callback contract, and nothing else.
9 *
10 * `subscribe(src, callable)` is host-SDK sugar over the wire subscription (a
11 * `SUBSCRIBER` field-write into `src:subscribers[]`, ADR-0049). It returns a
12 * `subscription_t` handle, and from then on every write to `src` invokes the callback
13 * **inline on the writing thread**, once per write, with the written rope value.
14 *
15 * Runs under ctest as `example_sub_callback`; it self-checks and returns non-zero on any
16 * mismatch.
17 */
18
19#include <cstddef>
20#include <cstdint>
21#include <cstdio>
22
23#include "libtracer/tracer.hpp"
24
25namespace {
26
27using tr::graph::path_t;
28using tr::graph::role_t;
29
30/** @brief A one-byte VALUE view over @p b (one heap segment). */
31tr::view::view_t value_byte(std::uint8_t b) {
32 tr::view::segment_ptr_t seg = tr::view::heap_alloc(1);
33 seg->bytes[0] = std::byte{b};
34 return tr::view::view_t::over(std::move(seg));
35}
36
37/** @brief Record a failed expectation on @p ok and report it. */
38void check(bool& ok, bool cond, const char* what) {
39 if (!cond) {
40 std::printf(" [FAIL] %s\n", what);
41 ok = false;
42 }
43}
44
45} // namespace
46
47int main() {
48 tr::graph::graph_t g;
49 const tr::graph::vertex_handle_t temp =
50 g.register_vertex(path_t("/sensor/temp"), role_t::STORED_VALUE);
51
52 int deliveries = 0;
53 std::uint8_t last = 0;
54 // The callback is bound BY ADDRESS (lvalues only) and is the edge's `ctx`, so it must
55 // outlive the subscription — here, main's frame.
56 auto on_temp = [&](const tr::view::rope_t& v) {
57 ++deliveries;
58 last = std::to_integer<std::uint8_t>(v.only().bytes()[0]);
59 std::printf(" delivery %d: %u\n", deliveries, last);
60 };
61 const auto sub = g.subscribe(path_t("/sensor/temp"), on_temp);
62
63 std::printf("subscribe(/sensor/temp) -> %s\n", sub ? "ok" : "error");
64 for (const std::uint8_t v : {std::uint8_t{7}, std::uint8_t{8}, std::uint8_t{9}})
65 (void)g.write(temp, value_byte(v));
66
67 bool ok = true;
68 check(ok, sub.has_value(), "subscribe returns a subscription_t handle");
69 check(ok, deliveries == 3, "one delivery per write, none extra");
70 check(ok, last == 9, "the callback sees the written value");
71 check(ok, g.own_subs(temp) == 1, "the vertex carries exactly one own subscriber slot");
72 std::printf("RESULT %s\n", ok ? "ok" : "FAILED");
73 return ok ? 0 : 1;
74}
See also: graph module · communication flows · in-process pub/sub.