use std::{
cell::RefCell,
sync::{Arc, Weak},
};
use super::{
BufferPool, CachedBuf, NUM_CLASSES,
inbox::{ChainIter, CrossThreadInbox, SEALED},
};
pub(crate) struct TlsPoolEntry {
pub(crate) pool_id: u64,
pub(crate) tid: u64,
pub(crate) pool_weak: Weak<BufferPool>,
pub(crate) inbox: Arc<CrossThreadInbox>,
pub(crate) local: [Vec<CachedBuf>; NUM_CLASSES],
}
impl TlsPoolEntry {
fn new(pool: &Arc<BufferPool>) -> Self {
Self {
pool_id: pool.pool_id,
tid: current_thread_id(),
pool_weak: Arc::downgrade(pool),
inbox: Arc::new(CrossThreadInbox::new()),
local: [const { Vec::new() }; NUM_CLASSES],
}
}
fn sweep(&mut self, pool: Option<&BufferPool>, retire: bool) {
let tid = self.tid;
for cls in 0..NUM_CLASSES {
let chain = self.inbox.seal_and_drain(cls);
if !chain.is_null() && chain != SEALED {
for node in ChainIter::new(chain) {
Self::sweep_node(cls, node, pool, retire, tid);
}
}
for buf in self.local[cls].drain(..) {
Self::sweep_node(cls, buf, pool, retire, tid);
}
}
}
#[inline]
fn sweep_node(cls: usize, node: CachedBuf, pool: Option<&BufferPool>, retire: bool, tid: u64) {
let cap = node.cap;
if let Some(pool) = pool
&& retire
&& !pool.is_closed()
&& pool.depot.push(cls, node, tid)
{
return;
}
if let Some(pool) = pool {
pool.budget_for(cls).release(cap as i64);
}
}
pub(crate) fn drain_and_release(&mut self, pool: &BufferPool) {
self.sweep(Some(pool), false);
}
}
impl Drop for TlsPoolEntry {
fn drop(&mut self) {
let pool_opt = self.pool_weak.upgrade();
self.sweep(pool_opt.as_deref(), true);
}
}
pub(crate) struct TlsPoolManager {
pub(crate) fast: Option<TlsPoolEntry>,
pub(crate) others: Vec<TlsPoolEntry>,
}
impl TlsPoolManager {
const fn new() -> Self {
Self {
fast: None,
others: Vec::new(),
}
}
pub(crate) fn find(&self, pool_id: u64) -> Option<&TlsPoolEntry> {
if let Some(entry) = self.fast.as_ref()
&& entry.pool_id == pool_id
{
return Some(entry);
}
self.others.iter().find(|e| e.pool_id == pool_id)
}
pub(crate) fn find_mut(&mut self, pool_id: u64) -> Option<&mut TlsPoolEntry> {
if let Some(entry) = self.fast.as_mut()
&& entry.pool_id == pool_id
{
return Some(entry);
}
self.others.iter_mut().find(|e| e.pool_id == pool_id)
}
pub(crate) fn get_or_create<'a>(&'a mut self, pool: &Arc<BufferPool>) -> &'a mut TlsPoolEntry {
if self
.fast
.as_ref()
.is_some_and(|e| e.pool_id == pool.pool_id)
{
return unsafe { self.fast.as_mut().unwrap_unchecked() };
}
if let Some(mut entry) = self.fast.take() {
match entry.pool_weak.upgrade() {
Some(p) if p.is_closed() => entry.drain_and_release(&p),
Some(_) => self.others.push(entry),
None => {}
}
}
if let Some(idx) = self.others.iter().position(|e| e.pool_id == pool.pool_id) {
let entry = self.others.swap_remove(idx);
return self.fast.insert(entry);
}
self
.others
.retain_mut(|entry| match entry.pool_weak.upgrade() {
Some(p) if !p.is_closed() => true,
Some(p) => {
entry.drain_and_release(&p);
false
}
None => false,
});
self.fast.insert(TlsPoolEntry::new(pool))
}
}
thread_local! {
pub(crate) static TLS_POOLS: RefCell<TlsPoolManager> = const { RefCell::new(TlsPoolManager::new()) };
}
pub use crate::thread::current_thread_id;