Verify preview admission bounds under contention and worker unwind
This commit is contained in:
1 parent
ec75a52cf7
commit
c8e611081f
2 files changed
+55
-2
No files matched your search
@@ -413,6 +413,51 @@ pub fn error_response(status: StatusCode) -> Response<Vec<u8>> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn concurrent_admission_stays_bounded_and_unwind_releases_capacity() {
|
||||||
|
let registry = Arc::new(PreviewRegistry::default());
|
||||||
|
let active = Arc::new(AtomicUsize::new(0));
|
||||||
|
let barrier = Arc::new(std::sync::Barrier::new(8));
|
||||||
|
let workers: Vec<_> = (0..8)
|
||||||
|
.map(|_| {
|
||||||
|
let registry = registry.clone();
|
||||||
|
let active = active.clone();
|
||||||
|
let barrier = barrier.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
barrier.wait();
|
||||||
|
for _ in 0..500 {
|
||||||
|
if let Some(admission) = registry.admit() {
|
||||||
|
let count = active.fetch_add(1, Ordering::SeqCst) + 1;
|
||||||
|
assert!(count <= MAX_REQUESTS);
|
||||||
|
std::thread::yield_now();
|
||||||
|
active.fetch_sub(1, Ordering::SeqCst);
|
||||||
|
drop(admission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
for worker in workers {
|
||||||
|
worker.join().unwrap();
|
||||||
|
}
|
||||||
|
assert_eq!(active.load(Ordering::SeqCst), 0);
|
||||||
|
assert_eq!(registry.requests.load(Ordering::SeqCst), 0);
|
||||||
|
let first = registry.admit().unwrap();
|
||||||
|
let interrupted = registry.clone();
|
||||||
|
assert!(std::panic::catch_unwind(move || {
|
||||||
|
let _second = interrupted.admit().unwrap();
|
||||||
|
panic!("injected preview worker panic");
|
||||||
|
})
|
||||||
|
.is_err());
|
||||||
|
let second = registry
|
||||||
|
.admit()
|
||||||
|
.expect("Unwinding must release the second slot");
|
||||||
|
assert!(registry.admit().is_none());
|
||||||
|
drop(first);
|
||||||
|
drop(second);
|
||||||
|
assert_eq!(registry.requests.load(Ordering::SeqCst), 0);
|
||||||
|
}
|
||||||
|
|
||||||
struct Fixture {
|
struct Fixture {
|
||||||
_directory: tempfile::TempDir,
|
_directory: tempfile::TempDir,
|
||||||
database: PathBuf,
|
database: PathBuf,
|
||||||
|
|||||||
+10
-2
@@ -21,7 +21,8 @@ Index-change notifications also advance the preview selection identity, so a
|
|||||||
same-path image/recording reloads rather than retaining previously decoded media.
|
same-path image/recording reloads rather than retaining previously decoded media.
|
||||||
Failed native preview preparation and image/audio/video loading now offer an explicit **Retry preview**
|
Failed native preview preparation and image/audio/video loading now offer an explicit **Retry preview**
|
||||||
button. It invalidates the old request revision and cannot restart a selection
|
button. It invalidates the old request revision and cannot restart a selection
|
||||||
that has since changed. There is no automatic retry loop. The added regression
|
that has since changed. Busy preparation receives at most one delayed retry;
|
||||||
|
other failures require explicit user action, with no unbounded retry loop. The added regression
|
||||||
passed with all 60 frontend tests, formatting and the production frontend build.
|
passed with all 60 frontend tests, formatting and the production frontend build.
|
||||||
|
|
||||||
This does not hash every recording on every request. A same-size/mtime in-place
|
This does not hash every recording on every request. A same-size/mtime in-place
|
||||||
@@ -53,7 +54,7 @@ for these protocol regressions.
|
|||||||
Ubuntu 24.04 WSL compiled the immutable `370db7e` Git snapshot with the locked
|
Ubuntu 24.04 WSL compiled the immutable `370db7e` Git snapshot with the locked
|
||||||
dependencies and `SEARCHI_FORCE_CPU=1`. All nine preview tests passed in 0.73 s;
|
dependencies and `SEARCHI_FORCE_CPU=1`. All nine preview tests passed in 0.73 s;
|
||||||
the complete native desktop suite then passed 45/45 in 2.78 s, with no skips.
|
the complete native desktop suite then passed 45/45 in 2.78 s, with no skips.
|
||||||
The native source tree remains identical to that snapshot; subsequent commits
|
At the time of validation the native source tree matched that snapshot; subsequent commits
|
||||||
changed frontend retry behavior only. Compilation took 8m 18s after missing
|
changed frontend retry behavior only. Compilation took 8m 18s after missing
|
||||||
locked dependencies were fetched. An initial offline attempt could not resolve
|
locked dependencies were fetched. An initial offline attempt could not resolve
|
||||||
the uncached Single-Instance plugin; it was not a product test failure.
|
the uncached Single-Instance plugin; it was not a product test failure.
|
||||||
@@ -69,3 +70,10 @@ The existing vendored GLib code emitted 34 compiler warnings. This was a debug
|
|||||||
Rust test build, not strict Linux Clippy, a packaged desktop launch, WebKit
|
Rust test build, not strict Linux Clippy, a packaged desktop launch, WebKit
|
||||||
playback, GPU inference or macOS validation. No model inference was executed.
|
playback, GPU inference or macOS validation. No model inference was executed.
|
||||||
Native custom-protocol playback and seeking remain open on all release systems.
|
Native custom-protocol playback and seeking remain open on all release systems.
|
||||||
|
|
||||||
|
A subsequent Windows CPU concurrency regression ran eight threads with 500
|
||||||
|
admission attempts each and checked that no more than two admitted jobs overlap.
|
||||||
|
It also injected a worker unwind and verified that capacity is released rather
|
||||||
|
than remaining busy. All 46 native tests passed in 1.04 s and strict all-target
|
||||||
|
Clippy passed. This added test was not included in the earlier Linux 45-test
|
||||||
|
snapshot; no additional production protocol behavior changed.
|
||||||
Reference in new issue
Block a user