feat(compress): move trace context reset to Rust

Keep the C-owned trace slot behind an explicit pointer projection so the
Rust trace boundary owns the complete enabled-call lifecycle. Rust now reads
the context, publishes the record, and clears the slot after the callback;
C only supplies private CCtx fields and the weak callback adapter.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo check --tests (rust)
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --tests -- -A clippy::manual-bits -D warnings (rust)
- ulimit -v 41943040; make -j1
- ulimit -v 41943040; make -j1 -C tests test
- Focused trace lifecycle test added; standalone cargo test remains link-blocked by the existing C-owned decompression view symbols.
This commit is contained in:
2026-07-21 23:37:03 +02:00
parent 9589c5e327
commit c7789446dc
2 changed files with 57 additions and 9 deletions
+2 -3
View File
@@ -7362,7 +7362,7 @@ typedef void (*ZSTD_rust_traceEnd_f)(
void* context, unsigned long long traceCtx, const void* trace);
typedef struct {
void* callbackContext;
unsigned long long traceCtx;
unsigned long long* traceCtx;
int streaming;
unsigned dictionaryID;
size_t dictionarySize;
@@ -7413,7 +7413,7 @@ void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize)
#if ZSTD_TRACE
ZSTD_rust_traceEndState state;
state.callbackContext = cctx;
state.traceCtx = cctx->traceCtx;
state.traceCtx = &cctx->traceCtx;
state.streaming = cctx->inBuffSize > 0 || cctx->outBuffSize > 0
|| cctx->appliedParams.nbWorkers > 0;
state.dictionaryID = cctx->dictID;
@@ -7424,7 +7424,6 @@ void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize)
state.cctx = cctx;
state.traceEnd = ZSTD_rust_traceEnd_callback;
ZSTD_rust_traceEnd(&state);
cctx->traceCtx = 0;
#else
(void)cctx;
(void)extraCSize;
+55 -6
View File
@@ -2840,7 +2840,7 @@ const _: () = {
#[repr(C)]
pub struct ZSTD_rust_traceEndState {
callback_context: *mut c_void,
trace_ctx: u64,
trace_ctx: *mut u64,
streaming: c_int,
dictionary_id: c_uint,
dictionary_size: usize,
@@ -2855,9 +2855,9 @@ const _: () = {
assert!(size_of::<CompressTraceEndFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_traceEndState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_traceEndState, trace_ctx) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_traceEndState, streaming) == size_of::<usize>() + size_of::<u64>());
assert!(offset_of!(ZSTD_rust_traceEndState, dictionary_id) == size_of::<usize>() + size_of::<u64>() + size_of::<c_int>());
assert!(offset_of!(ZSTD_rust_traceEndState, dictionary_size) == size_of::<usize>() + size_of::<u64>() + 2 * size_of::<c_int>());
assert!(offset_of!(ZSTD_rust_traceEndState, streaming) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_traceEndState, dictionary_id) == 2 * size_of::<usize>() + size_of::<c_int>());
assert!(offset_of!(ZSTD_rust_traceEndState, dictionary_size) == 2 * size_of::<usize>() + 2 * size_of::<c_int>());
assert!(offset_of!(ZSTD_rust_traceEndState, uncompressed_size) == offset_of!(ZSTD_rust_traceEndState, dictionary_size) + size_of::<usize>());
assert!(offset_of!(ZSTD_rust_traceEndState, compressed_size) == offset_of!(ZSTD_rust_traceEndState, dictionary_size) + 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_traceEndState, params) == offset_of!(ZSTD_rust_traceEndState, dictionary_size) + 3 * size_of::<usize>());
@@ -2872,7 +2872,10 @@ pub unsafe extern "C" fn ZSTD_rust_traceEnd(state: *const ZSTD_rust_traceEndStat
let Some(state) = (unsafe { state.as_ref() }) else {
return;
};
if state.trace_ctx == 0 {
let Some(trace_ctx) = (unsafe { state.trace_ctx.as_ref() }).copied() else {
return;
};
if trace_ctx == 0 {
return;
}
let trace = ZSTD_Trace {
@@ -2890,9 +2893,10 @@ pub unsafe extern "C" fn ZSTD_rust_traceEnd(state: *const ZSTD_rust_traceEndStat
unsafe {
(state.trace_end)(
state.callback_context,
state.trace_ctx,
trace_ctx,
(&trace as *const ZSTD_Trace).cast(),
);
*state.trace_ctx = 0;
}
}
@@ -24277,4 +24281,49 @@ mod tests {
drop(Box::from_raw(state.callbacks.cast_mut()));
}
}
#[derive(Default)]
struct TraceEndTestContext {
calls: usize,
trace_ctx: u64,
version: c_uint,
}
unsafe extern "C" fn trace_end_test_callback(
context: *mut c_void,
trace_ctx: u64,
trace: *const c_void,
) {
let context = unsafe { &mut *context.cast::<TraceEndTestContext>() };
context.calls += 1;
context.trace_ctx = trace_ctx;
context.version = unsafe { (*trace.cast::<ZSTD_Trace>()).version };
}
#[test]
fn trace_end_publishes_once_and_clears_context() {
let mut callback_context = TraceEndTestContext::default();
let mut trace_ctx = 42;
let state = ZSTD_rust_traceEndState {
callback_context: (&mut callback_context as *mut TraceEndTestContext).cast(),
trace_ctx: &mut trace_ctx,
streaming: 1,
dictionary_id: 7,
dictionary_size: 11,
uncompressed_size: 13,
compressed_size: 17,
params: ptr::null(),
cctx: ptr::null(),
trace_end: trace_end_test_callback,
};
unsafe { ZSTD_rust_traceEnd(&state) };
assert_eq!(trace_ctx, 0);
assert_eq!(callback_context.calls, 1);
assert_eq!(callback_context.trace_ctx, 42);
assert_eq!(callback_context.version, ZSTD_VERSION_NUMBER as c_uint);
unsafe { ZSTD_rust_traceEnd(&state) };
assert_eq!(callback_context.calls, 1);
}
}