Problem
crates/node/src/provider/ffi.rs exposes ProviderPtr as two raw pointers and reconstructs Arc<Provider> / Arc<Runtime> with Arc::from_raw on every FFI call. A raw Arc ownership token must be converted back exactly once; repeated conversion is not a sound ownership model. The current check_arc threshold also adds strong references without matching decrements.
The C API additionally returns CString::into_raw() from request(), but crates/node/include/rings.h has no matching string-free function or provider-destroy function. The global FFI E2E inbox registry consequently has no explicit cleanup path.
Proposed direction
Replace ProviderPtr with an opaque *mut ProviderHandle allocated as Box<ProviderHandle>. The handle should own ordinary Arc<Provider>, Arc<Runtime>, and its FFI inbox state directly. Do not expose or reconstruct raw Arc pointers across the boundary.
Add explicit C ABI cleanup functions, e.g.:
provider_destroy(ProviderHandle *)
provider_string_free(char *)
Remove check_arc, increase_provider_count, and increase_runtime_count. Ensure destroy removes the handle's inbox/associated state.
Acceptance criteria
- Every raw allocation has one documented corresponding free API.
- Repeated create/listen/request/take-events/destroy cycles are covered by an automated FFI integration test.
- Returned request strings are freed by the caller through the documented API.
- No FFI call uses
Arc::from_raw or manual Arc::increment_strong_count for handle lifetime.
- The public header documents ownership and invalid-handle behavior.
Problem
crates/node/src/provider/ffi.rsexposesProviderPtras two raw pointers and reconstructsArc<Provider>/Arc<Runtime>withArc::from_rawon every FFI call. A raw Arc ownership token must be converted back exactly once; repeated conversion is not a sound ownership model. The currentcheck_arcthreshold also adds strong references without matching decrements.The C API additionally returns
CString::into_raw()fromrequest(), butcrates/node/include/rings.hhas no matching string-free function or provider-destroy function. The global FFI E2E inbox registry consequently has no explicit cleanup path.Proposed direction
Replace
ProviderPtrwith an opaque*mut ProviderHandleallocated asBox<ProviderHandle>. The handle should own ordinaryArc<Provider>,Arc<Runtime>, and its FFI inbox state directly. Do not expose or reconstruct rawArcpointers across the boundary.Add explicit C ABI cleanup functions, e.g.:
provider_destroy(ProviderHandle *)provider_string_free(char *)Remove
check_arc,increase_provider_count, andincrease_runtime_count. Ensure destroy removes the handle's inbox/associated state.Acceptance criteria
Arc::from_rawor manualArc::increment_strong_countfor handle lifetime.