use std::cell::RefCell;
use std::sync::Arc;
use super::{Transaction, TxKey};
#[derive(Clone, Default)]
pub(crate) struct Joined {
pub(crate) keys: Vec<TxKey>,
pub(crate) direct: bool,
}
struct Frame {
tx: usize,
joined: Joined,
}
thread_local! {
static FRAMES: RefCell<Vec<Frame>> = const { RefCell::new(Vec::new()) };
}
struct Pop;
impl Drop for Pop {
fn drop(&mut self) {
FRAMES.with(|frames| frames.borrow_mut().pop());
}
}
fn id(tx: &Transaction) -> usize {
Arc::as_ptr(&tx.0) as usize
}
pub(super) fn joining<T>(tx: &Transaction, rebind: impl FnOnce() -> T) -> (T, Joined) {
FRAMES.with(|frames| {
frames.borrow_mut().push(Frame {
tx: id(tx),
joined: Joined::default(),
})
});
let pop = Pop;
let out = rebind();
let joined = FRAMES.with(|frames| {
frames
.borrow_mut()
.last_mut()
.map(|frame| std::mem::take(&mut frame.joined))
.unwrap_or_default()
});
drop(pop);
(out, joined)
}
pub(super) fn note_key(tx: &Transaction, key: TxKey) {
note(tx, |joined| {
if !joined.keys.contains(&key) {
joined.keys.push(key);
}
});
}
pub(super) fn note_joined(tx: &Transaction, from: &Joined) {
note(tx, |joined| {
for key in &from.keys {
if !joined.keys.contains(key) {
joined.keys.push(*key);
}
}
joined.direct |= from.direct;
});
}
pub(super) fn note_direct() {
FRAMES.with(|frames| {
if let Some(frame) = frames.borrow_mut().last_mut() {
frame.joined.direct = true;
}
});
}
fn note(tx: &Transaction, change: impl FnOnce(&mut Joined)) {
let tx = id(tx);
FRAMES.with(|frames| {
let mut frames = frames.borrow_mut();
if let Some(frame) = frames.iter_mut().rev().find(|frame| frame.tx == tx) {
change(&mut frame.joined);
}
});
}