use std::{cell::Cell, num::NonZeroU16, sync::atomic};
use index_type::{IndexType, array::TypedArray};
use crate::{
atomic_type::Atomic,
thread_state::{EncodedThreadState, ThreadState},
};
const MAX_CONCURRENT_THREADS: usize = 4096;
#[derive(IndexType, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct ThreadStorageSlotId(
pub NonZeroU16,
);
#[derive(Debug)]
pub struct ThreadStorageSlotValue {
pub state: Atomic<EncodedThreadState>,
}
static THREAD_STORAGE_SLOTS: TypedArray<
ThreadStorageSlotId,
ThreadStorageSlotValue,
MAX_CONCURRENT_THREADS,
> = TypedArray::from_array(
[const {
ThreadStorageSlotValue {
state: Atomic::<EncodedThreadState>::new(0),
}
}; MAX_CONCURRENT_THREADS],
);
pub fn thread_storage_slot_get_all()
-> impl Iterator<Item = (ThreadStorageSlotId, &'static ThreadStorageSlotValue)> {
THREAD_STORAGE_SLOTS.iter_enumerated()
}
pub fn thread_storage_slot_get(id: ThreadStorageSlotId) -> &'static ThreadStorageSlotValue {
&THREAD_STORAGE_SLOTS[id]
}
pub fn thread_storage_slot_alloc(initial_thread_state: ThreadState) -> Option<ThreadStorageSlotId> {
let encoded_initial_thread_state = initial_thread_state.encode();
for (slot_id, slot) in THREAD_STORAGE_SLOTS.iter_enumerated() {
if slot.state.load(atomic::Ordering::Relaxed) == ThreadState::NONE_ENCODED_VALUE {
match slot.state.compare_exchange(
ThreadState::NONE_ENCODED_VALUE,
encoded_initial_thread_state,
atomic::Ordering::Acquire,
atomic::Ordering::Relaxed,
) {
Ok(_) => {
return Some(slot_id);
}
Err(_) => {
}
}
}
}
None
}
pub fn thread_storage_slot_free(id: ThreadStorageSlotId) {
THREAD_STORAGE_SLOTS[id].state.store(
ThreadState::NONE_ENCODED_VALUE,
atomic::Ordering::Release,
);
}
pub struct OwnedThreadStorageSlot {
id: Cell<Option<ThreadStorageSlotId>>,
}
impl OwnedThreadStorageSlot {
pub const fn unallocated() -> Self {
Self {
id: Cell::new(None),
}
}
pub fn alloc(&self, initial_thread_state: ThreadState) {
if self.id.get().is_some() {
return;
}
let id = thread_storage_slot_alloc(initial_thread_state).expect(
"too many concurrent threads, failed to allocate a storage slot for a new thread",
);
self.id.set(Some(id));
}
pub fn dealloc(&self) {
let Some(id) = self.id.get() else { return };
thread_storage_slot_free(id);
self.id.set(None);
}
pub fn id(&self) -> Option<ThreadStorageSlotId> {
self.id.get()
}
}
impl Drop for OwnedThreadStorageSlot {
fn drop(&mut self) {
self.dealloc();
}
}
thread_local! {
static THREAD_STORAGE_SLOT: OwnedThreadStorageSlot = const { OwnedThreadStorageSlot::unallocated() };
}
pub fn this_thread_get_storage_slot_id() -> ThreadStorageSlotId {
THREAD_STORAGE_SLOT.with(|storage_slot| storage_slot.id().unwrap())
}
pub fn this_thread_get_storage_slot() -> &'static ThreadStorageSlotValue {
THREAD_STORAGE_SLOT.with(|storage_slot| thread_storage_slot_get(storage_slot.id().unwrap()))
}
pub fn this_thread_does_have_allocated_storage_slot() -> bool {
THREAD_STORAGE_SLOT.with(|storage_slot| storage_slot.id.get().is_some())
}
pub fn this_thread_alloc_storage_slot(initial_thread_state: ThreadState) {
THREAD_STORAGE_SLOT.with(|storage_slot| storage_slot.alloc(initial_thread_state))
}
pub fn this_thread_dealloc_storage_slot() {
THREAD_STORAGE_SLOT.with(|storage_slot| storage_slot.dealloc())
}