Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions crates/librqbit/src/storage/filesystem/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ impl TorrentStorage for FilesystemStorage {
}))
}

fn release_files(&self) -> anyhow::Result<()> {
for file in &self.opened_files {
file.close();
}

Ok(())
}

fn remove_directory_if_empty(&self, path: &Path) -> anyhow::Result<()> {
let path = self.output_folder.join(path);
if !path.is_dir() {
Expand Down
9 changes: 9 additions & 0 deletions crates/librqbit/src/storage/filesystem/opened_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,15 @@ impl OpenedFile {
})
}

pub fn close(&self) {
let mut g = self.file.write();
g.fd = None;
#[cfg(windows)]
{
g.tried_marking_sparse = false;
}
}

pub fn lock_read(&self) -> crate::Result<impl Deref<Target = File>> {
RwLockReadGuard::try_map(self.file.read(), |f| f.as_ref())
.ok()
Expand Down
9 changes: 9 additions & 0 deletions crates/librqbit/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ pub trait TorrentStorage: Send + Sync {
/// This is used to make the underlying object useless when e.g. pausing the torrent.
fn take(&self) -> anyhow::Result<Box<dyn TorrentStorage>>;

/// Release opened file handles while keeping storage paths and torrent state available.
fn release_files(&self) -> anyhow::Result<()> {
Ok(())
}

/// Callback called every time a piece has completed and has been validated.
/// Default implementation does nothing, but can be override in trait implementations.
fn on_piece_completed(&self, _piece_index: ValidPieceIndex) -> anyhow::Result<()> {
Expand Down Expand Up @@ -191,6 +196,10 @@ impl<U: TorrentStorage + ?Sized> TorrentStorage for Box<U> {
(**self).take()
}

fn release_files(&self) -> anyhow::Result<()> {
(**self).release_files()
}

fn remove_directory_if_empty(&self, path: &Path) -> anyhow::Result<()> {
(**self).remove_directory_if_empty(path)
}
Expand Down
27 changes: 26 additions & 1 deletion crates/librqbit/src/torrent_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,11 +391,30 @@ impl ManagedTorrent {
}

g.state = ManagedTorrentState::Paused(paused);
if start_paused
&& let ManagedTorrentState::Paused(paused) = &g.state
&& let Err(error) = paused.files.release_files()
{
warn!(
id=?t.shared.id,
info_hash=?t.shared.info_hash,
error=?error,
"error releasing files after paused initial check"
);
}
t.state_change_notify.notify_waiters();
_start(&t, peer_rx, start_paused, session, Some(g), token)
}
Err(err) => {
if init.is_pause_requested() {
if let Err(error) = init.files.release_files() {
warn!(
id=?init.shared.id,
info_hash=?init.shared.info_hash,
error=?error,
"error releasing files after paused initial check"
);
}
debug!("initial check paused");
t.state_change_notify.notify_waiters();
return Ok(());
Expand Down Expand Up @@ -477,6 +496,7 @@ impl ManagedTorrent {
match &g.state {
ManagedTorrentState::Live(live) => {
let paused = live.pause()?;
paused.files.release_files()?;
g.state = ManagedTorrentState::Paused(paused);
g.paused = true;
self.state_change_notify.notify_waiters();
Expand All @@ -490,7 +510,12 @@ impl ManagedTorrent {
Ok(())
}
ManagedTorrentState::Paused(_) => {
bail!("torrent is already paused");
let paused = g.state.take().assert_paused();
paused.files.release_files()?;
g.state = ManagedTorrentState::Paused(paused);
g.paused = true;
self.state_change_notify.notify_waiters();
Ok(())
}
ManagedTorrentState::Error(_) => {
bail!("can't pause torrent in error state")
Expand Down
Loading