about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorDrMeepster <19316085+DrMeepster@users.noreply.github.com>2022-10-18 00:48:40 -0700
committerRalf Jung <post@ralfj.de>2022-10-20 22:19:06 +0200
commit342251cb64dcd048c35e668a54c2f5f7ff1a3ad6 (patch)
treeaa746b3d1cb7c12e39af4bda58cc50c1f4f3ccb7 /src
parent3244c117f8b17c27203a625c5b9e3b281ea5debf (diff)
downloadrust-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.rs138
-rw-r--r--src/tools/miri/tests/pass/concurrency/windows_init_once.stdout6
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