use std::{
hint::spin_loop,
sync::{
Arc,
atomic::{
AtomicBool,
Ordering::{Acquire, Release},
},
},
thread::yield_now,
};
use compio::time::sleep;
use log::trace;
use wbase::{
backoff::{Backoff, BackoffStage, SLEEP_DURATION},
current_thread_id,
};
use wdev::Device;
use wepoch::LightEpoch;
use super::HybridLog;
use crate::error::{Error, Result};
#[inline]
async fn backoff(bo: &mut Backoff) {
match bo.stage() {
BackoffStage::Spin => spin_loop(),
BackoffStage::Yield => yield_now(),
BackoffStage::Sleep => sleep(SLEEP_DURATION).await,
}
bo.advance();
}
struct TlsResumeGuard<'a> {
epoch: &'a Arc<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<()> {
if new_begin <= self.addresses.begin() {
return Ok(());
}
let tail = self.addresses.tail();
if new_begin > tail {
return Err(Error::AddressOutOfRange {
addr: new_begin,
begin: self.addresses.begin(),
tail,
});
}
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_epoch_condition<F>(&self, mut condition: F)
where
F: FnMut() -> bool,
{
if condition() {
return;
}
let exited = self.unpin_self();
let _guard = TlsResumeGuard {
epoch: &self.epoch,
count: exited,
};
let mut backoff_state = Backoff::new();
while !condition() {
if backoff_state.step_count().is_multiple_of(64) {
let new_epoch = self.epoch.bump_epoch();
let tid = current_thread_id();
for entry in self.epoch.entries.iter() {
if entry.is_protected() && entry.thread_id() == tid {
entry.refresh_epoch(new_epoch);
}
}
} else {
self.epoch.drain();
}
backoff(&mut backoff_state).await;
}
}
async fn wait_safe_read_only_drained(&self, new_begin: u64) {
let drained = Arc::new(AtomicBool::new(false));
let drained_action = Arc::clone(&drained);
let addrs = Arc::clone(&self.addresses);
self.epoch.bump_current_epoch_action(move || {
addrs.shift_safe_read_only_address(new_begin);
addrs.shift_safe_head_address(new_begin);
drained_action.store(true, Release);
trace!("Epoch 封口推进 SafeHead/SafeReadOnly 至 {new_begin:#x}");
});
self
.wait_epoch_condition(|| drained.load(Acquire) && self.addresses.safe_head() >= new_begin)
.await;
trace!("截断屏障排空完成:safe_head 已越过 {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 safe_tail_address(&self) -> u64 {
self.addresses.safe_tail()
}
#[inline]
pub fn shift_read_only_to_tail(&self) -> u64 {
let tail = self.addresses.tail();
self.shift_read_only_address(tail);
tail
}
pub async fn shift_read_only_address_with_wait(&self, new_ro: u64, wait: bool) -> Result<()> {
let tail = self.addresses.tail();
let new_ro = new_ro.min(tail);
self.shift_read_only_address(new_ro);
if wait {
let flushed = self.addresses.flushed_until();
if flushed < new_ro {
self.flush_addr_range(flushed, new_ro).await?;
}
if self.addresses.flushed_until() < new_ro {
self.wait_flushed_until_address_async(new_ro).await;
}
if self.addresses.flushed_until() < new_ro {
return Err(Error::InvalidState(
"shift_read_only_address_with_wait 刷盘后目标区间仍未完整落盘".into(),
));
}
}
Ok(())
}
pub async fn wait_flushed_until_address_async(&self, target: u64) {
let mut bo = Backoff::new();
loop {
if self.addresses.flushed_until() >= target {
return;
}
match bo.stage() {
BackoffStage::Spin => spin_loop(),
BackoffStage::Yield => yield_now(),
BackoffStage::Sleep => {
let listener = self.flush_event.listen();
if self.addresses.flushed_until() >= target {
return;
}
listener.await;
}
}
bo.advance();
}
}
pub async fn shift_addresses_with_wait(
&self,
new_ro: u64,
new_head: u64,
wait_for_eviction: bool,
) -> Result<()> {
debug_assert!(
new_head <= new_ro,
"new_head ({new_head:#x}) 不能超过 new_ro ({new_ro:#x})"
);
let tail = self.addresses.tail();
let new_ro = new_ro.min(tail);
let new_head = new_head.min(new_ro);
self.shift_read_only_address_with_wait(new_ro, true).await?;
self.shift_head_address(new_head);
if wait_for_eviction {
self.wait_safe_head_drained(new_head).await;
}
Ok(())
}
pub async fn wait_safe_head_drained(&self, target_safe_head: u64) {
debug_assert!(
target_safe_head <= self.addresses.head(),
"target_safe_head ({target_safe_head:#x}) 不能超过 head ({:#x})",
self.addresses.head()
);
self
.wait_epoch_condition(|| self.addresses.safe_head() >= target_safe_head)
.await;
}
#[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)
}
}