Implement file_lock feature
This adds lock(), lock_shared(), try_lock(), try_lock_shared(), and unlock() to File gated behind the file_lock feature flag
This commit is contained in:
parent
a1fd235180
commit
d2cdc76256
10 changed files with 511 additions and 0 deletions
|
|
@ -615,6 +615,223 @@ impl File {
|
|||
self.inner.datasync()
|
||||
}
|
||||
|
||||
/// Acquire an exclusive advisory lock on the file. Blocks until the lock can be acquired.
|
||||
///
|
||||
/// This acquires an exclusive advisory lock; no other file handle to this file may acquire
|
||||
/// another lock.
|
||||
///
|
||||
/// If this file handle, or a clone of it, already holds an advisory lock the exact behavior is
|
||||
/// unspecified and platform dependent, including the possibility that it will deadlock.
|
||||
/// However, if this method returns, then an exclusive lock is held.
|
||||
///
|
||||
/// If the file not open for writing, it is unspecified whether this function returns an error.
|
||||
///
|
||||
/// Note, this is an advisory lock meant to interact with [`lock_shared`], [`try_lock`],
|
||||
/// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`]
|
||||
/// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag,
|
||||
/// and the `LockFileEx` function on Windows with the `LOCKFILE_EXCLUSIVE_LOCK` flag. Note that,
|
||||
/// this [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: io#platform-specific-behavior
|
||||
///
|
||||
/// [`lock_shared`]: File::lock_shared
|
||||
/// [`try_lock`]: File::try_lock
|
||||
/// [`try_lock_shared`]: File::try_lock_shared
|
||||
/// [`unlock`]: File::unlock
|
||||
/// [`read`]: Read::read
|
||||
/// [`write`]: Write::write
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(file_lock)]
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("foo.txt")?;
|
||||
/// f.lock()?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "file_lock", issue = "130994")]
|
||||
pub fn lock(&self) -> io::Result<()> {
|
||||
self.inner.lock()
|
||||
}
|
||||
|
||||
/// Acquire a shared advisory lock on the file. Blocks until the lock can be acquired.
|
||||
///
|
||||
/// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but
|
||||
/// none may hold an exclusive lock.
|
||||
///
|
||||
/// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is
|
||||
/// unspecified and platform dependent, including the possibility that it will deadlock.
|
||||
/// However, if this method returns, then a shared lock is held.
|
||||
///
|
||||
/// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`],
|
||||
/// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`]
|
||||
/// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` flag,
|
||||
/// and the `LockFileEx` function on Windows. Note that, this
|
||||
/// [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: io#platform-specific-behavior
|
||||
///
|
||||
/// [`lock`]: File::lock
|
||||
/// [`try_lock`]: File::try_lock
|
||||
/// [`try_lock_shared`]: File::try_lock_shared
|
||||
/// [`unlock`]: File::unlock
|
||||
/// [`read`]: Read::read
|
||||
/// [`write`]: Write::write
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(file_lock)]
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("foo.txt")?;
|
||||
/// f.lock_shared()?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "file_lock", issue = "130994")]
|
||||
pub fn lock_shared(&self) -> io::Result<()> {
|
||||
self.inner.lock_shared()
|
||||
}
|
||||
|
||||
/// Acquire an exclusive advisory lock on the file. Returns `Ok(false)` if the file is locked.
|
||||
///
|
||||
/// This acquires an exclusive advisory lock; no other file handle to this file may acquire
|
||||
/// another lock.
|
||||
///
|
||||
/// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is
|
||||
/// unspecified and platform dependent, including the possibility that it will deadlock.
|
||||
/// However, if this method returns, then an exclusive lock is held.
|
||||
///
|
||||
/// If the file not open for writing, it is unspecified whether this function returns an error.
|
||||
///
|
||||
/// Note, this is an advisory lock meant to interact with [`lock`], [`lock_shared`],
|
||||
/// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`]
|
||||
/// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` and
|
||||
/// `LOCK_NB` flags, and the `LockFileEx` function on Windows with the `LOCKFILE_EXCLUSIVE_LOCK`
|
||||
/// and `LOCKFILE_FAIL_IMMEDIATELY` flags. Note that, this
|
||||
/// [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: io#platform-specific-behavior
|
||||
///
|
||||
/// [`lock`]: File::lock
|
||||
/// [`lock_shared`]: File::lock_shared
|
||||
/// [`try_lock_shared`]: File::try_lock_shared
|
||||
/// [`unlock`]: File::unlock
|
||||
/// [`read`]: Read::read
|
||||
/// [`write`]: Write::write
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(file_lock)]
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("foo.txt")?;
|
||||
/// f.try_lock()?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "file_lock", issue = "130994")]
|
||||
pub fn try_lock(&self) -> io::Result<bool> {
|
||||
self.inner.try_lock()
|
||||
}
|
||||
|
||||
/// Acquire a shared advisory lock on the file.
|
||||
/// Returns `Ok(false)` if the file is exclusively locked.
|
||||
///
|
||||
/// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but
|
||||
/// none may hold an exclusive lock.
|
||||
///
|
||||
/// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is
|
||||
/// unspecified and platform dependent, including the possibility that it will deadlock.
|
||||
/// However, if this method returns, then a shared lock is held.
|
||||
///
|
||||
/// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`],
|
||||
/// [`try_lock`], and [`unlock`]. Its interactions with other methods, such as [`read`]
|
||||
/// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` and
|
||||
/// `LOCK_NB` flags, and the `LockFileEx` function on Windows with the
|
||||
/// `LOCKFILE_FAIL_IMMEDIATELY` flag. Note that, this
|
||||
/// [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: io#platform-specific-behavior
|
||||
///
|
||||
/// [`lock`]: File::lock
|
||||
/// [`lock_shared`]: File::lock_shared
|
||||
/// [`try_lock`]: File::try_lock
|
||||
/// [`unlock`]: File::unlock
|
||||
/// [`read`]: Read::read
|
||||
/// [`write`]: Write::write
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(file_lock)]
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("foo.txt")?;
|
||||
/// f.try_lock_shared()?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "file_lock", issue = "130994")]
|
||||
pub fn try_lock_shared(&self) -> io::Result<bool> {
|
||||
self.inner.try_lock_shared()
|
||||
}
|
||||
|
||||
/// Release all locks on the file.
|
||||
///
|
||||
/// All remaining locks are released when the file handle, and all clones of it, are dropped.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_UN` flag,
|
||||
/// and the `UnlockFile` function on Windows. Note that, this
|
||||
/// [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: io#platform-specific-behavior
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(file_lock)]
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("foo.txt")?;
|
||||
/// f.lock()?;
|
||||
/// f.unlock()?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "file_lock", issue = "130994")]
|
||||
pub fn unlock(&self) -> io::Result<()> {
|
||||
self.inner.unlock()
|
||||
}
|
||||
|
||||
/// Truncates or extends the underlying file, updating the size of
|
||||
/// this file to become `size`.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -203,6 +203,86 @@ fn file_test_io_seek_and_write() {
|
|||
assert!(read_str == final_msg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_lock_multiple_shared() {
|
||||
let tmpdir = tmpdir();
|
||||
let filename = &tmpdir.join("file_lock_multiple_shared_test.txt");
|
||||
let f1 = check!(File::create(filename));
|
||||
let f2 = check!(OpenOptions::new().write(true).open(filename));
|
||||
|
||||
// Check that we can acquire concurrent shared locks
|
||||
check!(f1.lock_shared());
|
||||
check!(f2.lock_shared());
|
||||
check!(f1.unlock());
|
||||
check!(f2.unlock());
|
||||
assert!(check!(f1.try_lock_shared()));
|
||||
assert!(check!(f2.try_lock_shared()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_lock_blocking() {
|
||||
let tmpdir = tmpdir();
|
||||
let filename = &tmpdir.join("file_lock_blocking_test.txt");
|
||||
let f1 = check!(File::create(filename));
|
||||
let f2 = check!(OpenOptions::new().write(true).open(filename));
|
||||
|
||||
// Check that shared locks block exclusive locks
|
||||
check!(f1.lock_shared());
|
||||
assert!(!check!(f2.try_lock()));
|
||||
check!(f1.unlock());
|
||||
|
||||
// Check that exclusive locks block shared locks
|
||||
check!(f1.lock());
|
||||
assert!(!check!(f2.try_lock_shared()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_lock_drop() {
|
||||
let tmpdir = tmpdir();
|
||||
let filename = &tmpdir.join("file_lock_dup_test.txt");
|
||||
let f1 = check!(File::create(filename));
|
||||
let f2 = check!(OpenOptions::new().write(true).open(filename));
|
||||
|
||||
// Check that locks are released when the File is dropped
|
||||
check!(f1.lock_shared());
|
||||
assert!(!check!(f2.try_lock()));
|
||||
drop(f1);
|
||||
assert!(check!(f2.try_lock()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_lock_dup() {
|
||||
let tmpdir = tmpdir();
|
||||
let filename = &tmpdir.join("file_lock_dup_test.txt");
|
||||
let f1 = check!(File::create(filename));
|
||||
let f2 = check!(OpenOptions::new().write(true).open(filename));
|
||||
|
||||
// Check that locks are not dropped if the File has been cloned
|
||||
check!(f1.lock_shared());
|
||||
assert!(!check!(f2.try_lock()));
|
||||
let cloned = check!(f1.try_clone());
|
||||
drop(f1);
|
||||
assert!(!check!(f2.try_lock()));
|
||||
drop(cloned)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn file_lock_double_unlock() {
|
||||
let tmpdir = tmpdir();
|
||||
let filename = &tmpdir.join("file_lock_double_unlock_test.txt");
|
||||
let f1 = check!(File::create(filename));
|
||||
let f2 = check!(OpenOptions::new().write(true).open(filename));
|
||||
|
||||
// On Windows a file handle may acquire both a shared and exclusive lock.
|
||||
// Check that both are released by unlock()
|
||||
check!(f1.lock());
|
||||
check!(f1.lock_shared());
|
||||
assert!(!check!(f2.try_lock()));
|
||||
check!(f1.unlock());
|
||||
assert!(check!(f2.try_lock()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_test_io_seek_shakedown() {
|
||||
// 01234567890123
|
||||
|
|
|
|||
|
|
@ -364,6 +364,26 @@ impl File {
|
|||
self.fsync()
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> io::Result<()> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn lock_shared(&self) -> io::Result<()> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn try_lock(&self) -> io::Result<bool> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn try_lock_shared(&self) -> io::Result<bool> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn unlock(&self) -> io::Result<()> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn truncate(&self, _size: u64) -> io::Result<()> {
|
||||
Err(Error::from_raw_os_error(22))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -350,6 +350,26 @@ impl File {
|
|||
self.flush()
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> io::Result<()> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn lock_shared(&self) -> io::Result<()> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn try_lock(&self) -> io::Result<bool> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn try_lock_shared(&self) -> io::Result<bool> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn unlock(&self) -> io::Result<()> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn truncate(&self, _size: u64) -> io::Result<()> {
|
||||
unsupported()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1254,6 +1254,43 @@ impl File {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> io::Result<()> {
|
||||
cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn lock_shared(&self) -> io::Result<()> {
|
||||
cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn try_lock(&self) -> io::Result<bool> {
|
||||
let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) });
|
||||
if let Err(ref err) = result {
|
||||
if err.kind() == io::ErrorKind::WouldBlock {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
result?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
pub fn try_lock_shared(&self) -> io::Result<bool> {
|
||||
let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) });
|
||||
if let Err(ref err) = result {
|
||||
if err.kind() == io::ErrorKind::WouldBlock {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
result?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
pub fn unlock(&self) -> io::Result<()> {
|
||||
cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn truncate(&self, size: u64) -> io::Result<()> {
|
||||
let size: off64_t =
|
||||
size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
|
||||
|
|
|
|||
|
|
@ -198,6 +198,26 @@ impl File {
|
|||
self.0
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn lock_shared(&self) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn try_lock(&self) -> io::Result<bool> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn try_lock_shared(&self) -> io::Result<bool> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn unlock(&self) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn truncate(&self, _size: u64) -> io::Result<()> {
|
||||
self.0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -420,6 +420,26 @@ impl File {
|
|||
self.fd.datasync()
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> io::Result<()> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn lock_shared(&self) -> io::Result<()> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn try_lock(&self) -> io::Result<bool> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn try_lock_shared(&self) -> io::Result<bool> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn unlock(&self) -> io::Result<()> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
pub fn truncate(&self, size: u64) -> io::Result<()> {
|
||||
self.fd.filestat_set_size(size)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2349,6 +2349,9 @@ Windows.Win32.Storage.FileSystem.GetFinalPathNameByHandleW
|
|||
Windows.Win32.Storage.FileSystem.GetFullPathNameW
|
||||
Windows.Win32.Storage.FileSystem.GetTempPathW
|
||||
Windows.Win32.Storage.FileSystem.INVALID_FILE_ATTRIBUTES
|
||||
Windows.Win32.Storage.FileSystem.LOCKFILE_EXCLUSIVE_LOCK
|
||||
Windows.Win32.Storage.FileSystem.LOCKFILE_FAIL_IMMEDIATELY
|
||||
Windows.Win32.Storage.FileSystem.LockFileEx
|
||||
Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE
|
||||
Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE_CALLBACK_REASON
|
||||
Windows.Win32.Storage.FileSystem.MAXIMUM_REPARSE_DATA_BUFFER_SIZE
|
||||
|
|
@ -2394,6 +2397,7 @@ Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAG_DIRECTORY
|
|||
Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAGS
|
||||
Windows.Win32.Storage.FileSystem.SYNCHRONIZE
|
||||
Windows.Win32.Storage.FileSystem.TRUNCATE_EXISTING
|
||||
Windows.Win32.Storage.FileSystem.UnlockFile
|
||||
Windows.Win32.Storage.FileSystem.VOLUME_NAME_DOS
|
||||
Windows.Win32.Storage.FileSystem.VOLUME_NAME_GUID
|
||||
Windows.Win32.Storage.FileSystem.VOLUME_NAME_NONE
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ windows_targets::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinit
|
|||
windows_targets::link!("kernel32.dll" "system" fn InitOnceComplete(lpinitonce : *mut INIT_ONCE, dwflags : u32, lpcontext : *const core::ffi::c_void) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn InitializeProcThreadAttributeList(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwattributecount : u32, dwflags : u32, lpsize : *mut usize) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn LocalFree(hmem : HLOCAL) -> HLOCAL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn LockFileEx(hfile : HANDLE, dwflags : LOCK_FILE_FLAGS, dwreserved : u32, nnumberofbytestolocklow : u32, nnumberofbytestolockhigh : u32, lpoverlapped : *mut OVERLAPPED) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, dwflags : MOVE_FILE_FLAGS) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn MultiByteToWideChar(codepage : u32, dwflags : MULTI_BYTE_TO_WIDE_CHAR_FLAGS, lpmultibytestr : PCSTR, cbmultibyte : i32, lpwidecharstr : PWSTR, cchwidechar : i32) -> i32);
|
||||
windows_targets::link!("kernel32.dll" "system" fn QueryPerformanceCounter(lpperformancecount : *mut i64) -> BOOL);
|
||||
|
|
@ -96,6 +97,7 @@ windows_targets::link!("kernel32.dll" "system" fn TlsGetValue(dwtlsindex : u32)
|
|||
windows_targets::link!("kernel32.dll" "system" fn TlsSetValue(dwtlsindex : u32, lptlsvalue : *const core::ffi::c_void) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockExclusive(srwlock : *mut SRWLOCK) -> BOOLEAN);
|
||||
windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockShared(srwlock : *mut SRWLOCK) -> BOOLEAN);
|
||||
windows_targets::link!("kernel32.dll" "system" fn UnlockFile(hfile : HANDLE, dwfileoffsetlow : u32, dwfileoffsethigh : u32, nnumberofbytestounlocklow : u32, nnumberofbytestounlockhigh : u32) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn UpdateProcThreadAttribute(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwflags : u32, attribute : usize, lpvalue : *const core::ffi::c_void, cbsize : usize, lppreviousvalue : *mut core::ffi::c_void, lpreturnsize : *const usize) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn WaitForMultipleObjects(ncount : u32, lphandles : *const HANDLE, bwaitall : BOOL, dwmilliseconds : u32) -> WAIT_EVENT);
|
||||
windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT);
|
||||
|
|
@ -2725,6 +2727,9 @@ pub struct LINGER {
|
|||
pub l_onoff: u16,
|
||||
pub l_linger: u16,
|
||||
}
|
||||
pub const LOCKFILE_EXCLUSIVE_LOCK: LOCK_FILE_FLAGS = 2u32;
|
||||
pub const LOCKFILE_FAIL_IMMEDIATELY: LOCK_FILE_FLAGS = 1u32;
|
||||
pub type LOCK_FILE_FLAGS = u32;
|
||||
pub type LPOVERLAPPED_COMPLETION_ROUTINE = Option<
|
||||
unsafe extern "system" fn(
|
||||
dwerrorcode: u32,
|
||||
|
|
|
|||
|
|
@ -346,6 +346,94 @@ impl File {
|
|||
self.fsync()
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> io::Result<()> {
|
||||
cvt(unsafe {
|
||||
let mut overlapped = mem::zeroed();
|
||||
c::LockFileEx(
|
||||
self.handle.as_raw_handle(),
|
||||
c::LOCKFILE_EXCLUSIVE_LOCK,
|
||||
0,
|
||||
u32::MAX,
|
||||
u32::MAX,
|
||||
&mut overlapped,
|
||||
)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn lock_shared(&self) -> io::Result<()> {
|
||||
cvt(unsafe {
|
||||
let mut overlapped = mem::zeroed();
|
||||
c::LockFileEx(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX, &mut overlapped)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn try_lock(&self) -> io::Result<bool> {
|
||||
let result = cvt(unsafe {
|
||||
let mut overlapped = mem::zeroed();
|
||||
c::LockFileEx(
|
||||
self.handle.as_raw_handle(),
|
||||
c::LOCKFILE_EXCLUSIVE_LOCK | c::LOCKFILE_FAIL_IMMEDIATELY,
|
||||
0,
|
||||
u32::MAX,
|
||||
u32::MAX,
|
||||
&mut overlapped,
|
||||
)
|
||||
});
|
||||
|
||||
if let Err(ref err) = result {
|
||||
if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32)
|
||||
|| err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32)
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
result?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn try_lock_shared(&self) -> io::Result<bool> {
|
||||
let result = cvt(unsafe {
|
||||
let mut overlapped = mem::zeroed();
|
||||
c::LockFileEx(
|
||||
self.handle.as_raw_handle(),
|
||||
c::LOCKFILE_FAIL_IMMEDIATELY,
|
||||
0,
|
||||
u32::MAX,
|
||||
u32::MAX,
|
||||
&mut overlapped,
|
||||
)
|
||||
});
|
||||
|
||||
if let Err(ref err) = result {
|
||||
if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32)
|
||||
|| err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32)
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
result?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn unlock(&self) -> io::Result<()> {
|
||||
// Unlock the handle twice because LockFileEx() allows a file handle to acquire
|
||||
// both an exclusive and shared lock, in which case the documentation states that:
|
||||
// "...two unlock operations are necessary to unlock the region; the first unlock operation
|
||||
// unlocks the exclusive lock, the second unlock operation unlocks the shared lock"
|
||||
cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) })?;
|
||||
let result =
|
||||
cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) });
|
||||
if let Err(ref err) = result {
|
||||
if err.raw_os_error() == Some(c::ERROR_NOT_LOCKED as i32) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
result?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn truncate(&self, size: u64) -> io::Result<()> {
|
||||
let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 };
|
||||
api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue