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:
@@ -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()))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user