#![cfg_attr(feature = "thread_local", feature(thread_local))]
#![cfg(not(miri))]
extern crate static_init;
use static_init::{constructor, destructor, dynamic, Finaly};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::thread::spawn;
struct A(i32);
impl A {
fn new(v: i32) -> Self {
for _ in 1..100000 {
std::hint::spin_loop();
}
A(v)
}
}
static FINALY_COUNT: AtomicUsize = AtomicUsize::new(0);
impl Finaly for A {
fn finaly(&self) {
FINALY_COUNT.fetch_add(1, Ordering::Relaxed);
}
}
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
impl Drop for A {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::Relaxed);
}
}
#[cfg(not(feature = "thread_local"))]
const FINALY_COUNT_EXPECTED: usize = 12;
#[cfg(not(feature = "thread_local"))]
const DROP_COUNT_EXPECTED: usize = 6;
#[cfg(feature = "thread_local")]
const FINALY_COUNT_EXPECTED: usize = 34;
#[cfg(feature = "thread_local")]
const DROP_COUNT_EXPECTED: usize = 28;
#[destructor(10)]
extern "C" fn test_d_counts() {
let c = FINALY_COUNT.load(Ordering::Relaxed);
if c != FINALY_COUNT_EXPECTED {
eprintln!("Wrong finaly count {}", c);
unsafe { libc::_exit(1) };
}
let c = DROP_COUNT.load(Ordering::Relaxed);
if c != DROP_COUNT_EXPECTED {
eprintln!("Wrong drop count {}", c);
unsafe { libc::_exit(1) };
}
}
macro_rules! make_test {
($name:ident,$acc:ident, $($att:ident)+ $(,$mut:ident)? $(=>$thread_local:ident)?) => {
#[test]
fn $name() {
#[dynamic($($att),*)]
$(#[$thread_local])?
static $($mut)? X0: A = A::new(42);
assert_eq!($acc!(X0),42);
#[dynamic($($att),*)]
$(#[$thread_local])?
static $($mut)? XPRE: A = A::new(42);
#[constructor(10)]
extern fn test_pre() {
assert_eq!($acc!(XPRE),42);
}
$(
use static_init::{Phased,Phase};
#[allow(unused)]
let $thread_local = 0;
eprintln!("tested");
assert!(!Phased::phase(&XPRE).intersects(Phase::INITIALIZED)
, "Assertions of this test are valid as long as the tests are not run in the same thread as the \
main thread. Please remove the `--test-threads=1` test option");
)?
assert_eq!($acc!(XPRE),42);
#[dynamic($($att),*)]
$(#[$thread_local])?
static $($mut)? XCONC: A = A::new(42);
static START: AtomicBool = AtomicBool::new(false);
static STARTED: AtomicUsize = AtomicUsize::new(0);
fn test_conc() {
STARTED.fetch_add(1,Ordering::Relaxed);
while START.compare_exchange_weak(true,true,Ordering::Relaxed,Ordering::Relaxed).is_err() {core::hint::spin_loop()}
assert_eq!($acc!(XCONC),42);
}
const NT: usize = 8;
let mut spawned = vec![];
for _ in 0..NT {
spawned.push(spawn(test_conc));
}
while STARTED.compare_exchange_weak(NT,NT,Ordering::Relaxed,Ordering::Relaxed).is_ok() {core::hint::spin_loop()}
START.store(true,Ordering::Relaxed);
spawned.into_iter().for_each(|t| {assert!(t.join().is_ok());});
}
}
}
macro_rules! acc0 {
($x:ident) => {
$x.0
};
}
macro_rules! accr {
($x:ident) => {
$x.read().0
};
}
make_test!(lazy, acc0, lazy);
make_test!(lesser_lazy, acc0, lazy);
make_test!(lazy_finalize, acc0, lazy finalize); make_test!(lesser_lazy_finalize, acc0, lesser_lazy finalize);
make_test!(mut_lazy, accr, lazy, mut);
make_test!(lesser_mut_lazy, accr, lazy, mut);
make_test!(mut_lazy_finalize, accr, lazy finalize, mut); make_test!(lesser_mut_lazy_finalize, accr, lesser_lazy finalize, mut); make_test!(mut_lazy_drop, accr, lazy drop, mut); make_test!(lesser_mut_lazy_drop, accr, lesser_lazy drop, mut);
#[cfg(feature = "thread_local")]
make_test!(thread_local_lazy, acc0, lazy => thread_local);
#[cfg(feature = "thread_local")]
make_test!(thread_local_lazy_finalize, acc0, lazy finalize => thread_local); #[cfg(feature = "thread_local")]
make_test!(thread_local_lazy_drop, acc0, lazy drop => thread_local);
#[cfg(feature = "thread_local")]
make_test!(thread_local_mut_lazy, accr, lazy, mut => thread_local);
#[cfg(feature = "thread_local")]
make_test!(thread_local_mut_lazy_finalize, accr, lazy finalize, mut => thread_local); #[cfg(feature = "thread_local")]
make_test!(thread_local_mut_lazy_drop, accr, lazy drop, mut => thread_local);