go-polycall
v1.0.0
Published
Go (cgo) binding for the Polycall C library (binding ABI v1)
Maintainers
Readme
go-polycall
Go binding (cgo) for the Polycall
C library, binding ABI v1 (polycall.h, library >= 1.1.0).
import polycall "github.com/obinexus/go-polycall"The package links the installed library with #cgo pkg-config: polycall
and wraps every ABI v1 entry point. Configuration parsing, RPC framing and
the polycall-peer/1 wire protocol all stay in the C core.
| Area | API |
| --- | --- |
| Library | CheckABI(), LoadedABIVersion(), Version(), LibraryVersion(), Strerror() |
| Configuration | RunConfig(path, run), Describe(path) |
| RPC | Call(endpoint, service, operation, inputJSON, timeoutMs) |
| Peer nodes | OpenPeer(id, bind, token), (*Peer).Close / Endpoint / NodeID / Register / Unregister / List / Ping / Send / Recv / RecvWithCapacity / Cancel / Health |
| Errors | *Error{Code, Name, Detail, Output, Needed}; errors.Is(err, polycall.ErrTimeout) |
Name is polycall_strerror(code), Detail is polycall_last_error()
captured inside the same C call as the failure (the detail is thread-local
in the core and a goroutine may change OS threads between cgo calls, so the
small C shims in pcshim.c fetch it immediately). Every function first
checks polycall_ffi_abi_version() == 1.
Requirements
- Go 1.22+, cgo enabled, a C compiler and
pkg-config. - The installed Polycall core:
polycall.pconPKG_CONFIG_PATH, the shared library on the loader path, and (for the tests) thepolycallCLI.
Loading: a missing library stops the program before main (Linux: exit 127
"cannot open shared object file"; Windows: STATUS_DLL_NOT_FOUND). Against
an old 1.0 library without the ABI v1 symbols, Windows refuses the program at
load time (STATUS_ENTRYPOINT_NOT_FOUND), and on Linux the package links with
-z now (Go 1.23 and later accept that flag in #cgo LDFLAGS), so the
loader's "undefined symbol" message stops it before main too. With Go 1.22
binding is lazy and that message ends the program at the first go-polycall
call instead (every entry point checks the ABI first); build with
CGO_LDFLAGS=-Wl,-z,now to get start-up binding there as well. A library
reporting another ABI version is refused by CheckABI() (ErrUnsupported).
Linux (core under /opt/polycall):
export PKG_CONFIG_PATH=/opt/polycall/lib/pkgconfig LD_LIBRARY_PATH=/opt/polycall/lib PATH=/opt/polycall/bin:$PATH
go test -v ./... && go test -race ./...Windows (cgo with MSYS2 UCRT64 gcc) works with either build of the core:
the UCRT64 build (libpolycall.dll, import library libpolycall.dll.a) or
the MSVC build (polycall.dll; MinGW's linker reads its import library
polycall.lib). Point PKG_CONFIG_PATH at the build's lib\pkgconfig:
$core = 'C:\path\to\windows-ucrt64-x64' # or ...\windows-msvc-x64
$env:CGO_ENABLED = '1'; $env:CC = 'C:\msys64\ucrt64\bin\gcc.exe'
$env:PKG_CONFIG = 'C:\msys64\ucrt64\bin\pkg-config.exe'
$env:PKG_CONFIG_PATH = "$core\lib\pkgconfig"
$env:PATH = "$core\bin;C:\msys64\ucrt64\bin;$env:PATH" # the DLL, polycall.exe
$env:GOCACHE = "$env:LOCALAPPDATA\go-build-$(Split-Path $core -Leaf)"
go test -race -v ./...Use a separate GOCACHE (or go build -a) per core: the Go build cache
does not key cgo packages on PKG_CONFIG_PATH, so switching cores with a
shared cache can reuse objects linked against the other DLL.
Example
if err := polycall.RunConfig("go-polycallrc", true); err != nil {
log.Fatal(err)
}
inbox, _ := polycall.OpenPeer("beta", "127.0.0.1:0", "shared-token")
defer inbox.Close()
sender, _ := polycall.OpenPeer("alpha", "", "shared-token") // send-only
defer sender.Close()
ep, _ := inbox.Endpoint()
if err := sender.Send(ep, []byte("hello"), "m-1", 5*time.Second); err != nil {
log.Fatal(err)
}
msg, _ := inbox.Recv(polycall.WaitForever) // msg.Sender == "alpha", msg.ID == "m-1"
out, err := polycall.Call("127.0.0.1:8084", "inventory", "get", `{"item_id":"widget-a"}`, 2000)go run ./examples/basic validates go-polycallrc;
cmd/polycall-peer-tool is a tiny peer process used by the cross-language
interop test scripts/interop-rust.sh (Go <-> Rust, both directions).
Tests
polycall_test.go runs against the real library and CLI
(POLYCALL_CLI, default polycall on PATH; tests needing the CLI are
reported as SKIP when it is missing, and fail with POLYCALL_REQUIRE_CLI=1).
It covers the checklist of the core's docs/BINDING_ABI.md: version/ABI
check; RunConfig valid / missing / invalid / strict / unsupported TLS and a
non-ASCII path; Describe; Call against polycall start and
polycall daemon start (success, unknown operation, remote error, deadline,
invalid input, concurrent calls, authenticated stop, no runtime); two
nodes exchanging both ways with sender and id verified; empty, UTF-8,
binary-with-NUL, 1 MiB and 1 MiB + 1 payloads; registry ownership; duplicate
id delivered once; auth failure; dead peer; receive timeout; too-small
buffer; cancel/close waking a blocked Recv while other goroutines keep
working; double close, calls after close, invalid handles; integer
boundaries (timeouts, capacities, id lengths); concurrent senders; and
interop with a polycall peer serve C node in both directions. Only
documented CLI arguments are used.
loader_test.go checks the loading failures above by re-running the test
binary against an empty directory, the real polycall 1.0.0 library
(POLYCALL_TEST_OLD_LIBRARY) and an ABI-2 stand-in built with $CC
(POLYCALL_REQUIRE_LOADER_TESTS=1 turns a missing prerequisite into a
failure).
npm
go-polycall (not yet published) carries the same sources for
JavaScript tooling; it does not load the library from JavaScript.
License: ISC (see LICENSE, as declared in package.json).
