Skip to content

feat(codegen): generated C sync/blocking wrappers + Rust e2e suite#123

Draft
gmelodie wants to merge 1 commit into
masterfrom
feat/generated-sync-wrappers
Draft

feat(codegen): generated C sync/blocking wrappers + Rust e2e suite#123
gmelodie wants to merge 1 commit into
masterfrom
feat/generated-sync-wrappers

Conversation

@gmelodie

@gmelodie gmelodie commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Every generated C call was async-with-callback, so each C consumer had to hand-roll the same ceremony — waiter structs, atomic done-flags, polling loops, a RUN() macro — just to make one sequential call (examples/timer/c_bindings/main.c was 226 lines, ~150 of it boilerplate). The C++ and Rust backends already generate blocking wrappers; the C surface had no equivalent.

This ports the C++ sync_call_helper.hpp.tpl pattern to C. A new ffi/codegen/templates/c/sync_call_helper.h.tpl provides a blocking-call helper (pthread mutex/condvar on POSIX, SRWLOCK/CONDITION_VARIABLE on Win32 — the same platform split the examples already use), and ffi/codegen/c.nim now emits a <lib>_ctx_<method>_sync(...) alongside every async method plus <lib>_ctx_create_sync(...) for the constructor. Each _sync call submits, waits up to timeout_ms, decodes the reply straight from a private copy of the payload bytes into a caller-owned out-param (released with the generated <lib>_free_<Type>() helper), and returns 0 — or fills err_buf and returns non-zero. The blocking state is heap-allocated and reference-counted so a callback that fires after the caller has already timed out writes into a live object and drops the last reference cleanly, with no use-after-free.

Rust already generates blocking wrappers (ctx.echo(..) blocks; ctx.echo_async(..).await doesn't), so its half of the task is a new end-to-end suite rather than codegen.

Affected Areas

Codegen: ffi/codegen/c.nim (new emitMethodSync / emitConstructorSync, sharing an emitSyncSubmitPrologue helper) and the new sync_call_helper.h.tpl; generateCBindings now also writes the shared nim_ffi_sync.h.

Generated bindings: regenerated timer + echo C headers, each gaining the _sync wrappers and the shared nim_ffi_sync.h.

Example: examples/timer/c_bindings/main.c rewritten on the sync API (226 to 127 lines); its README documents both the blocking and async surfaces.

Tests: tests/e2e/c now exercises both the async and the new sync paths; new tests/e2e/rust crate covers the blocking and tokio-async wrappers, wired as nimble test_rust_e2e; tests/unit/test_c_codegen.nim assertions updated.

CI: the native-e2e job now also runs the C and Rust suites (Linux), and the sanitized workflow runs test_c_e2e_sanitized.

Impact on Library Users

Additive for C consumers: new <lib>_ctx_*_sync functions sit alongside the unchanged async API, and a new shared header nim_ffi_sync.h is emitted next to the other generated headers and included by <lib>.h. Reply out-params from _sync calls are caller-owned and released with the generated free helper. No change to Rust or C++ output.

Risk Assessment

New threading in generated headers is the main risk; mitigated by mirroring the battle-tested C++ helper and by the reference-counted state. Verified under ASan/UBSan and TSan via test_c_e2e_sanitized, plus the 12-test Rust suite. Codegen output was checked byte-identical across the dedup refactor and remains idempotent (check_bindings covers the new header). Backward compatible — the async callback API is untouched.

One follow-up left for review: the cpp-e2e CI job now also runs the C and Rust e2e steps, so its check name ("C++ E2E") undersells it; the job id was left unchanged to avoid silently dropping a required-status-check gate — rename at your discretion.

References

Mirrors ffi/codegen/templates/cpp/sync_call_helper.hpp.tpl (C++ backend). Implements task 1.3 (generated C sync/blocking wrappers).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant