fix(threading): make auto-reset regression deterministic
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"schema": 1,
|
"schema": 1,
|
||||||
"recorded_unix_seconds": 1786610180,
|
"recorded_unix_seconds": 1786611260,
|
||||||
"upstream_commit": "2aa70bb68513b39795da5d13c88f31b86e85a3ba",
|
"upstream_commit": "2aa70bb68513b39795da5d13c88f31b86e85a3ba",
|
||||||
"material_count": 27,
|
"material_count": 27,
|
||||||
"generated_output_count": 8,
|
"generated_output_count": 8,
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
"dependency_manifest_sha256": "5f80b45ab18a45365673b55b52d68318bc31f602ce2f6559f573027cc446c58e",
|
"dependency_manifest_sha256": "5f80b45ab18a45365673b55b52d68318bc31f602ce2f6559f573027cc446c58e",
|
||||||
"third_party_notices_sha256": "c4d381944a9b57277963d2a5ee4e23a9a064c2ba93a676a49e023931b53a71d6",
|
"third_party_notices_sha256": "c4d381944a9b57277963d2a5ee4e23a9a064c2ba93a676a49e023931b53a71d6",
|
||||||
"native_notices_sha256": "b417ee7bf6b748cc86839e5354753c0f8c1f8309d13c0fea379c77f602e69fc0",
|
"native_notices_sha256": "b417ee7bf6b748cc86839e5354753c0f8c1f8309d13c0fea379c77f602e69fc0",
|
||||||
"distribution_manifest_sha256": "9dc77128962f410e5ff7ecee947d2726e537cc42d9ef57a18d639c9bdcd2406e",
|
"distribution_manifest_sha256": "95af81f60a3823cac35e6716d1500d052bc7e22127d84f705798414a085f6762",
|
||||||
"unknown_materials": 0,
|
"unknown_materials": 0,
|
||||||
"unknown_bundled_assets": 0,
|
"unknown_bundled_assets": 0,
|
||||||
"status": "ok"
|
"status": "ok"
|
||||||
|
|||||||
@@ -913,34 +913,50 @@ impl WaitHandleAsyncFactory {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::{Barrier, mpsc};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_reset_releases_exactly_one_waiter_per_signal() {
|
fn auto_reset_releases_exactly_one_waiter_per_signal() {
|
||||||
|
for _ in 0..32 {
|
||||||
let event = Arc::new(ManagedAutoResetEvent::new_with_constructor().unwrap());
|
let event = Arc::new(ManagedAutoResetEvent::new_with_constructor().unwrap());
|
||||||
let first = Arc::clone(&event);
|
let ready = Arc::new(Barrier::new(3));
|
||||||
let second = Arc::clone(&event);
|
let (completed, completions) = mpsc::channel();
|
||||||
let first_done = Arc::new(AtomicBool::new(false));
|
let mut waiters = Vec::new();
|
||||||
let second_done = Arc::new(AtomicBool::new(false));
|
for id in 0..2 {
|
||||||
let first_flag = Arc::clone(&first_done);
|
let event = Arc::clone(&event);
|
||||||
let second_flag = Arc::clone(&second_done);
|
let ready = Arc::clone(&ready);
|
||||||
let a = std::thread::spawn(move || {
|
let completed = completed.clone();
|
||||||
first.wait_one_with_method().unwrap();
|
waiters.push(std::thread::spawn(move || {
|
||||||
first_flag.store(true, Ordering::SeqCst);
|
ready.wait();
|
||||||
});
|
event.wait_one_with_method().unwrap();
|
||||||
let b = std::thread::spawn(move || {
|
completed.send(id).unwrap();
|
||||||
second.wait_one_with_method().unwrap();
|
}));
|
||||||
second_flag.store(true, Ordering::SeqCst);
|
}
|
||||||
});
|
drop(completed);
|
||||||
std::thread::sleep(Duration::from_millis(20));
|
|
||||||
|
// Release both contenders together. The signal is deliberately
|
||||||
|
// allowed to race their actual wait calls: an auto-reset event
|
||||||
|
// must retain one signal even when no waiter has blocked yet.
|
||||||
|
ready.wait();
|
||||||
event.set().unwrap();
|
event.set().unwrap();
|
||||||
std::thread::sleep(Duration::from_millis(20));
|
let first = completions
|
||||||
assert_ne!(
|
.recv_timeout(Duration::from_secs(2))
|
||||||
first_done.load(Ordering::SeqCst),
|
.expect("one waiter must consume the first signal");
|
||||||
second_done.load(Ordering::SeqCst)
|
assert_eq!(
|
||||||
|
completions.recv_timeout(Duration::from_millis(10)),
|
||||||
|
Err(mpsc::RecvTimeoutError::Timeout),
|
||||||
|
"one auto-reset signal released both waiters"
|
||||||
);
|
);
|
||||||
|
|
||||||
event.set().unwrap();
|
event.set().unwrap();
|
||||||
a.join().unwrap();
|
let second = completions
|
||||||
b.join().unwrap();
|
.recv_timeout(Duration::from_secs(2))
|
||||||
|
.expect("the second waiter must consume the second signal");
|
||||||
|
assert_ne!(first, second);
|
||||||
|
for waiter in waiters {
|
||||||
|
waiter.join().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1547,8 +1547,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "crates/libremetaverse/src/threading_runtime.rs",
|
"path": "crates/libremetaverse/src/threading_runtime.rs",
|
||||||
"bytes": 30843,
|
"bytes": 31537,
|
||||||
"sha256": "2ab324af0ba240770e7fd0a6d12d3fc4e6972722d099d9e7914e11cac18bef0e"
|
"sha256": "f08e8055e56655e922b4806f13f003ea0cd3574ad9fb8bdac5d11e1697fc51ab"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "crates/libremetaverse/src/transfers.rs",
|
"path": "crates/libremetaverse/src/transfers.rs",
|
||||||
|
|||||||
Reference in New Issue
Block a user