Skip to content

Commit 97e8644

Browse files
committed
Move LockedFile into its own module
1 parent 60a811e commit 97e8644

2 files changed

Lines changed: 227 additions & 214 deletions

File tree

crates/uv-fs/src/lib.rs

Lines changed: 4 additions & 214 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
use std::borrow::Cow;
2-
use std::fmt::Display;
2+
use std::io;
33
use std::path::{Path, PathBuf};
44

55
use tempfile::NamedTempFile;
6-
use tracing::{debug, error, info, trace, warn};
6+
use tracing::warn;
77

8+
pub use crate::locked_file::*;
89
pub use crate::path::*;
910

1011
pub mod cachedir;
12+
mod locked_file;
1113
mod path;
1214
pub mod which;
1315

@@ -666,218 +668,6 @@ fn is_known_already_locked_error(err: &std::fs::TryLockError) -> bool {
666668
}
667669
}
668670

669-
/// A file lock that is automatically released when dropped.
670-
#[derive(Debug)]
671-
#[must_use]
672-
pub struct LockedFile(fs_err::File);
673-
674-
impl LockedFile {
675-
/// Inner implementation for [`LockedFile::acquire_blocking`] and [`LockedFile::acquire`].
676-
fn lock_file_blocking(file: fs_err::File, resource: &str) -> Result<Self, std::io::Error> {
677-
trace!(
678-
"Checking lock for `{resource}` at `{}`",
679-
file.path().user_display()
680-
);
681-
match file.file().try_lock() {
682-
Ok(()) => {
683-
debug!("Acquired lock for `{resource}`");
684-
Ok(Self(file))
685-
}
686-
Err(err) => {
687-
// Log error code and enum kind to help debugging more exotic failures.
688-
if !is_known_already_locked_error(&err) {
689-
debug!("Try lock error: {err:?}");
690-
}
691-
info!(
692-
"Waiting to acquire lock for `{resource}` at `{}`",
693-
file.path().user_display(),
694-
);
695-
file.lock()?;
696-
debug!("Acquired lock for `{resource}`");
697-
Ok(Self(file))
698-
}
699-
}
700-
}
701-
702-
/// Inner implementation for [`LockedFile::acquire_no_wait`].
703-
fn lock_file_no_wait(file: fs_err::File, resource: &str) -> Option<Self> {
704-
trace!(
705-
"Checking lock for `{resource}` at `{}`",
706-
file.path().user_display()
707-
);
708-
match file.try_lock() {
709-
Ok(()) => {
710-
debug!("Acquired lock for `{resource}`");
711-
Some(Self(file))
712-
}
713-
Err(err) => {
714-
// Log error code and enum kind to help debugging more exotic failures.
715-
if !is_known_already_locked_error(&err) {
716-
debug!("Try lock error: {err:?}");
717-
}
718-
debug!("Lock is busy for `{resource}`");
719-
None
720-
}
721-
}
722-
}
723-
724-
/// Inner implementation for [`LockedFile::acquire_shared_blocking`] and
725-
/// [`LockedFile::acquire_blocking`].
726-
fn lock_file_shared_blocking(
727-
file: fs_err::File,
728-
resource: &str,
729-
) -> Result<Self, std::io::Error> {
730-
trace!(
731-
"Checking shared lock for `{resource}` at `{}`",
732-
file.path().user_display()
733-
);
734-
match file.try_lock_shared() {
735-
Ok(()) => {
736-
debug!("Acquired shared lock for `{resource}`");
737-
Ok(Self(file))
738-
}
739-
Err(err) => {
740-
// Log error code and enum kind to help debugging more exotic failures.
741-
if !is_known_already_locked_error(&err) {
742-
debug!("Try lock error: {err:?}");
743-
}
744-
info!(
745-
"Waiting to acquire shared lock for `{resource}` at `{}`",
746-
file.path().user_display(),
747-
);
748-
file.lock_shared()?;
749-
debug!("Acquired shared lock for `{resource}`");
750-
Ok(Self(file))
751-
}
752-
}
753-
}
754-
755-
/// The same as [`LockedFile::acquire`], but for synchronous contexts.
756-
///
757-
/// Do not use from an async context, as this can block the runtime while waiting for another
758-
/// process to release the lock.
759-
pub fn acquire_blocking(
760-
path: impl AsRef<Path>,
761-
resource: impl Display,
762-
) -> Result<Self, std::io::Error> {
763-
let file = Self::create(path)?;
764-
let resource = resource.to_string();
765-
Self::lock_file_blocking(file, &resource)
766-
}
767-
768-
/// The same as [`LockedFile::acquire_blocking`], but for synchronous contexts.
769-
///
770-
/// Do not use from an async context, as this can block the runtime while waiting for another
771-
/// process to release the lock.
772-
pub fn acquire_shared_blocking(
773-
path: impl AsRef<Path>,
774-
resource: impl Display,
775-
) -> Result<Self, std::io::Error> {
776-
let file = Self::create(path)?;
777-
let resource = resource.to_string();
778-
Self::lock_file_shared_blocking(file, &resource)
779-
}
780-
781-
/// Acquire a cross-process lock for a resource using a file at the provided path.
782-
#[cfg(feature = "tokio")]
783-
pub async fn acquire(
784-
path: impl AsRef<Path>,
785-
resource: impl Display,
786-
) -> Result<Self, std::io::Error> {
787-
let file = Self::create(path)?;
788-
let resource = resource.to_string();
789-
tokio::task::spawn_blocking(move || Self::lock_file_blocking(file, &resource)).await?
790-
}
791-
792-
/// Acquire a cross-process read lock for a shared resource using a file at the provided path.
793-
#[cfg(feature = "tokio")]
794-
pub async fn acquire_shared(
795-
path: impl AsRef<Path>,
796-
resource: impl Display,
797-
) -> Result<Self, std::io::Error> {
798-
let file = Self::create(path)?;
799-
let resource = resource.to_string();
800-
tokio::task::spawn_blocking(move || Self::lock_file_shared_blocking(file, &resource))
801-
.await?
802-
}
803-
804-
/// Acquire a cross-process lock for a resource using a file at the provided path
805-
///
806-
/// Unlike [`LockedFile::acquire`] this function will not wait for the lock to become available.
807-
///
808-
/// If the lock is not immediately available, [`None`] is returned.
809-
pub fn acquire_no_wait(path: impl AsRef<Path>, resource: impl Display) -> Option<Self> {
810-
let file = Self::create(path).ok()?;
811-
let resource = resource.to_string();
812-
Self::lock_file_no_wait(file, &resource)
813-
}
814-
815-
#[cfg(unix)]
816-
fn create(path: impl AsRef<Path>) -> Result<fs_err::File, std::io::Error> {
817-
use std::os::unix::fs::PermissionsExt;
818-
819-
// If path already exists, return it.
820-
if let Ok(file) = fs_err::OpenOptions::new()
821-
.read(true)
822-
.write(true)
823-
.open(path.as_ref())
824-
{
825-
return Ok(file);
826-
}
827-
828-
// Otherwise, create a temporary file with 777 permissions. We must set
829-
// permissions _after_ creating the file, to override the `umask`.
830-
let file = if let Some(parent) = path.as_ref().parent() {
831-
NamedTempFile::new_in(parent)?
832-
} else {
833-
NamedTempFile::new()?
834-
};
835-
if let Err(err) = file
836-
.as_file()
837-
.set_permissions(std::fs::Permissions::from_mode(0o777))
838-
{
839-
warn!("Failed to set permissions on temporary file: {err}");
840-
}
841-
842-
// Try to move the file to path, but if path exists now, just open path
843-
match file.persist_noclobber(path.as_ref()) {
844-
Ok(file) => Ok(fs_err::File::from_parts(file, path.as_ref())),
845-
Err(err) => {
846-
if err.error.kind() == std::io::ErrorKind::AlreadyExists {
847-
fs_err::OpenOptions::new()
848-
.read(true)
849-
.write(true)
850-
.open(path.as_ref())
851-
} else {
852-
Err(err.error)
853-
}
854-
}
855-
}
856-
}
857-
858-
#[cfg(not(unix))]
859-
fn create(path: impl AsRef<Path>) -> std::io::Result<fs_err::File> {
860-
fs_err::OpenOptions::new()
861-
.read(true)
862-
.write(true)
863-
.create(true)
864-
.open(path.as_ref())
865-
}
866-
}
867-
868-
impl Drop for LockedFile {
869-
fn drop(&mut self) {
870-
if let Err(err) = self.0.unlock() {
871-
error!(
872-
"Failed to unlock resource at `{}`; program may be stuck: {err}",
873-
self.0.path().display()
874-
);
875-
} else {
876-
debug!("Released lock at `{}`", self.0.path().display());
877-
}
878-
}
879-
}
880-
881671
/// An asynchronous reader that reports progress as bytes are read.
882672
#[cfg(feature = "tokio")]
883673
pub struct ProgressReader<Reader: tokio::io::AsyncRead + Unpin, Callback: Fn(usize) + Unpin> {

0 commit comments

Comments
 (0)