mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-30 20:44:34 +00:00 
			
		
		
		
	 9e5c942286
			
		
	
	
		9e5c942286
		
	
	
	
	
		
			
			This allows these test modules to pass on builds/targets without unwinding support, where `panic = "abort"` - the ignored tests are for functionality that's not supported on those targets.
		
			
				
	
	
		
			167 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			167 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use std::sync::Once;
 | |
| use std::sync::atomic::AtomicBool;
 | |
| use std::sync::atomic::Ordering::Relaxed;
 | |
| use std::sync::mpsc::channel;
 | |
| use std::time::Duration;
 | |
| use std::{panic, thread};
 | |
| 
 | |
| #[test]
 | |
| fn smoke_once() {
 | |
|     static O: Once = Once::new();
 | |
|     let mut a = 0;
 | |
|     O.call_once(|| a += 1);
 | |
|     assert_eq!(a, 1);
 | |
|     O.call_once(|| a += 1);
 | |
|     assert_eq!(a, 1);
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn stampede_once() {
 | |
|     static O: Once = Once::new();
 | |
|     static mut RUN: bool = false;
 | |
| 
 | |
|     let (tx, rx) = channel();
 | |
|     for _ in 0..10 {
 | |
|         let tx = tx.clone();
 | |
|         thread::spawn(move || {
 | |
|             for _ in 0..4 {
 | |
|                 thread::yield_now()
 | |
|             }
 | |
|             unsafe {
 | |
|                 O.call_once(|| {
 | |
|                     assert!(!RUN);
 | |
|                     RUN = true;
 | |
|                 });
 | |
|                 assert!(RUN);
 | |
|             }
 | |
|             tx.send(()).unwrap();
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     unsafe {
 | |
|         O.call_once(|| {
 | |
|             assert!(!RUN);
 | |
|             RUN = true;
 | |
|         });
 | |
|         assert!(RUN);
 | |
|     }
 | |
| 
 | |
|     for _ in 0..10 {
 | |
|         rx.recv().unwrap();
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 | |
| fn poison_bad() {
 | |
|     static O: Once = Once::new();
 | |
| 
 | |
|     // poison the once
 | |
|     let t = panic::catch_unwind(|| {
 | |
|         O.call_once(|| panic!());
 | |
|     });
 | |
|     assert!(t.is_err());
 | |
| 
 | |
|     // poisoning propagates
 | |
|     let t = panic::catch_unwind(|| {
 | |
|         O.call_once(|| {});
 | |
|     });
 | |
|     assert!(t.is_err());
 | |
| 
 | |
|     // we can subvert poisoning, however
 | |
|     let mut called = false;
 | |
|     O.call_once_force(|p| {
 | |
|         called = true;
 | |
|         assert!(p.is_poisoned())
 | |
|     });
 | |
|     assert!(called);
 | |
| 
 | |
|     // once any success happens, we stop propagating the poison
 | |
|     O.call_once(|| {});
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 | |
| fn wait_for_force_to_finish() {
 | |
|     static O: Once = Once::new();
 | |
| 
 | |
|     // poison the once
 | |
|     let t = panic::catch_unwind(|| {
 | |
|         O.call_once(|| panic!());
 | |
|     });
 | |
|     assert!(t.is_err());
 | |
| 
 | |
|     // make sure someone's waiting inside the once via a force
 | |
|     let (tx1, rx1) = channel();
 | |
|     let (tx2, rx2) = channel();
 | |
|     let t1 = thread::spawn(move || {
 | |
|         O.call_once_force(|p| {
 | |
|             assert!(p.is_poisoned());
 | |
|             tx1.send(()).unwrap();
 | |
|             rx2.recv().unwrap();
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     rx1.recv().unwrap();
 | |
| 
 | |
|     // put another waiter on the once
 | |
|     let t2 = thread::spawn(|| {
 | |
|         let mut called = false;
 | |
|         O.call_once(|| {
 | |
|             called = true;
 | |
|         });
 | |
|         assert!(!called);
 | |
|     });
 | |
| 
 | |
|     tx2.send(()).unwrap();
 | |
| 
 | |
|     assert!(t1.join().is_ok());
 | |
|     assert!(t2.join().is_ok());
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn wait() {
 | |
|     for _ in 0..50 {
 | |
|         let val = AtomicBool::new(false);
 | |
|         let once = Once::new();
 | |
| 
 | |
|         thread::scope(|s| {
 | |
|             for _ in 0..4 {
 | |
|                 s.spawn(|| {
 | |
|                     once.wait();
 | |
|                     assert!(val.load(Relaxed));
 | |
|                 });
 | |
|             }
 | |
| 
 | |
|             once.call_once(|| val.store(true, Relaxed));
 | |
|         });
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 | |
| fn wait_on_poisoned() {
 | |
|     let once = Once::new();
 | |
| 
 | |
|     panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err();
 | |
|     panic::catch_unwind(|| once.wait()).unwrap_err();
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 | |
| fn wait_force_on_poisoned() {
 | |
|     let once = Once::new();
 | |
| 
 | |
|     thread::scope(|s| {
 | |
|         panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err();
 | |
| 
 | |
|         s.spawn(|| {
 | |
|             thread::sleep(Duration::from_millis(100));
 | |
| 
 | |
|             once.call_once_force(|_| {});
 | |
|         });
 | |
| 
 | |
|         once.wait_force();
 | |
|     })
 | |
| }
 |