diff options
| author | DrMeepster <19316085+DrMeepster@users.noreply.github.com> | 2022-10-18 00:48:40 -0700 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2022-10-20 22:19:06 +0200 |
| commit | 342251cb64dcd048c35e668a54c2f5f7ff1a3ad6 (patch) | |
| tree | aa746b3d1cb7c12e39af4bda58cc50c1f4f3ccb7 /src | |
| parent | 3244c117f8b17c27203a625c5b9e3b281ea5debf (diff) | |
| download | rust-342251cb64dcd048c35e668a54c2f5f7ff1a3ad6.tar.gz rust-342251cb64dcd048c35e668a54c2f5f7ff1a3ad6.zip | |
add test for init once
Diffstat (limited to 'src')
| -rw-r--r-- | src/tools/miri/tests/pass/concurrency/windows_init_once.rs | 138 | ||||
| -rw-r--r-- | src/tools/miri/tests/pass/concurrency/windows_init_once.stdout | 6 |
2 files changed, 144 insertions, 0 deletions
diff --git a/src/tools/miri/tests/pass/concurrency/windows_init_once.rs b/src/tools/miri/tests/pass/concurrency/windows_init_once.rs new file mode 100644 index 00000000000..d3c72c3d028 --- /dev/null +++ b/src/tools/miri/tests/pass/concurrency/windows_init_once.rs @@ -0,0 +1,138 @@ +//@only-target-windows: Uses win32 api functions +// We are making scheduler assumptions here. +//@compile-flags: -Zmiri-preemption-rate=0 + +use std::ffi::c_void; +use std::ptr::null_mut; +use std::thread; + +#[derive(Copy, Clone)] +struct SendPtr<T>(*mut T); + +unsafe impl<T> Send for SendPtr<T> {} + +extern "system" { + fn InitOnceBeginInitialize( + init: *mut *mut c_void, + flags: u32, + pending: *mut i32, + context: *mut c_void, + ) -> i32; + + fn InitOnceComplete(init: *mut *mut c_void, flags: u32, context: *mut c_void) -> i32; +} + +const TRUE: i32 = 1; +const FALSE: i32 = 0; + +const INIT_ONCE_INIT_FAILED: u32 = 4; + +fn single_thread() { + let mut init_once = null_mut(); + 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 = null_mut(); + + 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 = null_mut(); + 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 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 = null_mut(); + 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 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 main() { + single_thread(); + block_until_complete(); + retry_on_fail(); +} diff --git a/src/tools/miri/tests/pass/concurrency/windows_init_once.stdout b/src/tools/miri/tests/pass/concurrency/windows_init_once.stdout new file mode 100644 index 00000000000..f3d5aad8edc --- /dev/null +++ b/src/tools/miri/tests/pass/concurrency/windows_init_once.stdout @@ -0,0 +1,6 @@ +completing initialization +finished waiting for initialization +finished waiting for initialization +failing initialization +retrying initialization +finished waiting for initialization |
