Move disk writes to a queue

This commit is contained in:
Igor Katson 2024-05-01 13:16:04 +01:00
parent ce7c7a3f89
commit b8bbe6a87f
3 changed files with 49 additions and 39 deletions

View file

@ -1382,23 +1382,22 @@ impl PeerHandler {
// By this time we reach here, no other peer can for this piece. All others, even if they steal pieces would // By this time we reach here, no other peer can for this piece. All others, even if they steal pieces would
// have fallen off above in one of the defensive checks. // have fallen off above in one of the defensive checks.
self.state
.meta let work = {
.spawner let state = self.state.clone();
.spawn_block_in_place(move || { let addr = self.addr;
let counters = self.counters.clone();
let piece = piece.clone_to_owned();
move || {
let index = piece.index; let index = piece.index;
// Not being able to write to storage is a fatal error. You need to unpause the // Not being able to write to storage is a fatal error. You need to unpause the
// torrent to recover from it. // torrent to recover from it.
match self match state.file_ops().write_chunk(addr, &piece, &chunk_info) {
.state
.file_ops()
.write_chunk(self.addr, &piece, &chunk_info)
{
Ok(()) => {} Ok(()) => {}
Err(e) => { Err(e) => {
error!("FATAL: error writing chunk to disk: {:?}", e); error!("FATAL: error writing chunk to disk: {:?}", e);
return self.state.on_fatal_error(e); return state.on_fatal_error(e);
} }
} }
@ -1407,61 +1406,58 @@ impl PeerHandler {
None => return Ok(()), None => return Ok(()),
}; };
match self match state
.state
.file_ops() .file_ops()
.check_piece(self.addr, chunk_info.piece_index, &chunk_info) .check_piece(addr, chunk_info.piece_index, &chunk_info)
.with_context(|| format!("error checking piece={index}"))? .with_context(|| format!("error checking piece={index}"))?
{ {
true => { true => {
{ {
let mut g = self.state.lock_write("mark_piece_downloaded"); let mut g = state.lock_write("mark_piece_downloaded");
g.get_chunks_mut()? g.get_chunks_mut()?
.mark_piece_downloaded(chunk_info.piece_index); .mark_piece_downloaded(chunk_info.piece_index);
} }
// Global piece counters. // Global piece counters.
let piece_len = let piece_len = state.lengths.piece_length(chunk_info.piece_index) as u64;
self.state.lengths.piece_length(chunk_info.piece_index) as u64; state
self.state
.stats .stats
.downloaded_and_checked_bytes .downloaded_and_checked_bytes
// This counter is used to compute "is_finished", so using // This counter is used to compute "is_finished", so using
// stronger ordering. // stronger ordering.
.fetch_add(piece_len, Ordering::Release); .fetch_add(piece_len, Ordering::Release);
self.state state
.stats .stats
.downloaded_and_checked_pieces .downloaded_and_checked_pieces
// This counter is used to compute "is_finished", so using // This counter is used to compute "is_finished", so using
// stronger ordering. // stronger ordering.
.fetch_add(1, Ordering::Release); .fetch_add(1, Ordering::Release);
self.state state
.stats .stats
.have_bytes .have_bytes
.fetch_add(piece_len, Ordering::Relaxed); .fetch_add(piece_len, Ordering::Relaxed);
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
self.state.stats.total_piece_download_ms.fetch_add( state.stats.total_piece_download_ms.fetch_add(
full_piece_download_time.as_millis() as u64, full_piece_download_time.as_millis() as u64,
Ordering::Relaxed, Ordering::Relaxed,
); );
// Per-peer piece counters. // Per-peer piece counters.
self.counters counters.on_piece_completed(piece_len, full_piece_download_time);
.on_piece_downloaded(piece_len, full_piece_download_time); state.peers.reset_peer_backoff(addr);
self.state.peers.reset_peer_backoff(self.addr);
debug!("piece={} successfully downloaded and verified", index); debug!("piece={} successfully downloaded and verified", index);
self.state.on_piece_completed(chunk_info.piece_index)?; state.on_piece_completed(chunk_info.piece_index)?;
self.state.maybe_transmit_haves(chunk_info.piece_index); state.maybe_transmit_haves(chunk_info.piece_index);
} }
false => { false => {
warn!( warn!(
"checksum for piece={} did not validate. disconecting peer.", "checksum for piece={} did not validate. disconecting peer.",
index index
); );
self.state state
.lock_write("mark_piece_broken") .lock_write("mark_piece_broken")
.get_chunks_mut()? .get_chunks_mut()?
.mark_piece_broken_if_not_have(chunk_info.piece_index); .mark_piece_broken_if_not_have(chunk_info.piece_index);
@ -1469,8 +1465,10 @@ impl PeerHandler {
} }
}; };
Ok::<_, anyhow::Error>(()) Ok::<_, anyhow::Error>(())
}) }
.with_context(|| format!("error processing received chunk {chunk_info:?}"))?; };
tokio::runtime::Handle::current().spawn_blocking(work);
Ok(()) Ok(())
} }
} }

View file

@ -25,7 +25,7 @@ pub(crate) struct PeerCountersAtomic {
} }
impl PeerCountersAtomic { impl PeerCountersAtomic {
pub(crate) fn on_piece_downloaded(&self, piece_len: u64, elapsed: Duration) { pub(crate) fn on_piece_completed(&self, piece_len: u64, elapsed: Duration) {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
let elapsed = elapsed.as_millis() as u64; let elapsed = elapsed.as_millis() as u64;
self.total_piece_download_ms self.total_piece_download_ms

View file

@ -75,24 +75,36 @@ pub fn serialize_piece_preamble(chunk: &ChunkInfo, mut buf: &mut [u8]) -> usize
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Piece<ByteBuf> { pub struct Piece<B> {
pub index: u32, pub index: u32,
pub begin: u32, pub begin: u32,
pub block: ByteBuf, pub block: B,
} }
impl<ByteBuf> Piece<ByteBuf> impl<B: CloneToOwned> CloneToOwned for Piece<B> {
type Target = Piece<B::Target>;
fn clone_to_owned(&self) -> Self::Target {
Piece {
index: self.index,
begin: self.begin,
block: self.block.clone_to_owned(),
}
}
}
impl<B> Piece<B>
where where
ByteBuf: AsRef<[u8]>, B: AsRef<[u8]>,
{ {
pub fn from_data<T>(index: u32, begin: u32, block: T) -> Piece<ByteBuf> pub fn from_data<T>(index: u32, begin: u32, block: T) -> Piece<B>
where where
ByteBuf: From<T>, B: From<T>,
{ {
Piece { Piece {
index, index,
begin, begin,
block: ByteBuf::from(block), block: B::from(block),
} }
} }
@ -103,13 +115,13 @@ where
buf.copy_from_slice(self.block.as_ref()); buf.copy_from_slice(self.block.as_ref());
self.block.as_ref().len() + 8 self.block.as_ref().len() + 8
} }
pub fn deserialize<'a>(buf: &'a [u8]) -> Piece<ByteBuf> pub fn deserialize<'a>(buf: &'a [u8]) -> Piece<B>
where where
ByteBuf: From<&'a [u8]> + 'a, B: From<&'a [u8]> + 'a,
{ {
let index = byteorder::BigEndian::read_u32(&buf[0..4]); let index = byteorder::BigEndian::read_u32(&buf[0..4]);
let begin = byteorder::BigEndian::read_u32(&buf[4..8]); let begin = byteorder::BigEndian::read_u32(&buf[4..8]);
let block = ByteBuf::from(&buf[8..]); let block = B::from(&buf[8..]);
Piece { Piece {
index, index,
begin, begin,