1- use crate :: prelude:: * ;
1+ //! File locking abstractions to make directory locks easy and safe.
2+ //!
3+ //! Directory structures are typically represented by a struct containing the directory path,
4+ //! which then has methods for getting files or directories within it. This struct should implement
5+ //! `PathsAccess`, and then instead of passing it around directly, it should be stored in a
6+ //! [`DirectoryStructureLock`]. This ensures that paths cannot be accessed without holding the appropriate
7+ //! lock.
8+ //!
9+ //! Temporary locks can be acquired using the `with_read` and `with_write` methods, which take
10+ //! async closures. For locks that should be stored in a structure, `into_read` and `into_write` can be used
11+ //! to convert the lock into an owned guard.
12+
13+ use crate :: { fs, prelude:: * } ;
214use snafu:: { ResultExt , Snafu } ;
315use std:: { fs:: File , io, ops:: Deref } ;
416use tokio:: { sync:: RwLock , task:: spawn_blocking} ;
517
18+ /// Directory lock ensuring safe concurrency around filesystem operations.
619pub struct DirectoryStructureLock < T : PathsAccess > {
720 paths_access : T ,
821 lock_file : RwLock < File > ,
922 lock_path : PathBuf ,
1023}
1124
25+ /// A directory structure, typically with methods to access specific paths within it.
26+ ///
27+ /// One file within it is selected as the file for advisory locks.
1228pub trait PathsAccess : Send + Sync + ' static {
29+ /// Path to the canonical file for locking the directory structure. Usually `$dir/.lock`.
1330 fn lock_file ( & self ) -> PathBuf ;
1431}
1532
1633impl < T : PathsAccess > DirectoryStructureLock < T > {
34+ /// Creates a new lock, implicitly calling [`fs::create_dir_all`] on the parent.
1735 pub fn open_or_create ( paths_access : T ) -> Result < Self , LockError > {
1836 let lock_path = paths_access. lock_file ( ) ;
19- crate :: fs:: create_dir_all ( lock_path. parent ( ) . unwrap ( ) ) ?;
37+ fs:: create_dir_all ( lock_path. parent ( ) . unwrap ( ) ) ?;
2038 let lock_file =
2139 File :: create ( & lock_path) . context ( OpenLockFileFailedSnafu { path : & lock_path } ) ?;
2240 Ok ( Self {
@@ -26,7 +44,8 @@ impl<T: PathsAccess> DirectoryStructureLock<T> {
2644 } )
2745 }
2846
29- pub async fn read ( self ) -> Result < DirectoryStructureGuardOwned < T > , LockError > {
47+ /// Converts the lock structure into an owned read-lock.
48+ pub async fn into_read ( self ) -> Result < DirectoryStructureGuardOwned < T > , LockError > {
3049 spawn_blocking ( move || {
3150 let lock_file = self . lock_file . into_inner ( ) ;
3251 lock_file. lock_shared ( ) . context ( LockFailedSnafu {
@@ -41,7 +60,8 @@ impl<T: PathsAccess> DirectoryStructureLock<T> {
4160 . unwrap ( )
4261 }
4362
44- pub async fn write ( self ) -> Result < DirectoryStructureGuardOwned < T > , LockError > {
63+ /// Converts the lock structure into an owned write-lock.
64+ pub async fn into_write ( self ) -> Result < DirectoryStructureGuardOwned < T > , LockError > {
4565 spawn_blocking ( move || {
4666 let lock_file = self . lock_file . into_inner ( ) ;
4767 lock_file. lock ( ) . context ( LockFailedSnafu {
@@ -55,6 +75,8 @@ impl<T: PathsAccess> DirectoryStructureLock<T> {
5575 . await
5676 . unwrap ( )
5777 }
78+
79+ /// Accesses the directory structure under a read lock.
5880 pub async fn with_read < R > ( & self , f : impl AsyncFnOnce ( & T ) -> R ) -> Result < R , LockError > {
5981 let guard = self . lock_file . read ( ) . await ;
6082 let lock_file = guard. try_clone ( ) . context ( HandleCloneFailedSnafu {
@@ -73,6 +95,7 @@ impl<T: PathsAccess> DirectoryStructureLock<T> {
7395 Ok ( ret)
7496 }
7597
98+ /// Accesses the directory structure under a write lock.
7699 pub async fn with_write < R > ( & self , f : impl AsyncFnOnce ( & T ) -> R ) -> Result < R , LockError > {
77100 let guard = self . lock_file . write ( ) . await ;
78101 let lock_file = guard. try_clone ( ) . context ( HandleCloneFailedSnafu {
@@ -107,6 +130,7 @@ pub enum LockError {
107130 HandleCloneFailed { source : io:: Error , path : PathBuf } ,
108131}
109132
133+ /// File lock guard. Do not use as a temporary in an expression - if you are making a temporary lock, use `with_*`.
110134pub struct DirectoryStructureGuardOwned < T > {
111135 paths_access : T ,
112136 guard : File ,
0 commit comments