mirror of
https://github.com/logos-storage/nim-libplum.git
synced 2026-06-07 09:40:01 +00:00
1.9 KiB
1.9 KiB
nim-libplum
Nim binding for libplum (PCP / NAT-PMP / UPnP-IGD port mapping), with a chronos-based async wrapper.
Commands
git submodule update --init # vendor/libplum is required for everything
nimble test # builds vendor/libplum.a via cmake, then runs unit tests
nimble testIntegration # miniupnpd integration tests in Docker/Podman (NET_ADMIN)
nimble format # nph on libplum/ and tests/
Debug env vars: LIBPLUM_VERBOSE=1, TEST_VERBOSE=1, MINIUPNPD_VERBOSE=1.
Architecture
libplum/libplum.nim— raw C bindings (importc), no logiclibplum/plum.nim— public API: bridges libplum's C callback thread to chronostests/test_plum.nim— unit suite + integration suites gated by-d:miniupnp_protocolapi.md— user-facing API doc, keep in sync with plum.nimvendor/libplum— git submodule, built statically by nimble tasks
Threading invariants (critical)
mappingCallbackruns on libplum's internal C thread, wrapped inforeignThreadGc; it must stayraises: []and only touch thread-safe state- The mapping signal fires exactly once (first of SUCCESS/FAILURE/DESTROYED
via
resolved.exchange);createMappingowns it and closes it on the chronos loop thread after consuming that fire. Never callThreadSignalPtr.close()from the C thread: close() unregisters the fd from the calling thread's dispatcher MappingHandleis pinned withGC_refwhile libplum holdsuser_ptr; unpinned only in the DESTROYED callbackactiveMappingsis anAtomic[int]counting pinned handles; libplum is the source of truth for mapping state (hasMapping)- The wrapper relies on a libplum guarantee: DESTROYED fires exactly once for
every created mapping (explicit destroy, or
destroy_all_mappingsduringplum_cleanup, synchronously before it returns), verified invendor/libplum/src/client.c. Re-verify this when bumping the submodule