diff options
| author | Christopher Berner <me@cberner.com> | 2024-10-18 21:08:25 -0700 |
|---|---|---|
| committer | Christopher Berner <me@cberner.com> | 2024-10-19 13:50:19 -0700 |
| commit | e0fdaa8624ee062b38372c2987947ff2dc6f7113 (patch) | |
| tree | 786b55e5fad2c27fb0402c5e557b5fea5c16d1fe | |
| parent | d2cdc76256313e8f23f585107c43badb49d5473d (diff) | |
| download | rust-e0fdaa8624ee062b38372c2987947ff2dc6f7113.tar.gz rust-e0fdaa8624ee062b38372c2987947ff2dc6f7113.zip | |
Support lock() and lock_shared() on async IO Files
| -rw-r--r-- | library/std/src/fs/tests.rs | 38 | ||||
| -rw-r--r-- | library/std/src/sys/pal/windows/fs.rs | 62 |
2 files changed, 87 insertions, 13 deletions
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index fe3dc4adff0..05efed6b5df 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -284,6 +284,44 @@ fn file_lock_double_unlock() { } #[test] +#[cfg(windows)] +fn file_lock_blocking_async() { + use crate::thread::{sleep, spawn}; + const FILE_FLAG_OVERLAPPED: u32 = 0x40000000; + + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_lock_blocking_async.txt"); + let f1 = check!(File::create(filename)); + let f2 = + check!(OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).write(true).open(filename)); + + check!(f1.lock()); + + // Ensure that lock() is synchronous when the file is opened for asynchronous IO + let t = spawn(move || { + check!(f2.lock()); + }); + sleep(Duration::from_secs(1)); + assert!(!t.is_finished()); + check!(f1.unlock()); + t.join().unwrap(); + + // Ensure that lock_shared() is synchronous when the file is opened for asynchronous IO + let f2 = + check!(OpenOptions::new().custom_flags(FILE_FLAG_OVERLAPPED).write(true).open(filename)); + check!(f1.lock()); + + // Ensure that lock() is synchronous when the file is opened for asynchronous IO + let t = spawn(move || { + check!(f2.lock_shared()); + }); + sleep(Duration::from_secs(1)); + assert!(!t.is_finished()); + check!(f1.unlock()); + t.join().unwrap(); +} + +#[test] fn file_test_io_seek_shakedown() { // 01234567890123 let initial_msg = "qwer-asdf-zxcv"; diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index bc728d498c3..138ac59c161 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -346,27 +346,63 @@ impl File { self.fsync() } - pub fn lock(&self) -> io::Result<()> { - cvt(unsafe { - let mut overlapped = mem::zeroed(); - c::LockFileEx( + fn acquire_lock(&self, flags: c::LOCK_FILE_FLAGS) -> io::Result<()> { + unsafe { + let mut overlapped: c::OVERLAPPED = mem::zeroed(); + let event = c::CreateEventW(ptr::null_mut(), c::FALSE, c::FALSE, ptr::null()); + if event.is_null() { + return Err(io::Error::last_os_error()); + } + overlapped.hEvent = event; + let lock_result = cvt(c::LockFileEx( self.handle.as_raw_handle(), - c::LOCKFILE_EXCLUSIVE_LOCK, + flags, 0, u32::MAX, u32::MAX, &mut overlapped, - ) - })?; - Ok(()) + )); + + let final_result = match lock_result { + Ok(_) => Ok(()), + Err(err) => { + if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) { + // Wait for the lock to be acquired. This can happen asynchronously, + // if the file handle was opened for async IO + let wait_result = c::WaitForSingleObject(overlapped.hEvent, c::INFINITE); + if wait_result == c::WAIT_OBJECT_0 { + // Wait completed successfully, get the lock operation status + let mut bytes_transferred = 0; + cvt(c::GetOverlappedResult( + self.handle.as_raw_handle(), + &mut overlapped, + &mut bytes_transferred, + c::TRUE, + )) + .map(|_| ()) + } else if wait_result == c::WAIT_FAILED { + // Wait failed + Err(io::Error::last_os_error()) + } else { + // WAIT_ABANDONED and WAIT_TIMEOUT should not be possible + unreachable!() + } + } else { + Err(err) + } + } + }; + c::CloseHandle(overlapped.hEvent); + final_result + } + } + + pub fn lock(&self) -> io::Result<()> { + self.acquire_lock(c::LOCKFILE_EXCLUSIVE_LOCK) } 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(()) + self.acquire_lock(0) } pub fn try_lock(&self) -> io::Result<bool> { |
