blob: 5e33617f98ad1790b34d5675a4958fd5b8b1643a [file] [log] [blame]
//@only-target-windows: Uses win32 api functions
// We are making scheduler assumptions here.
//@compile-flags: -Zmiri-preemption-rate=0
use std::ptr::null_mut;
use std::thread;
use windows_sys::Win32::Foundation::{FALSE, TRUE};
use windows_sys::Win32::System::Threading::{
InitOnceBeginInitialize, InitOnceComplete, INIT_ONCE, INIT_ONCE_INIT_FAILED,
};
// not in windows-sys
const INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { Ptr: null_mut() };
#[derive(Copy, Clone)]
struct SendPtr<T>(*mut T);
unsafe impl<T> Send for SendPtr<T> {}
fn single_thread() {
let mut init_once = INIT_ONCE_STATIC_INIT;
let mut pending = 0;
unsafe {
assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE);
assert_eq!(pending, TRUE);
assert_eq!(InitOnceComplete(&mut init_once, 0, null_mut()), TRUE);
assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE);
assert_eq!(pending, FALSE);
}
let mut init_once = INIT_ONCE_STATIC_INIT;
unsafe {
assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE);
assert_eq!(pending, TRUE);
assert_eq!(InitOnceComplete(&mut init_once, INIT_ONCE_INIT_FAILED, null_mut()), TRUE);
assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE);
assert_eq!(pending, TRUE);
}
}
fn block_until_complete() {
let mut init_once = INIT_ONCE_STATIC_INIT;
let mut pending = 0;
unsafe {
assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE);
assert_eq!(pending, TRUE);
}
let init_once_ptr = SendPtr(&mut init_once);
let waiter = move || unsafe {
let init_once_ptr = init_once_ptr; // avoid field capture
let mut pending = 0;
assert_eq!(InitOnceBeginInitialize(init_once_ptr.0, 0, &mut pending, null_mut()), TRUE);
assert_eq!(pending, FALSE);
println!("finished waiting for initialization");
};
let waiter1 = thread::spawn(waiter);
let waiter2 = thread::spawn(waiter);
// this yield ensures `waiter1` & `waiter2` are blocked on the main thread
thread::yield_now();
println!("completing initialization");
unsafe {
assert_eq!(InitOnceComplete(init_once_ptr.0, 0, null_mut()), TRUE);
}
waiter1.join().unwrap();
waiter2.join().unwrap();
}
fn retry_on_fail() {
let mut init_once = INIT_ONCE_STATIC_INIT;
let mut pending = 0;
unsafe {
assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE);
assert_eq!(pending, TRUE);
}
let init_once_ptr = SendPtr(&mut init_once);
let waiter = move || unsafe {
let init_once_ptr = init_once_ptr; // avoid field capture
let mut pending = 0;
assert_eq!(InitOnceBeginInitialize(init_once_ptr.0, 0, &mut pending, null_mut()), TRUE);
if pending == 1 {
println!("retrying initialization");
assert_eq!(InitOnceComplete(init_once_ptr.0, 0, null_mut()), TRUE);
} else {
println!("finished waiting for initialization");
}
};
let waiter1 = thread::spawn(waiter);
let waiter2 = thread::spawn(waiter);
// this yield ensures `waiter1` & `waiter2` are blocked on the main thread
thread::yield_now();
println!("failing initialization");
unsafe {
assert_eq!(InitOnceComplete(init_once_ptr.0, INIT_ONCE_INIT_FAILED, null_mut()), TRUE);
}
waiter1.join().unwrap();
waiter2.join().unwrap();
}
fn no_data_race_after_complete() {
let mut init_once = INIT_ONCE_STATIC_INIT;
let mut pending = 0;
unsafe {
assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE);
assert_eq!(pending, TRUE);
}
let init_once_ptr = SendPtr(&mut init_once);
let mut place = 0;
let place_ptr = SendPtr(&mut place);
let reader = thread::spawn(move || unsafe {
let init_once_ptr = init_once_ptr; // avoid field capture
let place_ptr = place_ptr; // avoid field capture
let mut pending = 0;
// this doesn't block because reader only executes after `InitOnceComplete` is called
assert_eq!(InitOnceBeginInitialize(init_once_ptr.0, 0, &mut pending, null_mut()), TRUE);
assert_eq!(pending, FALSE);
// this should not data race
place_ptr.0.read()
});
unsafe {
// this should not data race
place_ptr.0.write(1);
}
unsafe {
assert_eq!(InitOnceComplete(init_once_ptr.0, 0, null_mut()), TRUE);
}
// run reader (without preemption, it has not taken a step yet)
assert_eq!(reader.join().unwrap(), 1);
}
fn main() {
single_thread();
block_until_complete();
retry_on_fail();
no_data_race_after_complete();
}