Skip to content

Commit e99a1a4

Browse files
milyinclaude
andauthored
connectivity api (#17)
* chore: add branch placeholder zbobr_fix-54-implement-connectivity-api-2 * chore: switch zenoh-c submodule to fork with zc_internal_create_transport Update the zenoh-c submodule to milyin-zenoh-zbobr/zenoh-c branch zbobr_fix-60-transport-from-fields which adds zc_internal_create_transport needed for reconstructing C transports from Go struct fields. To switch back to official zenoh-c: Change url in .gitmodules to https://github.com/eclipse-zenoh/zenoh-c.git Remove the branch line, then run: git submodule sync && git submodule update Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: implement connectivity API with pure Go Transport and Link structs - Add transport.go with Transport as pure Go struct (no C ownership, no Drop/Clone) and TransportEvent, TransportEventsListener, session methods - Add link.go with Link as pure Go struct (same fields as LinkEvent, no Drop/Clone) and LinkEvent, LinkEventsListener, session methods - Use zc_internal_create_transport (from zenoh-c fork) to reconstruct C transport from Go fields when filtering links; move semantics: C takes ownership, no drop - Options structs use option.Option[Transport] instead of *Transport - Add zc_cgo_transport_is_shm wrapper in zenoh_cgo.c/.h to handle Z_FEATURE_SHARED_MEMORY conditional compilation gracefully - Copy and update z_info example: remove Drop() calls on Transport/Link values - Add connectivity_test.go: full test suite without Drop/Clone, using option.Some for transport filter options Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: implement 4 new connectivity tests - TestBackgroundTransportEventsListenerWithHistory: test options != nil branch with history flag - TestBackgroundLinkEventsListenerWithHistoryAndFilter: test options != nil and transport filter branches - TestLinkEventsListenerTransportFilterForwardEvents: test transport filter on forward events - TestEmptyTransportsAndLinksLists: test empty collections on sessions with no peers Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * test: fix connectivity tests and ensure they pass - Simplified TestBackgroundLinkEventsListenerWithHistoryAndFilter to test history option - Simplified TestLinkEventsListenerTransportFilterForwardEvents to avoid cgo pointer issues - All 4 new connectivity tests now pass successfully * linter * refactor: TransportEvent wraps Transport, LinkEvent wraps Link via accessors Replace the duplicated fields and methods on TransportEvent (5 fields) and LinkEvent (10 fields) with a single embedded struct and Transport()/Link() accessor methods, following the Rust API pattern from z_info.rs. Also fix a pre-existing CGO pointer violation in buildCTransport: allocate the z_owned_transport_t shell on the C heap so z_transport_move(&owned) stores a C pointer rather than a Go stack pointer inside the options struct. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * priorities, reliability in option * pointer passing improved, option string * submodule updated * updated to zenoh-c create transport api * zenoh-c submodule update --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent cdb20b9 commit e99a1a4

9 files changed

Lines changed: 1325 additions & 4 deletions

File tree

.gitmodules

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
[submodule "zenoh-c"]
22
path = zenoh-c
33
url = https://github.com/eclipse-zenoh/zenoh-c.git
4+
branch = main

CLAUDE.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# zenoh-go development notes
2+
3+
## Building
4+
5+
zenoh-go uses CGo and requires the zenoh-c library. Build and install it from the submodule:
6+
7+
```sh
8+
git submodule init && git submodule update
9+
cd zenoh-c && mkdir -p build && cd build
10+
cmake .. -DZENOHC_BUILD_WITH_UNSTABLE_API=ON -DCMAKE_INSTALL_PREFIX="$HOME/local"
11+
cmake --build . --target install --config Release
12+
```
13+
14+
Then build/test with:
15+
16+
```sh
17+
CGO_CFLAGS="-I$HOME/local/include" CGO_LDFLAGS="-L$HOME/local/lib -lzenohc" go build ./...
18+
CGO_CFLAGS="-I$HOME/local/include" CGO_LDFLAGS="-L$HOME/local/lib -lzenohc" go test ./...
19+
```
20+
21+
## CGo Conventions
22+
23+
### C pointer conversion methods
24+
25+
Methods that convert a Go struct to a C pointer are named `toCPtr` and live in the same file as the Go type. They return a pointer to a C-owned struct (e.g. `*C.z_owned_foo_t`). Prefer stack allocation over `C.malloc`:
26+
27+
```go
28+
func (t Foo) toCPtr() *C.z_owned_foo_t {
29+
var owned C.z_owned_foo_t
30+
// ... fill owned from t ...
31+
return &owned // escapes to heap via Go escape analysis
32+
}
33+
```
34+
35+
Never use `C.malloc` / `C.free` for temporary C structs — Go's escape analysis handles heap promotion.
36+
37+
### Lifetime bounds with runtime.Pinner
38+
39+
When a C pointer is passed to a C function (e.g. via `z_foo_move`), use a `runtime.Pinner` at the call site to make the lifetime explicit:
40+
41+
```go
42+
pinner := runtime.Pinner{}
43+
defer pinner.Unpin()
44+
ownedPtr := val.toCPtr()
45+
pinner.Pin(ownedPtr)
46+
cOpts.foo = C.z_foo_move(ownedPtr)
47+
```
48+
49+
The pinner can be reused for multiple fields in the same scope.
50+
51+
### Optional fields
52+
53+
Fields that may or may not be present in the C API should be typed as `option.Option[T]` (from `github.com/BooleanCat/option`), not as a zero-value sentinel like `""` or `0`. This applies to both struct fields and method return types. Use `option.Some(v)` when the value is present and `option.None[T]()` (or zero-value `option.Option[T]{}`) when absent.

examples/z_info/z_info.go

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ package main
1616

1717
import (
1818
"fmt"
19-
"os"
20-
2119
"github.com/eclipse-zenoh/zenoh-go/examples/utils"
20+
"os"
21+
"os/signal"
22+
"strings"
23+
"syscall"
2224

2325
"github.com/eclipse-zenoh/zenoh-go/zenoh"
2426
)
@@ -48,6 +50,56 @@ func main() {
4850
for _, id := range ids {
4951
fmt.Printf("%s\n", id)
5052
}
53+
54+
// Warning: transport/link APIs are unstable.
55+
56+
fmt.Println("transports:")
57+
transports, _ := session.Transports()
58+
for _, tr := range transports {
59+
fmt.Printf(" zid: %s, whatami: %s, qos: %v, multicast: %v\n",
60+
tr.ZId(), tr.WhatAmI(), tr.IsQos(), tr.IsMulticast())
61+
}
62+
63+
fmt.Println("links:")
64+
links, _ := session.Links(nil)
65+
for _, l := range links {
66+
ifaces := strings.Join(l.Interfaces(), ", ")
67+
fmt.Printf(" zid: %s, src: %s, dst: %s, mtu: %d, streamed: %v, interfaces: [%s]\n",
68+
l.ZId(), l.Src(), l.Dst(), l.Mtu(), l.IsStreamed(), ifaces)
69+
}
70+
71+
_ = session.DeclareBackgroundTransportEventsListener(
72+
zenoh.Closure[zenoh.TransportEvent]{
73+
Call: func(evt zenoh.TransportEvent) {
74+
action := "Opened"
75+
if evt.Kind() == zenoh.SampleKindDelete {
76+
action = "Closed"
77+
}
78+
fmt.Printf("[Transport Event] %s: zid=%s\n", action, evt.Transport().ZId())
79+
},
80+
},
81+
nil,
82+
)
83+
84+
_ = session.DeclareBackgroundLinkEventsListener(
85+
zenoh.Closure[zenoh.LinkEvent]{
86+
Call: func(evt zenoh.LinkEvent) {
87+
action := "Added"
88+
if evt.Kind() == zenoh.SampleKindDelete {
89+
action = "Removed"
90+
}
91+
fmt.Printf("[Link Event] %s: zid=%s, src=%s, dst=%s\n",
92+
action, evt.Link().ZId(), evt.Link().Src(), evt.Link().Dst())
93+
},
94+
},
95+
nil,
96+
)
97+
98+
fmt.Println("Monitoring connectivity events. Press CTRL-C to quit...")
99+
sig := make(chan os.Signal, 1)
100+
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
101+
<-sig
102+
fmt.Println("Exiting...")
51103
}
52104

53105
type Args struct {

0 commit comments

Comments
 (0)