feat(mt): move serial reset orchestration into Rust

Move the MT serial-state reset ordering into Rust. C retains parameter adjustment, private LDM table allocation and clearing, dictionary loading, window/checksum state, and allocator behavior behind callbacks; Rust now owns the reset sequencing and allocation-failure stop point.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1; cargo test --manifest-path rust/Cargo.toml
- ulimit -v 41943040; CARGO_BUILD_JOBS=1; cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings
- ulimit -v 41943040; make -B -C programs -j1 zstd
- ulimit -v 41943040; make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s
This commit is contained in:
2026-07-19 20:11:35 +02:00
parent 2557319583
commit e3fe5b5a24
2 changed files with 371 additions and 44 deletions
+230
View File
@@ -199,6 +199,9 @@ pub type ZSTDMT_initSetBufferSizeFn = unsafe extern "C" fn(*mut c_void, usize);
pub type ZSTDMT_initResizeRoundBufferFn = unsafe extern "C" fn(*mut c_void, usize) -> usize;
pub type ZSTDMT_initResetStreamFn = unsafe extern "C" fn(*mut c_void);
pub type ZSTDMT_initSerialResetFn = unsafe extern "C" fn(*mut c_void, usize) -> usize;
pub type ZSTDMT_serialResetVoidFn = unsafe extern "C" fn(*mut c_void);
pub type ZSTDMT_serialResetSetNbSeqFn = unsafe extern "C" fn(*mut c_void, usize);
pub type ZSTDMT_serialResetResizeFn = unsafe extern "C" fn(*mut c_void) -> c_int;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
@@ -1134,6 +1137,67 @@ where
serial_reset(target_section_size)
}
/// Run the MT serial-state reset policy while C retains its private LDM
/// tables, dictionary/window state, checksum state, and allocator callbacks.
/// The callback order mirrors the original reset path and stops immediately
/// when table allocation reports failure.
#[no_mangle]
pub unsafe extern "C" fn ZSTDMT_rust_serialStateReset(
enable_ldm: c_int,
checksum_enabled: c_int,
max_nb_seq: usize,
opaque: *mut c_void,
reset_next_job: Option<ZSTDMT_serialResetVoidFn>,
reset_checksum: Option<ZSTDMT_serialResetVoidFn>,
set_nb_seq: Option<ZSTDMT_serialResetSetNbSeqFn>,
reset_window: Option<ZSTDMT_serialResetVoidFn>,
resize_tables: Option<ZSTDMT_serialResetResizeFn>,
zero_tables: Option<ZSTDMT_serialResetVoidFn>,
load_dictionary: Option<ZSTDMT_serialResetVoidFn>,
copy_window: Option<ZSTDMT_serialResetVoidFn>,
) -> c_int {
let (
Some(reset_next_job),
Some(reset_checksum),
Some(set_nb_seq),
Some(reset_window),
Some(resize_tables),
Some(zero_tables),
Some(load_dictionary),
Some(copy_window),
) = (
reset_next_job,
reset_checksum,
set_nb_seq,
reset_window,
resize_tables,
zero_tables,
load_dictionary,
copy_window,
)
else {
return 1;
};
unsafe {
reset_next_job(opaque);
if checksum_enabled != 0 {
reset_checksum(opaque);
}
if enable_ldm == ZSTD_PS_ENABLE {
set_nb_seq(opaque, max_nb_seq);
reset_window(opaque);
if resize_tables(opaque) != 0 {
return 1;
}
zero_tables(opaque);
load_dictionary(opaque);
copy_window(opaque);
}
}
0
}
/// C ABI entry point for the MT streaming initializer. C owns every
/// allocation, dictionary handle, synchronization object, and private context
/// mutation; this wrapper only connects those operations to the Rust policy.
@@ -3734,6 +3798,172 @@ mod tests {
}
}
#[derive(Default)]
struct SerialResetTestContext {
events: Vec<&'static str>,
max_nb_seq: usize,
resize_result: c_int,
}
unsafe extern "C" fn serial_reset_test_next_job(context: *mut c_void) {
unsafe {
(*context.cast::<SerialResetTestContext>())
.events
.push("next-job");
}
}
unsafe extern "C" fn serial_reset_test_checksum(context: *mut c_void) {
unsafe {
(*context.cast::<SerialResetTestContext>())
.events
.push("checksum");
}
}
unsafe extern "C" fn serial_reset_test_set_nb_seq(context: *mut c_void, max_nb_seq: usize) {
unsafe {
let context = &mut *context.cast::<SerialResetTestContext>();
context.events.push("seq-size");
context.max_nb_seq = max_nb_seq;
}
}
unsafe extern "C" fn serial_reset_test_window(context: *mut c_void) {
unsafe {
(*context.cast::<SerialResetTestContext>())
.events
.push("window");
}
}
unsafe extern "C" fn serial_reset_test_resize(context: *mut c_void) -> c_int {
unsafe {
let context = &mut *context.cast::<SerialResetTestContext>();
context.events.push("resize");
context.resize_result
}
}
unsafe extern "C" fn serial_reset_test_zero(context: *mut c_void) {
unsafe {
(*context.cast::<SerialResetTestContext>())
.events
.push("zero");
}
}
unsafe extern "C" fn serial_reset_test_dictionary(context: *mut c_void) {
unsafe {
(*context.cast::<SerialResetTestContext>())
.events
.push("dictionary");
}
}
unsafe extern "C" fn serial_reset_test_copy_window(context: *mut c_void) {
unsafe {
(*context.cast::<SerialResetTestContext>())
.events
.push("copy-window");
}
}
type SerialResetTestCallbacks = (
Option<ZSTDMT_serialResetVoidFn>,
Option<ZSTDMT_serialResetVoidFn>,
Option<ZSTDMT_serialResetSetNbSeqFn>,
Option<ZSTDMT_serialResetVoidFn>,
Option<ZSTDMT_serialResetResizeFn>,
Option<ZSTDMT_serialResetVoidFn>,
Option<ZSTDMT_serialResetVoidFn>,
Option<ZSTDMT_serialResetVoidFn>,
);
fn serial_reset_test_callbacks() -> SerialResetTestCallbacks {
(
Some(serial_reset_test_next_job),
Some(serial_reset_test_checksum),
Some(serial_reset_test_set_nb_seq),
Some(serial_reset_test_window),
Some(serial_reset_test_resize),
Some(serial_reset_test_zero),
Some(serial_reset_test_dictionary),
Some(serial_reset_test_copy_window),
)
}
#[test]
fn serial_state_reset_runs_ldm_operations_in_c_order() {
let mut context = SerialResetTestContext::default();
let callbacks = serial_reset_test_callbacks();
let result = unsafe {
ZSTDMT_rust_serialStateReset(
ZSTD_PS_ENABLE,
1,
123,
(&mut context as *mut SerialResetTestContext).cast(),
callbacks.0,
callbacks.1,
callbacks.2,
callbacks.3,
callbacks.4,
callbacks.5,
callbacks.6,
callbacks.7,
)
};
assert_eq!(result, 0);
assert_eq!(context.max_nb_seq, 123);
assert_eq!(
context.events,
vec![
"next-job",
"checksum",
"seq-size",
"window",
"resize",
"zero",
"dictionary",
"copy-window",
]
);
}
#[test]
fn serial_state_reset_stops_before_zeroing_after_resize_failure() {
let mut context = SerialResetTestContext {
resize_result: 1,
..Default::default()
};
let callbacks = serial_reset_test_callbacks();
let result = unsafe {
ZSTDMT_rust_serialStateReset(
ZSTD_PS_ENABLE,
0,
123,
(&mut context as *mut SerialResetTestContext).cast(),
callbacks.0,
callbacks.1,
callbacks.2,
callbacks.3,
callbacks.4,
callbacks.5,
callbacks.6,
callbacks.7,
)
};
assert_eq!(result, 1);
assert_eq!(
context.events,
vec!["next-job", "seq-size", "window", "resize"]
);
}
#[test]
fn init_c_stream_preserves_success_order_and_normalization() {
let projection = init_projection();