use std::{hint::spin_loop, sync::Arc, thread::yield_now, time::Duration};
use compio::time::sleep;
use log::trace;
use wdev::Device;
use wepoch::{LightEpoch, current_thread_id};
use super::HybridLog;
use crate::error::{Error, Result};
const SPIN_BEFORE_YIELD: usize = 32;
const YIELD_BEFORE_SLEEP: usize = 64;
const BACKOFF_SLEEP_MICROS: u64 = 50;
#[inline]
async fn backoff(round: usize) {
if round < SPIN_BEFORE_YIELD {
spin_loop();
} else if round < YIELD_BEFORE_SLEEP {
yield_now();
} else {
sleep(Duration::from_micros(BACKOFF_SLEEP_MICROS)).await;
}
}
struct TlsResumeGuard<'a> {
epoch: &'a LightEpoch,
count: usize,
}
impl Drop for TlsResumeGuard<'_> {
#[inline]
fn drop(&mut self) {
for _ in 0..self.count {
self.epoch.resume();
}
}
}
impl<D: Device> HybridLog<D> {
pub fn shift_read_only_address(&self, new_ro: u64) {
let old_ro = self.addresses.shift_read_only_address(new_ro);
if new_ro > old_ro {
let addrs = Arc::clone(&self.addresses);
self.epoch.bump_current_epoch_action(move || {
addrs.shift_safe_read_only_address(new_ro);
trace!("Epoch 安全推进 SafeReadOnlyAddress 至 {new_ro:#x}");
});
if !self.epoch.this_instance_protected() {
self.epoch.bump_epoch();
}
}
}
pub fn shift_head_address(&self, new_head: u64) {
let new_head = new_head.min(self.addresses.flushed_until());
let old_head = self.addresses.shift_head_address(new_head);
if new_head > old_head {
let addrs = Arc::clone(&self.addresses);
self.epoch.bump_current_epoch_action(move || {
addrs.shift_safe_head_address(new_head);
trace!("Epoch 安全推进 SafeHeadAddress 至 {new_head:#x}");
});
if !self.epoch.this_instance_protected() {
self.epoch.bump_epoch();
}
}
}
pub async fn shift_begin_address(&self, new_begin: u64) -> Result<()> {
self.shift_read_only_address(new_begin);
let flushed = self.addresses.flushed_until();
if flushed < new_begin {
self.flush_addr_range(flushed, new_begin).await?;
}
if self.addresses.flushed_until() < new_begin {
return Err(Error::InvalidState(
"shift_begin_address 补刷后待截断区间仍未完整落盘(flushed_until < new_begin)".into(),
));
}
self.shift_head_address(new_begin);
self.addresses.shift_begin_address(new_begin);
self.wait_safe_read_only_drained(new_begin).await;
let dev = Arc::clone(&self.device);
dev
.truncate_until_address(new_begin)
.await
.map_err(Error::from)
}
async fn wait_safe_read_only_drained(&self, new_begin: u64) {
if self.addresses.safe_read_only() >= new_begin {
return;
}
let addrs = Arc::clone(&self.addresses);
self.epoch.bump_current_epoch_action(move || {
addrs.shift_safe_read_only_address(new_begin);
trace!("Epoch 封口推进 SafeReadOnlyAddress 至 {new_begin:#x}");
});
let exited = self.unpin_self();
let _guard = TlsResumeGuard {
epoch: &self.epoch,
count: exited,
};
let mut spins = 0usize;
while self.addresses.safe_read_only() < new_begin {
if spins.is_multiple_of(64) {
self.epoch.bump_epoch();
} else {
self.epoch.drain();
}
backoff(spins).await;
spins = spins.wrapping_add(1);
}
trace!("截断屏障排空完成:safe_read_only 已越过 {new_begin:#x}");
}
fn unpin_self(&self) -> usize {
let mut exited = 0usize;
while self.epoch.this_instance_protected() {
self.epoch.suspend();
exited += 1;
}
let tid = current_thread_id();
let current = self.epoch.current_epoch();
for entry in self.epoch.entries.iter() {
if entry.is_protected() && entry.thread_id() == tid {
entry.refresh_epoch(current);
}
}
exited
}
#[inline]
pub fn tail_address(&self) -> u64 {
self.addresses.tail()
}
#[inline]
pub fn shift_read_only_to_tail(&self) -> u64 {
let tail = self.addresses.tail();
self.shift_read_only_address(tail);
tail
}
#[inline]
pub fn read_only_address(&self) -> u64 {
self.addresses.read_only()
}
#[inline]
pub fn safe_read_only_address(&self) -> u64 {
self.addresses.safe_read_only()
}
#[inline]
pub fn head_address(&self) -> u64 {
self.addresses.head()
}
#[inline]
pub fn safe_head_address(&self) -> u64 {
self.addresses.safe_head()
}
#[inline]
pub fn flushed_until_address(&self) -> u64 {
self.addresses.flushed_until()
}
#[inline]
pub fn begin_address(&self) -> u64 {
self.addresses.begin()
}
#[inline]
pub fn is_mutable(&self, addr: u64) -> bool {
self.addresses.is_mutable(addr)
}
#[inline]
pub fn is_read_only(&self, addr: u64) -> bool {
self.addresses.is_read_only(addr)
}
#[inline]
pub fn is_in_memory(&self, addr: u64) -> bool {
self.addresses.is_in_memory(addr)
}
#[inline]
pub fn is_on_disk(&self, addr: u64) -> bool {
self.addresses.is_on_disk(addr)
}
}