feat(codegen): generated C sync/blocking wrappers + Rust e2e suite#123
Draft
gmelodie wants to merge 1 commit into
Draft
feat(codegen): generated C sync/blocking wrappers + Rust e2e suite#123gmelodie wants to merge 1 commit into
gmelodie wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.cwas 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.tplpattern to C. A newffi/codegen/templates/c/sync_call_helper.h.tplprovides a blocking-call helper (pthread mutex/condvar on POSIX, SRWLOCK/CONDITION_VARIABLE on Win32 — the same platform split the examples already use), andffi/codegen/c.nimnow emits a<lib>_ctx_<method>_sync(...)alongside every async method plus<lib>_ctx_create_sync(...)for the constructor. Each_synccall submits, waits up totimeout_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 fillserr_bufand 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(..).awaitdoesn't), so its half of the task is a new end-to-end suite rather than codegen.Affected Areas
Codegen:
ffi/codegen/c.nim(newemitMethodSync/emitConstructorSync, sharing anemitSyncSubmitProloguehelper) and the newsync_call_helper.h.tpl;generateCBindingsnow also writes the sharednim_ffi_sync.h.Generated bindings: regenerated timer + echo C headers, each gaining the
_syncwrappers and the sharednim_ffi_sync.h.Example:
examples/timer/c_bindings/main.crewritten on the sync API (226 to 127 lines); its README documents both the blocking and async surfaces.Tests:
tests/e2e/cnow exercises both the async and the new sync paths; newtests/e2e/rustcrate covers the blocking and tokio-async wrappers, wired asnimble test_rust_e2e;tests/unit/test_c_codegen.nimassertions 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_*_syncfunctions sit alongside the unchanged async API, and a new shared headernim_ffi_sync.his emitted next to the other generated headers and included by<lib>.h. Reply out-params from_synccalls 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_bindingscovers the new header). Backward compatible — the async callback API is untouched.One follow-up left for review: the
cpp-e2eCI 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).