prolog-polycall
v1.1.0
Published
SWI-Prolog foreign library for the Polycall binding ABI v1 (libpolycall >= 1.1.0): config validation, RPC calls and peer nodes
Maintainers
Readme
prolog-polycall
SWI-Prolog binding for the Polycall
core's binding ABI v1 (polycall.h, libpolycall >= 1.1.0): configuration
validation, polycall_rpc calls and peer-to-peer nodes. npm source
distribution prolog-polycall (not published yet).
src/prolog_polycall.c is a SWI-Prolog foreign library that includes the
real <polycall.h> and links the core through pkg-config;
src/prolog_polycall.pl is the Prolog module. Configuration parsing, the
wire protocols and the peer transport stay in libpolycall.
Build
Requirements: SWI-Prolog >= 9 (swipl, headers), a C11 compiler, make,
pkg-config, and the Polycall core >= 1.1.0 installed with its polycall.pc.
export PKG_CONFIG_PATH=/opt/polycall/lib/pkgconfig # if the core is not in a system prefix
make # -> lib/prolog_polycall.so
swipl -p foreign=lib examples/basic.pl prolog-polycallrcOn Linux the foreign library is linked with -z now and a RUNPATH to the
core's libdir: a missing core or an old 1.0 core without the ABI v1 symbols
makes use_foreign_library/1 fail with a clear dynamic-loader error. A core
whose polycall_ffi_abi_version() is not 1 is reported when the module loads
and every API predicate then throws
polycall_error(-15, 'POLYCALL_E_UNSUPPORTED', ...) — it is never used.
On Windows (MSYS2 UCRT64 / MinGW-w64 GCC with GNU make), the Makefile's
Windows_NT branch builds lib/prolog_polycall.dll, linking libswipl
from the SWI-Prolog installation (PLBASE/bin) and the core's import library
through pkg-config, e.g. the UCRT64 core (libpolycall.dll,
lib/libpolycall.dll.a, lib/pkgconfig/polycall.pc):
export PKG_CONFIG_PATH=/c/polycall/lib/pkgconfig # the core's prefix
make SWIPL="/c/Program Files/swipl/bin/swipl.exe"
PATH="/c/polycall/bin:$PATH" swipl -p foreign=lib examples/basic.pl prolog-polycallrcA DLL resolves its imports when it is loaded, so a missing core or a 1.0
core without the ABI v1 symbols makes use_foreign_library/1 fail there
too. QA status: built and tested on Linux x86_64 (SWI-Prolog 10.1.16 and
9.0.4); the Windows and macOS (Darwin) branches are implemented but have
not been run in QA yet (no SWI-Prolog on the Windows QA host, no macOS host).
API (module prolog_polycall)
:- use_module(library(prolog_polycall)). % or use_module('src/prolog_polycall')
?- polycall_abi_version(1), polycall_version(V).
?- run_config('prolog-polycallrc', Status). % polycall_ffi_run_config(Path, 1); Status unchanged
?- run_config('prolog-polycallrc', false, S). % run=0: unknown keys are warnings
?- run_config_or_throw('prolog-polycallrc').
?- polycall_describe('prolog-polycallrc', JSON).
% one polycall_rpc round trip to `polycall start` / `polycall daemon start`
?- polycall_call('127.0.0.1:8084', inventory, get, '{"item_id":"widget-a"}', 2000, Out).
?- peer_open(alpha, "127.0.0.1:0", '', H), % '' bind = send-only, '' token = none
peer_register(H, beta, '127.0.0.1:9002'),
peer_send(H, beta, utf8("hello"), 'msg-1', 5000),
peer_recv(H, 5000, message(Sender, MessageId, Payload)),
peer_list(H, Pairs), peer_health(H, JSON), peer_ping(H, beta, 2000),
peer_unregister(H, beta), peer_cancel(H), peer_close(H).Payloads are octets: a string/atom/code list of chars 0..255 (binary-safe,
NUL allowed), or utf8(Text). Received payloads are strings of octets.
Timeouts are milliseconds or infinite. Failures throw
error(polycall_error(Status, Name, Detail), context(Pred/Arity, _))with the POLYCALL_E_* status, its polycall_strerror name and the
polycall_last_error detail of this thread. polycall_call/6 adds the remote
error object (polycall_error(Status, Name, Detail, Output)), and a
peer_recv/4 with a too-small capacity throws
polycall_error(-10, 'POLYCALL_E_TOO_LARGE', Detail, needed(Bytes)) and leaves
the message queued. A peer_recv blocked in one thread is woken by
peer_cancel/1 or peer_close/1 from another; other Prolog threads keep
running while one is blocked in the core, but the blocked thread handles
thread_signal/2 (e.g. an abort) only after the call returns.
Arguments are checked before they reach C, with standard Prolog errors:
timeouts must be integers in 0..4294967295 or infinite
(domain_error(polycall_timeout_ms, T), type_error(integer, T)), handles
must fit int32 (domain_error(polycall_handle, H)), a peer_recv/4
capacity must be >= 0 (domain_error(polycall_capacity, C); capacities
above the 1 MiB message limit are clamped, never allocated), and text
arguments (paths, ids, endpoints, JSON, tokens) must not contain code 0,
which would reach C silently truncated (domain_error(polycall_text, T)).
Changed in 1.1.0: errors were polycall_error(Status); they now carry the
name and detail.
Tests
tests/run-real-core.sh builds the foreign library and runs the PlUnit
suite (tests/prolog_polycall_tests.pl) against the real installed core:
version/ABI, a missing core, an old core and an ABI 2 core in child swipl
processes, run_config, polycall_call against a live polycall start
runtime, two nodes both directions, payload matrix (empty, UTF-8, binary +
NUL, 1 MiB, 1 MiB + 1), registry ownership, de-duplication, auth, dead peers,
timeouts, small buffers, cancel and close waking blocked receivers (threads),
handle lifecycle, 8 concurrent sender threads, argument bounds (timeouts,
capacities, handles, NUL in text), a non-ASCII configuration path, and
interop with a polycall peer serve C node. A missing swipl, make,
pkg-config or core is reported as SKIP (exit 77), never as success; the run
fails unless every test ran and passed.
sh tests/run-real-core.sh # core in /opt/polycall, or POLYCALL_PREFIX / POLYCALL_LIBRARY
sh tests/run-memcheck.sh asan # the foreign library under ASan + UBSan
sh tests/run-memcheck.sh valgrind # the whole suite under valgrind memcheckThe ASan run preloads the sanitizer runtime into swipl with
use_sigaltstack=0: SWI-Prolog installs its own alternate signal stack per
thread, and ASan's default would try to unmap that buffer when the thread
exits (an ASan CHECK failure that is not a defect in this code).
License
MIT, Nnamdi Michael Okpala ([email protected]), see LICENSE.
