refactor(fileio): move compression teardown order to Rust

Keep fileio's compression-resource teardown order in the Rust policy layer:
dictionary, write pool, read pool, then compression context.  The C adapter
keeps cRess_t and each private resource layout local while exposing only
one callback projection to Rust, preserving the existing cleanup behavior.
The Rust entry point also treats a null state as a no-op and verifies the
projection layout at compile time.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml free_c_resources --lib
- ulimit -v 41943040; make -j1
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml --all-targets
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings
This commit is contained in:
2026-07-20 04:38:09 +02:00
parent 66e7799f04
commit dc83c98f06
2 changed files with 142 additions and 5 deletions
+89 -1
View File
@@ -12,7 +12,7 @@
use std::ffi::{c_void, CStr};
use std::io::{self, Write};
use std::mem::size_of;
use std::mem::{offset_of, size_of};
use std::os::raw::{c_char, c_int, c_uint};
use std::ptr;
@@ -103,6 +103,50 @@ pub struct FIO_listFileInfoProjection_t {
nbFiles: u32,
}
type FIO_freeCResourcesCallbackFn = unsafe extern "C" fn(*mut c_void);
/// C-owned compression-resource teardown projection.
///
/// Rust owns the stable cleanup order while C keeps `cRess_t`, dictionary,
/// asynchronous-pool, and stream layouts private behind callbacks.
#[repr(C)]
pub struct FIO_rust_freeCResourcesState {
callback_context: *mut c_void,
free_dict: FIO_freeCResourcesCallbackFn,
free_write_pool: FIO_freeCResourcesCallbackFn,
free_read_pool: FIO_freeCResourcesCallbackFn,
free_cctx: FIO_freeCResourcesCallbackFn,
}
const _: () = {
assert!(offset_of!(FIO_rust_freeCResourcesState, callback_context) == 0);
assert!(offset_of!(FIO_rust_freeCResourcesState, free_dict) == size_of::<usize>());
assert!(
offset_of!(FIO_rust_freeCResourcesState, free_write_pool) == 2 * size_of::<usize>()
);
assert!(
offset_of!(FIO_rust_freeCResourcesState, free_read_pool) == 3 * size_of::<usize>()
);
assert!(offset_of!(FIO_rust_freeCResourcesState, free_cctx) == 4 * size_of::<usize>());
assert!(size_of::<FIO_rust_freeCResourcesState>() == 5 * size_of::<usize>());
};
#[no_mangle]
pub unsafe extern "C" fn FIO_rust_freeCResources(
state: *const FIO_rust_freeCResourcesState,
) {
if state.is_null() {
return;
}
let state = unsafe { &*state };
unsafe {
(state.free_dict)(state.callback_context);
(state.free_write_pool)(state.callback_context);
(state.free_read_pool)(state.callback_context);
(state.free_cctx)(state.callback_context);
}
}
pub type FIO_listMultipleFilesIsStdinFn = unsafe extern "C" fn(*mut c_void, *const c_char) -> c_int;
pub type FIO_listMultipleFilesDisplayFn = unsafe extern "C" fn(*mut c_void);
pub type FIO_listMultipleFilesListFileFn = unsafe extern "C" fn(
@@ -1869,6 +1913,50 @@ mod tests {
use std::fs;
use std::mem::{align_of, offset_of, size_of};
#[derive(Default)]
struct FreeCResourcesTestState {
events: Vec<&'static str>,
}
unsafe extern "C" fn free_c_resources_dict_test(context: *mut c_void) {
let state = unsafe { &mut *context.cast::<FreeCResourcesTestState>() };
state.events.push("dict");
}
unsafe extern "C" fn free_c_resources_write_pool_test(context: *mut c_void) {
let state = unsafe { &mut *context.cast::<FreeCResourcesTestState>() };
state.events.push("write");
}
unsafe extern "C" fn free_c_resources_read_pool_test(context: *mut c_void) {
let state = unsafe { &mut *context.cast::<FreeCResourcesTestState>() };
state.events.push("read");
}
unsafe extern "C" fn free_c_resources_cctx_test(context: *mut c_void) {
let state = unsafe { &mut *context.cast::<FreeCResourcesTestState>() };
state.events.push("cctx");
}
#[test]
fn free_c_resources_preserves_teardown_order_and_null_policy() {
let mut context = FreeCResourcesTestState::default();
let state = FIO_rust_freeCResourcesState {
callback_context: &mut context as *mut FreeCResourcesTestState as *mut c_void,
free_dict: free_c_resources_dict_test,
free_write_pool: free_c_resources_write_pool_test,
free_read_pool: free_c_resources_read_pool_test,
free_cctx: free_c_resources_cctx_test,
};
unsafe {
FIO_rust_freeCResources(ptr::null());
FIO_rust_freeCResources(&state);
}
assert_eq!(context.events, ["dict", "write", "read", "cctx"]);
}
fn temporary_file_path(name: &str) -> std::path::PathBuf {
std::env::temp_dir().join(format!("zstd-fileio-prefs-{}-{name}", std::process::id()))
}