use crate::tasks::{CbArgs, IOAction, IOEvent, IOEventMerged, TaskArgs};
use crossfire::{BlockingTxTrait, SendError};
use embed_seglist::SegList;
use io_buffer::Buffer;
use rustix::io::Errno;
use std::borrow::{Borrow, BorrowMut};
use std::marker::PhantomData;
use std::os::fd::RawFd;
struct MergedInfo<C: CbArgs> {
first_event: Box<IOEvent<C>>,
tail_offset: i64,
total_size: usize,
}
pub struct MergeBuffer<C: CbArgs> {
pub merge_size_limit: usize,
merged_info: Option<MergedInfo<C>>,
merged_events: SegList<IOEventMerged<C>>,
}
impl<C: CbArgs> MergeBuffer<C> {
#[inline(always)]
pub fn new(merge_size_limit: usize) -> Self {
Self { merge_size_limit, merged_info: None, merged_events: SegList::new() }
}
#[inline(always)]
pub fn may_add_event(&mut self, event: &IOEvent<C>) -> bool {
if let Some(ref info) = self.merged_info {
if event.get_size() as usize > self.merge_size_limit {
return false;
}
return info.tail_offset == event.offset;
} else {
return true;
}
}
#[inline(always)]
pub fn push_event(&mut self, event: IOEvent<C>) -> bool {
if let Some(ref mut info) = self.merged_info {
debug_assert_eq!(info.tail_offset, event.offset, "push_event: event not contiguous");
debug_assert!(
info.total_size + event.get_size() as usize <= self.merge_size_limit,
"push_event: exceeds merge_size_limit"
);
if self.merged_events.is_empty() {
let first_merged = info.first_event.extract_merged();
self.merged_events.push(first_merged);
}
info.total_size += event.get_size() as usize;
info.tail_offset += event.get_size() as i64;
self.merged_events.push(event.into_merged());
return info.total_size >= self.merge_size_limit;
} else {
let size = event.get_size() as usize;
let offset = event.offset;
self.merged_info = Some(MergedInfo {
first_event: Box::new(event),
tail_offset: offset + size as i64,
total_size: size,
});
return size >= self.merge_size_limit;
}
}
#[inline(always)]
pub fn len(&self) -> usize {
if !self.merged_events.is_empty() {
self.merged_events.len()
} else {
self.merged_info.as_ref().map(|_| 1).unwrap_or(0)
}
}
#[inline(always)]
fn take(
&mut self, action: IOAction,
) -> Result<Option<Box<IOEvent<C>>>, SegList<IOEventMerged<C>>> {
if let Some(info) = self.merged_info.take() {
if self.merged_events.is_empty() {
return Ok(Some(info.first_event));
}
let sub_tasks = std::mem::replace(&mut self.merged_events, SegList::new());
debug_assert!(sub_tasks.len() > 1);
let size = info.total_size;
match Buffer::aligned(size as i32) {
Ok(mut buffer) => {
if action == IOAction::Write {
let mut write_offset = 0;
for merged in sub_tasks.iter() {
buffer.copy_from(write_offset, merged.buf.as_ref());
write_offset += merged.buf.len();
}
}
let mut master = info.first_event;
master.set_merged_tasks(buffer, sub_tasks);
Ok(Some(master))
}
Err(_) => Err(sub_tasks),
}
} else {
Ok(None)
}
}
#[inline]
pub fn flush<F, B>(
&mut self, fd: RawFd, action: IOAction, on_failure: B,
) -> Option<Box<IOEvent<C>>>
where
B: Borrow<F>,
F: Fn(C, Errno),
{
match self.take(action) {
Ok(Some(mut event)) => {
event.set_fd(fd);
Some(event)
}
Ok(None) => None,
Err(sub_tasks) => {
for merged in sub_tasks {
if let Some(args) = merged.args {
(on_failure.borrow())(args, Errno::NOMEM);
}
}
None
}
}
}
}
pub struct MergeSubmitter<
C: CbArgs,
S: BlockingTxTrait<Box<IOEvent<C>>>,
B: BorrowMut<MergeBuffer<C>>,
F: Fn(C, Errno),
> {
fd: RawFd,
buffer: B,
sender: S,
action: IOAction,
on_failure: F,
_phan: PhantomData<fn(&C)>,
}
impl<C, S, F> MergeSubmitter<C, S, MergeBuffer<C>, F>
where
C: CbArgs,
S: BlockingTxTrait<Box<IOEvent<C>>>,
F: Fn(C, Errno),
{
#[inline]
pub fn new(
fd: RawFd, sender: S, merge_size_limit: usize, action: IOAction, on_failure: F,
) -> Self {
log_assert!(merge_size_limit > 0);
Self {
fd,
sender,
action,
buffer: MergeBuffer::<C>::new(merge_size_limit),
on_failure,
_phan: Default::default(),
}
}
}
impl<C, S, B, F> MergeSubmitter<C, S, B, F>
where
C: CbArgs,
S: BlockingTxTrait<Box<IOEvent<C>>>,
B: BorrowMut<MergeBuffer<C>>,
F: Fn(C, Errno),
{
#[inline]
pub fn with_buffer(fd: RawFd, sender: S, action: IOAction, buffer: B, on_failure: F) -> Self {
Self { fd, sender, action, buffer, _phan: Default::default(), on_failure }
}
#[inline]
pub fn add_event(&mut self, event: IOEvent<C>) -> Result<(), Errno> {
log_debug_assert_eq!(self.fd, event.fd);
log_debug_assert_eq!(event.action, self.action);
let event_size = event.get_size();
let buffer = self.buffer.borrow_mut();
if event_size >= buffer.merge_size_limit as u64 || !buffer.may_add_event(&event) {
if let Err(e) = self._flush() {
if let Some(TaskArgs::Callback(args)) = event.args {
(self.on_failure)(args, e);
}
return Err(e);
}
}
if self.buffer.borrow_mut().push_event(event) {
self._flush()?;
}
return Ok(());
}
#[inline]
pub fn flush(&mut self) -> Result<(), Errno> {
self._flush()
}
#[inline(always)]
fn _flush(&mut self) -> Result<(), Errno> {
if let Some(event) =
self.buffer.borrow_mut().flush::<F, &F>(self.fd, self.action, &self.on_failure)
{
trace!("mio: submit event from flush {:?}", event);
if let Err(SendError(fail_event)) = self.sender.send(event) {
let e = Errno::SHUTDOWN;
if let Some(TaskArgs::Callback(args)) = fail_event.args {
(self.on_failure)(args, e);
}
return Err(e);
}
}
Ok(())
}
}