use std::cell::RefCell;
use std::hash::Hash;
use std::sync::atomic::AtomicUsize as StdAtomicUsize;
use crate::sync_types::sync::atomic::{AtomicBool, Ordering};
use crate::sync_types::sync::Mutex;
use crate::sync_types::thread::{current as current_thread, park, Thread};
#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
pub(crate) struct Token(u128);
impl Token {
pub(crate) fn new() -> Self {
const THREAD_ID_OFFSET: usize = 64;
static THREAD_COUNTER: StdAtomicUsize = StdAtomicUsize::new(0);
thread_local! {
static THREAD_ID: u128 = THREAD_COUNTER.fetch_add(1, Ordering::Relaxed) as u128;
static COUNTER: RefCell<u128> = RefCell::new(0);
}
let thread_id = THREAD_ID.with(|thread_id| *thread_id);
let thread_local_counter = COUNTER.with(|counter| {
let mut counter = counter.borrow_mut();
*counter += 1;
*counter
});
let global_counter = thread_id << THREAD_ID_OFFSET | thread_local_counter;
Self(global_counter)
}
}
#[derive(Debug)]
pub struct BlockingWaitStrategy {
listeners: Mutex<Vec<(Token, Thread)>>,
is_empty: AtomicBool,
}
pub trait WaitStrategy {
fn wait(&self);
fn notify(&self);
fn register(&self, token: &Token);
fn unregister(&self, token: &Token);
}
impl BlockingWaitStrategy {
pub fn new() -> Self {
const INITIAL_LISTENER_CAPACITY: usize = 16;
Self {
listeners: Mutex::new(Vec::with_capacity(INITIAL_LISTENER_CAPACITY)),
is_empty: AtomicBool::new(true),
}
}
}
impl WaitStrategy for BlockingWaitStrategy {
fn wait(&self) {
park()
}
fn notify(&self) {
if !self.is_empty.load(Ordering::SeqCst) {
let mut listeners = self.listeners.lock().unwrap();
if !self.is_empty.load(Ordering::SeqCst) {
for (_, thread) in listeners.drain(..) {
thread.unpark();
}
self.is_empty.store(listeners.is_empty(), Ordering::SeqCst);
}
}
}
fn register(&self, token: &Token) {
let mut listeners = self.listeners.lock().unwrap();
listeners.push((*token, current_thread()));
self.is_empty.store(listeners.is_empty(), Ordering::SeqCst);
}
fn unregister(&self, token: &Token) {
let mut listeners = self.listeners.lock().unwrap();
listeners.retain(|(t, _)| *t != *token);
}
}