// Example 16 — Event Callbacks: overflow and node-stopped signals // // Two complementary observation mechanisms: // // 1. Per-node overflow callback set_overflow_callback() // Fired (with a timestamp) when a node's output channel is full and an // item is dropped. Useful for targeted monitoring of a specific node. // // 2. Network-level event handler net.set_event_handler() // Aggregate callback covering every node: receives the node name, a // NodeEvent (Overflow or Closed), and a timestamp. Register once and // observe the whole network. // // Pipeline: [fast_source] --int--> [slow_sink] // // fast_source produces at ~500 items/s; slow_sink consumes at ~20 items/s. // The channel capacity is 3, so overflows appear within milliseconds. #include #include #include #include #include using namespace kpn; using namespace std::chrono; // ── Node functions ──────────────────────────────────────────────────────────── // --8<-- [start:node_fns] static std::atomic g_seq{0}; static int fast_source() { std::this_thread::sleep_for(milliseconds(2)); // ~500/s return g_seq.fetch_add(1); } static void slow_sink(int x) { std::this_thread::sleep_for(milliseconds(50)); // ~20/s std::cout << " consumed: " << x << '\n'; } // --8<-- [end:node_fns] // ── main ────────────────────────────────────────────────────────────────────── int main() { auto src = make_node(/*capacity=*/3); auto snk = make_node (/*capacity=*/3); // --8<-- [start:per_node_callback] // Per-node overflow callback — no node name needed, known at registration. std::atomic overflow_count{0}; src.set_overflow_callback([&](steady_clock::time_point ts) { auto ms = duration_cast(ts.time_since_epoch()).count(); std::cerr << "[overflow] fast_source at t=" << ms << "ms\n"; overflow_count.fetch_add(1); }); // --8<-- [end:per_node_callback] Network net; // --8<-- [start:network_event_handler] // Network-level aggregate handler — covers every node, includes node name. net.set_event_handler([](std::string_view name, NodeEvent ev, steady_clock::time_point ts) { auto ms = duration_cast(ts.time_since_epoch()).count(); std::string_view kind = (ev == NodeEvent::Overflow) ? "overflow" : "closed"; std::cerr << "[net:" << kind << "] node=" << name << " t=" << ms << "ms\n"; }); // --8<-- [end:network_event_handler] net.add("source", src) .add("sink", snk) .connect("source", src.output<0>(), "sink", snk.input<0>()) .build() .start(); std::this_thread::sleep_for(milliseconds(300)); net.stop(); std::cout << "\nTotal overflows observed by per-node callback: " << overflow_count.load() << '\n'; }