nim-ffi/CHANGELOG.md

10 KiB

Changelog

All notable changes to this project are documented in this file.

[Unreleased]

Changed

  • User event callbacks now run on a dedicated event thread fed by a bounded SPSC queue (default capacity 1024), so a slow listener can no longer block the FFI thread or concurrent add_event_listener / remove_event_listener calls (#6).
  • Replaced the dedicated watchdog thread with a heartbeat check that runs on the event thread. The FFI thread advances an atomic heartbeat each loop iteration; if it stalls for more than 1s past the start-up grace window, the event thread emits the not_responding event.
  • declareLibrary no longer emits the shared-library soname / install_name linker flags when building as an executable (--app:lib guard), so FFI code can be unit-tested as a plain binary — fatal on macOS, where -install_name requires -dynamiclib.

Added

  • {.ffiEvent.} no longer requires an explicit wire-name string: when omitted it is derived from the proc name via camelToSnakeCase (onPeerConnectedon_peer_connected), matching how {.ffi.} derives its C export symbol. Pass a string literal only to override it.
  • FFI annotations ({.ffi.}, {.ffiCtor.}, {.ffiDtor.}, {.ffiEvent.}, {.ffiHandle.}, {.ffiRaw.}) that expand after genBindings() now produce a loud compile error instead of being silently dropped from the generated bindings.
  • C binding generator (-d:targetLang=c): emits a header-only C binding (<lib>.h) plus a CMakeLists.txt, alongside the existing Rust / C++ / CDDL backends. Requests/responses travel as CBOR using the same vendored TinyCBOR the C++ backend uses. C has no generics or overloading, so each seq[T] / Option[T] is monomorphised into its own struct + encode/decode/free triple. The high-level <lib>_ctx_* API is asynchronous: each method/constructor takes a typed result callback and the binding owns and reclaims all reply data and error strings (valid only for the duration of the callback), so the caller never frees anything — there is no blocking wait and no manual-free contract. Shared codegen helpers were extracted into ffi/codegen/common.nim (used by both the C and C++ backends). New nimble genbindings_c / genbindings_c_echo / check_bindings_c / test_c_e2e tasks, a tests/e2e/c ctest harness, and a tests/unit/test_c_codegen.nim unit suite.
  • Configurable per-request handler timeout with a finite default: each FFIContext now carries a defaultRequestTimeout (5s) applied to every handler, replacing the previous unbounded wait so a wedged handler can no longer hang a foreign caller forever. On trip the caller is unblocked with an ffi request timed out after <n>ms err; the handler is left running (not cancelled, since a hard-cancel mid-call into the underlying library can leave it partial), and the callback still fires exactly once. Override per proc with a "timeout = <ms>" spec (e.g. {.ffi: "timeout = 30000".}), parsed like the abi = ... spec; runtime-only, codegen ignores it (#93).
  • Per-interaction ABI-format annotations: declareLibrary now takes an optional defaultABIFormat ("cbor" default, or "c") that every {.ffi.} / {.ffiCtor.} / {.ffiDtor.} / {.ffiRaw.} / {.ffiEvent.} inherits, and each annotation can override it with an "abi = c" / "abi = cbor" spec (e.g. {.ffi: "abi = cbor".}). declareLibrary is now required before any FFI annotation (#78).
  • c (flat C-struct) ABI codec: every {.ffi: "abi = c".} type gets a <T>_CWire companion plus cwirePack / cwireUnpack / cwireFree. This first slice covers the flat path — POD scalars and string (as cstring); composite fields follow. (The c proc-dispatch path and its CBOR-free C generator landed later in this release — see the -d:targetLang=c_abi entry below; c events remain CBOR-only.)
  • CBOR-free abi = c C binding generator (-d:targetLang=c_abi): emits a single self-contained <lib>.h whose flat _CWire structs are the C ABI, so the C consumer passes native structs and links no CBOR at all (contrast the CBOR -d:targetLang=c backend). The c proc-dispatch path is now wired end-to-end: the generated exported wrappers cwireUnpack the request into a Nim object, reuse the existing CBOR thread transport internally, and a Nim reply trampoline cwirePacks the response back into a flat struct for the caller's typed callback. abiCodegenImplemented now accepts c for proc/ctor/dtor annotations (events remain CBOR-only). New examples/echo/c_abi_bindings/ (checked in beside the CBOR c_bindings/ for comparison), nimble genbindings_c_abi_echo / check_bindings_c_abi / test_c_abi_e2e / test_c_abi_e2e_sanitized tasks, and a tests/e2e/c_abi ctest harness (#105).
  • tests/bench/bench_codec.nim (+ nimble bench_codec): a single-process microbenchmark comparing the cbor and c codecs across payload shapes, isolating codec cost from the (identical) thread/callback round-trip.
  • Queue-overflow handling: when the bounded event queue is full, the library sets a sticky "stuck" flag, logs an error, fires not_responding from the event thread, and rejects subsequent sendRequestToFFIThread calls with event queue stuck - library cannot accept new requests.

[0.2.0] - 2026-06-04

Major release introducing the CBOR-based wire format, CBOR-backed FFI events with a multi-listener registry, multi-language binding generation (C++, Rust, CDDL), CI hardening with sanitizers, and several robustness fixes around context lifetime and memory safety.

Added

  • CBOR serialization as the FFI wire format, replacing the previous JSON/string-based serial.nim (#23).
  • CBOR-backed FFI events: event payloads are now serialized with CBOR (#39).
  • Multi-listener event registry (FFIEventRegistry) and its wiring into FFIContext (#45, #49).
  • Event-listener ABI with per-event typed listeners (#50).
  • C++ typed per-event listeners in the generated bindings (#51).
  • Rust per-event typed listeners (add_on_<x>_listener + wildcard add_event_listener) (#52) and Rust event example bindings/clients (#53).
  • C++ binding generator with end-to-end tests driven by CMake/CTest (#27), later expanded with multi-context, cross-library, pipeline, and stress tests (#42).
  • CDDL schema generator for the FFI types (#24).
  • CI pipeline: parallel test execution (#26), AddressSanitizer / UndefinedBehaviorSanitizer / ThreadSanitizer jobs (#34), and a cross-platform OS matrix for the C++ e2e suite (#38).
  • CBOR type-coverage tests (#41).

Changed

  • Removed the redundant ffiType macro; the ffi macro is now the single authoring entry point (#22).
  • Generated C++ avoids move constructors and assignment operators (#36) and no longer throws exceptions across the binding boundary (#46).
  • Removed the wildcard event listener; event dispatch is now strictly per-event (#70).

Fixed

  • Use-after-free in the event/context lifetime path (#47).

[0.1.4] - 2026-05-13

Full changelog

Added

  • Simplified FFI authoring with auto-generated C++ and Rust language bindings, including new ffi/codegen/cpp.nim, ffi/codegen/rust.nim and shared ffi/codegen/meta.nim helpers (#15).
  • Rust example bindings and clients under examples/nim_timer/ (rust_bindings and rust_client, the latter with a Tokio async variant) (#15).
  • JSON/string-based FFI (de)serialization via ffi/serial.nim (ffiSerialize/ffiDeserialize), with tests/test_serial.nim coverage. (CBOR replaced this layer later, in 0.2.0.)
  • FFI context pool (ffi/ffi_context_pool.nim) using a fixed array of contexts.
  • Test suite expansion: test_alloc.nim, test_ctx_validation.nim, test_ffi_context.nim, test_gc_compat.nim.
  • Continuous integration pipeline (#12).

Fixed

  • Context buffer overflow (#21).
  • Use a fixed array of contexts to avoid consuming all file descriptors (#14).
  • Memory leaks (#11).
  • Add install_name for macOS shared libraries (#8).

Changed

  • Run tests with the refc garbage collector (#20).
  • Remove CatchableError usage (#19).
  • Update license files to comply with Logos licensing requirements.

[0.1.3] - 2026-01-23

Fixed

  • Properly import and re-export chronicles so downstream packages get the logging macros transitively.

[0.1.2] - 2026-01-23

Fixed

  • Re-export chronicles and std/tables when the ffi module is imported, so generated code resolves these symbols at the call site.

[0.1.1] - 2026-01-23

Initial tagged release.

Added

  • Core ffi macro for declaring procs exposed across the FFI boundary.
  • FFIContext with a dedicated worker thread, request dispatch, and a watchdog with configurable timeout (#7).
  • License files updated to comply with Logos licensing requirements.