Works fine now finally
This commit is contained in:
parent
7e180c05b3
commit
f0788f2c4a
4 changed files with 121 additions and 35 deletions
|
|
@ -194,6 +194,14 @@ impl ChunkTracker {
|
||||||
.filter_map(|id| self.lengths.validate_piece_index(id))
|
.filter_map(|id| self.lengths.validate_piece_index(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_piece_queued(&self, id: ValidPieceIndex) -> bool {
|
||||||
|
self.queue_pieces[id.get() as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_piece_have(&self, id: ValidPieceIndex) -> bool {
|
||||||
|
self.have[id.get() as usize]
|
||||||
|
}
|
||||||
|
|
||||||
// None if wrong chunk
|
// None if wrong chunk
|
||||||
// true if did something
|
// true if did something
|
||||||
// false if didn't do anything
|
// false if didn't do anything
|
||||||
|
|
|
||||||
|
|
@ -163,9 +163,6 @@ impl HttpApi {
|
||||||
headers: http::HeaderMap,
|
headers: http::HeaderMap,
|
||||||
) -> Result<impl IntoResponse> {
|
) -> Result<impl IntoResponse> {
|
||||||
let mut stream = state.api_stream(idx, file_id)?;
|
let mut stream = state.api_stream(idx, file_id)?;
|
||||||
|
|
||||||
dbg!(&headers);
|
|
||||||
|
|
||||||
let mut status = StatusCode::OK;
|
let mut status = StatusCode::OK;
|
||||||
let mut output_headers = HeaderMap::new();
|
let mut output_headers = HeaderMap::new();
|
||||||
output_headers.insert("Accept-Ranges", HeaderValue::from_static("bytes"));
|
output_headers.insert("Accept-Ranges", HeaderValue::from_static("bytes"));
|
||||||
|
|
|
||||||
|
|
@ -965,10 +965,18 @@ impl PeerHandler {
|
||||||
let n = {
|
let n = {
|
||||||
let mut n_opt = None;
|
let mut n_opt = None;
|
||||||
let bf = &live.bitfield;
|
let bf = &live.bitfield;
|
||||||
for n in g
|
let chunk_tracker = g.get_chunks()?;
|
||||||
.get_chunks()?
|
let priority_streamed_pieces = self
|
||||||
.iter_queued_pieces(&g.file_priorities, &self.state.files)
|
.state
|
||||||
{
|
.streams
|
||||||
|
.iter_next_pieces(&self.state.lengths)
|
||||||
|
.filter(|pid| {
|
||||||
|
!chunk_tracker.is_piece_have(*pid)
|
||||||
|
&& chunk_tracker.is_piece_queued(*pid)
|
||||||
|
});
|
||||||
|
let natural_order_pieces =
|
||||||
|
chunk_tracker.iter_queued_pieces(&g.file_priorities, &self.state.files);
|
||||||
|
for n in priority_streamed_pieces.chain(natural_order_pieces) {
|
||||||
if bf.get(n.get() as usize).map(|v| *v) == Some(true) {
|
if bf.get(n.get() as usize).map(|v| *v) == Some(true) {
|
||||||
n_opt = Some(n);
|
n_opt = Some(n);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ use std::{
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use librqbit_core::lengths::ValidPieceIndex;
|
use itertools::Itertools;
|
||||||
|
use librqbit_core::lengths::{Lengths, ValidPieceIndex};
|
||||||
use tokio::io::{AsyncRead, AsyncSeek};
|
use tokio::io::{AsyncRead, AsyncSeek};
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
|
|
@ -19,10 +20,21 @@ use super::ManagedTorrentHandle;
|
||||||
|
|
||||||
type StreamId = usize;
|
type StreamId = usize;
|
||||||
|
|
||||||
|
// Buffer either 1/10th of the file forward.
|
||||||
|
const PER_STREAM_BUF_PART: u64 = 10;
|
||||||
|
// Or 32 mb, whichever is larger
|
||||||
|
const PER_STREAM_BUF_MIN: u64 = 32 * 1024 * 1024;
|
||||||
|
|
||||||
|
struct StreamState {
|
||||||
|
current_piece: ValidPieceIndex,
|
||||||
|
file_len: u64,
|
||||||
|
waker: Option<Waker>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct TorrentStreams {
|
pub(crate) struct TorrentStreams {
|
||||||
next_stream_id: AtomicUsize,
|
next_stream_id: AtomicUsize,
|
||||||
wakers_by_stream: DashMap<StreamId, (ValidPieceIndex, Waker)>,
|
streams: DashMap<StreamId, StreamState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TorrentStreams {
|
impl TorrentStreams {
|
||||||
|
|
@ -30,31 +42,83 @@ impl TorrentStreams {
|
||||||
self.next_stream_id.fetch_add(1, Ordering::Relaxed)
|
self.next_stream_id.fetch_add(1, Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_waker(&self, stream_id: StreamId, piece_id: ValidPieceIndex, waker: Waker) {
|
fn register_waker(&self, stream_id: StreamId, waker: Waker) {
|
||||||
self.wakers_by_stream.insert(stream_id, (piece_id, waker));
|
if let Some(mut s) = self.streams.get_mut(&stream_id) {
|
||||||
|
let vm = s.value_mut();
|
||||||
|
vm.waker = Some(waker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue 1st, 2nd etc pieces from each stream in turn until they get 1/10th of the file .
|
||||||
|
pub(crate) fn iter_next_pieces(
|
||||||
|
&self,
|
||||||
|
lengths: &Lengths,
|
||||||
|
) -> impl Iterator<Item = ValidPieceIndex> {
|
||||||
|
let all = self
|
||||||
|
.streams
|
||||||
|
.iter()
|
||||||
|
.map(|s| {
|
||||||
|
let remaining = (s.value().file_len
|
||||||
|
+ lengths.piece_length(s.value().current_piece) as u64)
|
||||||
|
.div_ceil(PER_STREAM_BUF_PART)
|
||||||
|
.max(PER_STREAM_BUF_MIN);
|
||||||
|
(s.value().current_piece, remaining)
|
||||||
|
})
|
||||||
|
.map(Some)
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
struct It {
|
||||||
|
all: Vec<Option<(ValidPieceIndex, u64)>>,
|
||||||
|
lengths: Lengths,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for It {
|
||||||
|
type Item = ValidPieceIndex;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
for item in self.all.iter_mut() {
|
||||||
|
if let Some((p, remaining)) = item {
|
||||||
|
let y = *p;
|
||||||
|
let pl = self.lengths.piece_length(y);
|
||||||
|
*remaining = remaining.saturating_sub(pl as u64);
|
||||||
|
if *remaining == 0 {
|
||||||
|
*item = None;
|
||||||
|
} else if let Some(next_p) = self.lengths.validate_piece_index(y.get() + 1)
|
||||||
|
{
|
||||||
|
*item = Some((next_p, *remaining))
|
||||||
|
} else {
|
||||||
|
*item = None;
|
||||||
|
}
|
||||||
|
return Some(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
It {
|
||||||
|
all,
|
||||||
|
lengths: *lengths,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn wake_streams_on_piece_completed(&self, piece_id: ValidPieceIndex) {
|
pub(crate) fn wake_streams_on_piece_completed(&self, piece_id: ValidPieceIndex) {
|
||||||
let mut woken = Vec::new();
|
for mut w in self.streams.iter_mut() {
|
||||||
for w in self.wakers_by_stream.iter() {
|
if w.value().current_piece == piece_id {
|
||||||
if w.value().0 == piece_id {
|
if let Some(waker) = w.value_mut().waker.take() {
|
||||||
trace!(
|
trace!(
|
||||||
stream_id = *w.key(),
|
stream_id = *w.key(),
|
||||||
piece_id = piece_id.get(),
|
piece_id = piece_id.get(),
|
||||||
"waking stream"
|
"waking stream"
|
||||||
);
|
);
|
||||||
w.value().1.wake_by_ref();
|
waker.wake();
|
||||||
woken.push(*w.key());
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for w in woken {
|
|
||||||
self.wakers_by_stream.remove(&w);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop_stream(&self, stream_id: StreamId) {
|
fn drop_stream(&self, stream_id: StreamId) {
|
||||||
trace!(stream_id, "dropping stream");
|
trace!(stream_id, "dropping stream");
|
||||||
self.wakers_by_stream.remove(&stream_id);
|
self.streams.remove(&stream_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,8 +184,10 @@ impl AsyncRead for FileStream {
|
||||||
let piece_offset = abs_pos % dpl as u64;
|
let piece_offset = abs_pos % dpl as u64;
|
||||||
let piece_remaining = piece_len as u64 - piece_offset;
|
let piece_remaining = piece_len as u64 - piece_offset;
|
||||||
|
|
||||||
// queue N pieces after this if not yet
|
// queue N pieces after this if not yet. The "if let" should never fail.
|
||||||
// TODO
|
if let Some(mut s) = self.streams.streams.get_mut(&self.stream_id) {
|
||||||
|
s.value_mut().current_piece = piece_id;
|
||||||
|
}
|
||||||
|
|
||||||
// if the piece is not there, register to wake when it is
|
// if the piece is not there, register to wake when it is
|
||||||
// check if we have the piece for real
|
// check if we have the piece for real
|
||||||
|
|
@ -129,7 +195,7 @@ impl AsyncRead for FileStream {
|
||||||
let have = ct.get_have_pieces()[piece_id.get() as usize];
|
let have = ct.get_have_pieces()[piece_id.get() as usize];
|
||||||
if !have {
|
if !have {
|
||||||
self.streams
|
self.streams
|
||||||
.register_waker(self.stream_id, piece_id, cx.waker().clone());
|
.register_waker(self.stream_id, cx.waker().clone());
|
||||||
}
|
}
|
||||||
have
|
have
|
||||||
}));
|
}));
|
||||||
|
|
@ -179,10 +245,7 @@ impl AsyncSeek for FileStream {
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
let end_i64 = map_io_err!(TryInto::<i64>::try_into(self.file_len))?;
|
let end_i64 = map_io_err!(TryInto::<i64>::try_into(self.file_len))?;
|
||||||
let new_pos: i64 = match position {
|
let new_pos: i64 = match position {
|
||||||
SeekFrom::Start(s) => {
|
SeekFrom::Start(s) => map_io_err!(s.try_into())?,
|
||||||
self.as_mut().position = s;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
SeekFrom::End(e) => map_io_err!(TryInto::<i64>::try_into(self.file_len))? + e,
|
SeekFrom::End(e) => map_io_err!(TryInto::<i64>::try_into(self.file_len))? + e,
|
||||||
SeekFrom::Current(o) => map_io_err!(TryInto::<i64>::try_into(self.position))? + o,
|
SeekFrom::Current(o) => map_io_err!(TryInto::<i64>::try_into(self.position))? + o,
|
||||||
};
|
};
|
||||||
|
|
@ -240,16 +303,26 @@ impl ManagedTorrent {
|
||||||
let (fd_len, fd_offset) =
|
let (fd_len, fd_offset) =
|
||||||
self.with_opened_file(file_id, |fd| (fd.len, fd.offset_in_torrent))?;
|
self.with_opened_file(file_id, |fd| (fd.len, fd.offset_in_torrent))?;
|
||||||
let streams = self.streams()?;
|
let streams = self.streams()?;
|
||||||
Ok(FileStream {
|
let first_piece = self.info().lengths.validate_piece_index(0).context("bug")?;
|
||||||
|
let s = FileStream {
|
||||||
stream_id: streams.next_id(),
|
stream_id: streams.next_id(),
|
||||||
streams,
|
streams: streams.clone(),
|
||||||
file_id,
|
file_id,
|
||||||
position: 0,
|
position: 0,
|
||||||
|
|
||||||
file_len: fd_len,
|
file_len: fd_len,
|
||||||
file_torrent_abs_offset: fd_offset,
|
file_torrent_abs_offset: fd_offset,
|
||||||
torrent: self,
|
torrent: self,
|
||||||
})
|
};
|
||||||
|
streams.streams.insert(
|
||||||
|
s.stream_id,
|
||||||
|
StreamState {
|
||||||
|
current_piece: first_piece,
|
||||||
|
waker: None,
|
||||||
|
file_len: s.file_len,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Ok(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue