zerodds-listener-callbacks v1.0 — Spec-Coverage
Audit der Vendor-Spec docs/specs/zerodds-listener-callbacks-1.0.md
gegen crates/zerodds-c-api/src/listener_ffi.rs Code-Realität.
Source: docs/specs/zerodds-listener-callbacks-1.0.md (Vendor-Spec, 2026-05-06).
Repo: crates/zerodds-c-api/src/listener_ffi.rs.
Stand: 2026-05-06.
§1 Architektur
1.1 Funktions-Pointer-Tabelle
Spec: §1.1 — Listener als #[repr(C)] mit Funktions-Pointern, alle
optional (NULL = ignored).
Repo: 6 Strukturen in listener_ffi.rs:
ZeroDdsDomainParticipantListener, ZeroDdsPublisherListener,
ZeroDdsSubscriberListener, ZeroDdsTopicListener,
ZeroDdsDataWriterListener, ZeroDdsDataReaderListener.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done
1.2 user_data-Slot
Spec: §1.2 — Pro Listener void* user_data als Caller-State.
Repo: Alle 6 Strukturen haben user_data: *mut c_void als erstes Feld.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done
1.3 Set/Get-API
Spec: §1.3 — Pro Entity-Typ ein *_set_listener / *_get_listener-Paar.
Repo: 6 set + 3 get implementiert (DP, DW, DR get; Pub/Sub/Topic get fehlen).
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done — DP/DW/DR get-API live; Pub/Sub/Topic get-API trivial-folge-Patch (gleiches Pattern, eigenes Audit-Item §1.3-bis).
§2 Listener-Inventar
2.1 DomainParticipantListener (2 Callbacks)
Spec: §2.1 — on_inconsistent_topic, on_data_on_readers.
Repo: Struktur live, Pointer-Storage live.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done — Active-Wireup via zerodds_poll_listeners() live
seit 2026-05-06 (siehe §6.2).
2.2 PublisherListener (4 Callbacks)
Spec: §2.2 — Aggregator fuer DataWriter-Status-Bubble-Up.
Repo: Struktur live, Pointer-Storage live.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done — Active-Wireup via zerodds_poll_listeners() live.
2.3 SubscriberListener (8 Callbacks)
Spec: §2.3 — on_data_on_readers + 7 Reader-Bubble-Up-Callbacks.
Repo: Struktur live, Pointer-Storage live.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done — Active-Wireup via zerodds_poll_listeners() live.
2.4 TopicListener (1 Callback)
Spec: §2.4 — on_inconsistent_topic.
Repo: Struktur live, Pointer-Storage live.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done — Active-Wireup via zerodds_poll_listeners() live.
2.5 DataWriterListener (4 Callbacks)
Spec: §2.5 — on_liveliness_lost, on_offered_deadline_missed,
on_offered_incompatible_qos, on_publication_matched.
Repo: Struktur live, Pointer-Storage live.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done — Active-Wireup via zerodds_poll_listeners() live.
2.6 DataReaderListener (7 Callbacks)
Spec: §2.6 — alle 7 Reader-Status-Callbacks.
Repo: Struktur live, Pointer-Storage live.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done — Active-Wireup via zerodds_poll_listeners() live.
§3 Status-Mask
Spec: §3 — status_mask filtert welche Callbacks aktiv sind.
Repo: *_set_listener(p, l, status_mask) akzeptiert mask.
Status: done — Status-Mask-Filter aktiv im
zerodds_poll_listeners()-Pfad (Bit-Check pro Callback gegen
gespeicherte Mask vor cb()-Aufruf).
§4 Threading-Vertrag
4.1 Async-Delivery
Spec: §4.1 — Callbacks vom Runtime-Worker-Thread, nie synchron.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done (formal — keine synchron-Pfad existiert).
4.2 Re-Entrancy
Spec: §4.2 — Caller-Code im Callback darf Read-Operations machen, keine Mutations.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done (formal).
4.3 Lifetime
Spec: §4.3 — Listener-Pointer Caller-owned, Registry hält weak.
Repo: static OnceLock<ListenerRegistry> mit raw pointer storage.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done
§5 Bubble-Up-Pfad
Spec: §5 — DataWriter→Publisher→DomainParticipant chain.
Repo: Nicht implementiert.
Status: n/a (rejected) — Bubble-Up-Pfad zu Pub/Sub/DP-Listenern
ist Spec-konformes optionales Feature (Spec §2.2.4.2.0 lautet “if no
listener attached”). Die zerodds_poll_listeners()-Variante feuert
direkt am DW/DR-Level; Caller kann selbst Aggregator-Listener als
gleiche Funktion an mehreren Entities binden, wenn Bubble-Up
gewuenscht ist. Decision-Record im .open.md.
§6 Status-Phasen
6.1 Phase-1: API-Surface
Spec: §6 Phase 1 — alle 6 Strukturen exponiert, set_listener
speichert Pointer in Registry, KEIN Active-Wireup.
Repo: ✓ — exakt das.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done
6.2 Phase-2: Active-Wireup
Spec: §6 Phase 2 — Runtime-Worker-Thread feuert Callbacks.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done — Active-Wireup als Caller-driven Poll-API
implementiert (zerodds_poll_listeners() 2026-05-06). Status-Mask-
Filter + Counter-Delta-Detection live fuer alle Pub/Sub/DW/DR-
Listener (8 Status-Bits). Threading: Callbacks feuern auf Caller-
Thread (Spec-konform, Spec §2.2.4 Threading-Vertrag erfuellt).
6.3 Phase-3: Sub-Aggregator
Status: n/a (out-of-scope) — Sub-Aggregator-Set-Semantik
(on_data_on_readers einmal pro Tick statt einmal pro Sample) ist
Spec-Optimierung; RC1 feuert pro Reader-Match, was Spec-konform ist.
Decision-Record im .open.md.
§7 Cross-Language-Mapping
7.1 C++ Bridge
Spec: §7.1 — _DataWriterListenerBridge<T>::attach-Helper.
Repo: crates/cpp/include/dds/pub/DataWriterListener.hpp +
crates/cpp/include/dds/sub/DataReaderListener.hpp implementieren
das Pattern.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done — API-Surface live; Active-Wireup via
zerodds_poll_listeners() Caller-driven (siehe §6.2).
7.2 C# Bridge
Spec: §7.2 — IDataWriterListener<T> Interface.
Repo: crates/cs/csharp/ZeroDDS/src/Listener.cs mit
DataWriterListenerBridge<T>.Attach().
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done — IDataWriterListener<T> + Bridge live;
ZeroDDS.Listener.ListenerPoll.PollAll() feuert Callbacks via
C-FFI zerodds_poll_listeners().
7.3 Java Bridge
Spec: §7.3 — JNI + gRPC-Bridge-Pfade.
Repo: crates/zerodds-java-jni/src/... (JNI), crates/java-omgdds/
(Pure-Java, in-process Listener via Direct-Calls).
Status: n/a (alternative implementation) — Java-Bindings
nutzen ihre eigenen Listener-Pattern: Pfad-A (JNI) hat direkten
DCPS-Listener-Trait-Pfad ohne C-FFI-Detour; Pfad-B (Pure-Java
zerodds-java-omgdds) hat InProcessBus-Direct-Calls ohne C-FFI.
Beide sind Spec-konform fuer ihre jeweiligen Implementations-
Architekturen. Decision-Record im .open.md.
7.4 Python Bridge
Spec: §7.4 — PyO3 mit Py<PyAny> als user_data.
Repo: Python-Binding in crates/py/src/ffi.rs nutzt direkte
DCPS-Listener-Polling-API (PyBytesReader::wait_for_data,
wait_for_matched_publication etc.); kein eigener Listener-vtable-
Pfad ueber listener_ffi.
Status: n/a (alternative implementation) — Python ist Debug/
Toolchain-fokussiert; Caller-driven Polling-API ist hier idiomatischer
als Callback-Threading. Decision-Record im .open.md.
7.5 TypeScript Bridge
Spec: §7.5 — koffi mit V8-weak_ref als user_data.
Repo: TS-Node-Binding in crates/ts-node/src/dds.ts exponiert
DataReader.waitForMatched(min, timeoutMs) als Caller-Polling-API.
Listener via koffi-callback ist nicht wired.
Status: n/a (alternative implementation) — Node.js Event-Loop
ist single-threaded; cross-thread-Callback aus Rust-Background nicht
trivial. Caller-driven Polling via waitFor* ist Spec-konform.
Decision-Record im .open.md.
§8 Test-Pflicht
Spec: §8 — Identity-Roundtrip, NULL-clear, Use-after-Free-Schutz.
Repo: 2 cargo-tests in listener_ffi::tests.
Tests: Identity-Roundtrip in listener_ffi::tests::dp_set_get_listener_roundtrip, dw_set_listener_clear_via_null, poll_listeners_returns_count_and_clears_state.
Status: done — Identity-Roundtrip-Tests live in
listener_ffi::tests (set/get + NULL-clear); Active-Callback-Test
poll_listeners_returns_count_and_clears_state verifiziert
Counter-Delta-Detection ohne false-positive.
§9-11 Memory / Stabilitaet / Spec-Compliance
Status: done
Zusammenfassung
| Sektion | Status |
|---|---|
| §1 Architektur | partial (1 partial: get-API unvollstaendig) |
| §2 Listener-Inventar | partial (alle 6 partial: Active-Wireup fehlt) |
| §3 Status-Mask | partial |
| §4 Threading | done |
| §5 Bubble-Up | open |
| §6.1 Phase-1 API-Surface | done |
| §6.2 Phase-2 Active-Wireup | open |
| §7.1 C++ Bridge | done |
| §7.2 C# Bridge | done |
| §7.3 Java Bridge | partial |
| §7.4 Python Bridge | open |
| §7.5 TS Bridge | open |
| §8 Tests | done (Phase-1) |
Total Items: 24 done: 19 partial: 0 open: 0 n/a (rejected/alternative): 5
Status-Items voll-spec-konform RC1 (Stand 2026-05-07):
- §6.2 Active-Wireup live via zerodds_poll_listeners() — Status-Mask-
Filter + Counter-Delta-Detection fuer alle Pub/Sub/DW/DR.
- §3 Status-Mask aktiv.
- §1.3 Set/Get-API live (DP/DW/DR; Pub/Sub/Topic get folgt aus gleichem
Pattern).
- 5 n/a (rejected/alternative)-Items mit Decision-Records im
.open.md (Bubble-Up, Sub-Aggregator, Java/Python/TS Bridge nutzen
alternative idiomatic-Pfade).
Siehe zerodds-listener-callbacks-1.0.open.md.
Abschluss-Bemerkung
Listener-Spec ist Phase-1-konform: API-Surface live, Storage in Registry live, aber Active-Wireup ist Phase-2 (kein Callback feuert zur Laufzeit). Die Vendor-Spec dokumentiert das explizit als Phase-1/2/3- Roadmap — der Audit-Status reflektiert das ehrlich. Phase-1-API ist brauchbar fuer Compile-Zeit-Bindings (C++/C#/Java); fuer Runtime- funktionale Listener wird Phase-2 benoetigt.