Skip to content

Commit f0d0882

Browse files
authored
Merge pull request #4378 from RalfJung/flock
use File::lock to implement flock, and add a test for File::lock
2 parents ccb3aca + b1652dc commit f0d0882

File tree

5 files changed

+50
-105
lines changed

5 files changed

+50
-105
lines changed

src/tools/miri/Cargo.lock

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,6 @@ dependencies = [
555555
"tempfile",
556556
"tikv-jemalloc-sys",
557557
"ui_test",
558-
"windows-sys",
559558
]
560559

561560
[[package]]

src/tools/miri/Cargo.toml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,6 @@ libc = "0.2"
4040
libffi = "4.0.0"
4141
libloading = "0.8"
4242

43-
[target.'cfg(target_family = "windows")'.dependencies]
44-
windows-sys = { version = "0.59", features = [
45-
"Win32_Foundation",
46-
"Win32_System_IO",
47-
"Win32_Storage_FileSystem",
48-
] }
49-
5043
[dev-dependencies]
5144
ui_test = "0.29.1"
5245
colored = "2"

src/tools/miri/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#![feature(unqualified_local_imports)]
1717
#![feature(derive_coerce_pointee)]
1818
#![feature(arbitrary_self_types)]
19+
#![feature(file_lock)]
1920
// Configure clippy and other lints
2021
#![allow(
2122
clippy::collapsible_else_if,

src/tools/miri/src/shims/unix/fs.rs

Lines changed: 19 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
33
use std::borrow::Cow;
44
use std::fs::{
5-
DirBuilder, File, FileType, OpenOptions, ReadDir, read_dir, remove_dir, remove_file, rename,
5+
DirBuilder, File, FileType, OpenOptions, ReadDir, TryLockError, read_dir, remove_dir,
6+
remove_file, rename,
67
};
78
use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write};
89
use std::path::{Path, PathBuf};
@@ -89,103 +90,27 @@ impl UnixFileDescription for FileHandle {
8990
communicate_allowed: bool,
9091
op: FlockOp,
9192
) -> InterpResult<'tcx, io::Result<()>> {
92-
// cfg(bootstrap)
93-
macro_rules! cfg_select_dispatch {
94-
($($tokens:tt)*) => {
95-
#[cfg(bootstrap)]
96-
cfg_match! { $($tokens)* }
97-
98-
#[cfg(not(bootstrap))]
99-
cfg_select! { $($tokens)* }
100-
};
101-
}
102-
10393
assert!(communicate_allowed, "isolation should have prevented even opening a file");
104-
cfg_select_dispatch! {
105-
all(target_family = "unix", not(target_os = "solaris")) => {
106-
use std::os::fd::AsRawFd;
107-
108-
use FlockOp::*;
109-
// We always use non-blocking call to prevent interpreter from being blocked
110-
let (host_op, lock_nb) = match op {
111-
SharedLock { nonblocking } => (libc::LOCK_SH | libc::LOCK_NB, nonblocking),
112-
ExclusiveLock { nonblocking } => (libc::LOCK_EX | libc::LOCK_NB, nonblocking),
113-
Unlock => (libc::LOCK_UN, false),
114-
};
11594

116-
let fd = self.file.as_raw_fd();
117-
let ret = unsafe { libc::flock(fd, host_op) };
118-
let res = match ret {
119-
0 => Ok(()),
120-
-1 => {
121-
let err = io::Error::last_os_error();
122-
if !lock_nb && err.kind() == io::ErrorKind::WouldBlock {
123-
throw_unsup_format!("blocking `flock` is not currently supported");
124-
}
125-
Err(err)
126-
}
127-
ret => panic!("Unexpected return value from flock: {ret}"),
128-
};
129-
interp_ok(res)
95+
use FlockOp::*;
96+
// We must not block the interpreter loop, so we always `try_lock`.
97+
let (res, nonblocking) = match op {
98+
SharedLock { nonblocking } => (self.file.try_lock_shared(), nonblocking),
99+
ExclusiveLock { nonblocking } => (self.file.try_lock(), nonblocking),
100+
Unlock => {
101+
return interp_ok(self.file.unlock());
130102
}
131-
target_family = "windows" => {
132-
use std::os::windows::io::AsRawHandle;
133-
134-
use windows_sys::Win32::Foundation::{
135-
ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, FALSE, TRUE,
136-
};
137-
use windows_sys::Win32::Storage::FileSystem::{
138-
LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx, UnlockFile,
139-
};
140-
141-
let fh = self.file.as_raw_handle();
142-
143-
use FlockOp::*;
144-
let (ret, lock_nb) = match op {
145-
SharedLock { nonblocking } | ExclusiveLock { nonblocking } => {
146-
// We always use non-blocking call to prevent interpreter from being blocked
147-
let mut flags = LOCKFILE_FAIL_IMMEDIATELY;
148-
if matches!(op, ExclusiveLock { .. }) {
149-
flags |= LOCKFILE_EXCLUSIVE_LOCK;
150-
}
151-
let ret = unsafe { LockFileEx(fh, flags, 0, !0, !0, &mut std::mem::zeroed()) };
152-
(ret, nonblocking)
153-
}
154-
Unlock => {
155-
let ret = unsafe { UnlockFile(fh, 0, 0, !0, !0) };
156-
(ret, false)
157-
}
158-
};
103+
};
159104

160-
let res = match ret {
161-
TRUE => Ok(()),
162-
FALSE => {
163-
let mut err = io::Error::last_os_error();
164-
// This only runs on Windows hosts so we can use `raw_os_error`.
165-
// We have to be careful not to forward that error code to target code.
166-
let code: u32 = err.raw_os_error().unwrap().try_into().unwrap();
167-
if matches!(code, ERROR_IO_PENDING | ERROR_LOCK_VIOLATION) {
168-
if lock_nb {
169-
// The io error mapping does not know about these error codes,
170-
// so we translate it to `WouldBlock` manually.
171-
let desc = format!("LockFileEx wouldblock error: {err}");
172-
err = io::Error::new(io::ErrorKind::WouldBlock, desc);
173-
} else {
174-
throw_unsup_format!("blocking `flock` is not currently supported");
175-
}
176-
}
177-
Err(err)
178-
}
179-
_ => panic!("Unexpected return value: {ret}"),
180-
};
181-
interp_ok(res)
182-
}
183-
_ => {
184-
let _ = op;
185-
throw_unsup_format!(
186-
"flock is supported only on UNIX (except Solaris) and Windows hosts"
187-
);
188-
}
105+
match res {
106+
Ok(()) => interp_ok(Ok(())),
107+
Err(TryLockError::Error(err)) => interp_ok(Err(err)),
108+
Err(TryLockError::WouldBlock) =>
109+
if nonblocking {
110+
interp_ok(Err(ErrorKind::WouldBlock.into()))
111+
} else {
112+
throw_unsup_format!("blocking `flock` is not currently supported");
113+
},
189114
}
190115
}
191116
}

src/tools/miri/tests/pass/shims/fs.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
#![feature(io_error_more)]
44
#![feature(io_error_uncategorized)]
5+
#![feature(file_lock)]
56

67
use std::collections::BTreeMap;
78
use std::ffi::OsString;
89
use std::fs::{
9-
File, OpenOptions, canonicalize, create_dir, read_dir, remove_dir, remove_dir_all, remove_file,
10-
rename,
10+
self, File, OpenOptions, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename,
1111
};
1212
use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write};
1313
use std::path::Path;
@@ -33,6 +33,8 @@ fn main() {
3333
test_canonicalize();
3434
#[cfg(unix)]
3535
test_pread_pwrite();
36+
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
37+
test_flock();
3638
}
3739
}
3840

@@ -240,7 +242,7 @@ fn test_canonicalize() {
240242
let path = dir_path.join("test_file");
241243
drop(File::create(&path).unwrap());
242244

243-
let p = canonicalize(format!("{}/./test_file", dir_path.to_string_lossy())).unwrap();
245+
let p = fs::canonicalize(format!("{}/./test_file", dir_path.to_string_lossy())).unwrap();
244246
assert_eq!(p.to_string_lossy().find("/./"), None);
245247

246248
remove_dir_all(&dir_path).unwrap();
@@ -351,3 +353,28 @@ fn test_pread_pwrite() {
351353
f.read_exact(&mut buf1).unwrap();
352354
assert_eq!(&buf1, b" m");
353355
}
356+
357+
// This function does seem to exist on Illumos but std does not expose it there.
358+
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
359+
fn test_flock() {
360+
let bytes = b"Hello, World!\n";
361+
let path = utils::prepare_with_content("miri_test_fs_flock.txt", bytes);
362+
let file1 = OpenOptions::new().read(true).write(true).open(&path).unwrap();
363+
let file2 = OpenOptions::new().read(true).write(true).open(&path).unwrap();
364+
365+
// Test that we can apply many shared locks
366+
file1.lock_shared().unwrap();
367+
file2.lock_shared().unwrap();
368+
// Test that shared lock prevents exclusive lock
369+
assert!(matches!(file1.try_lock().unwrap_err(), fs::TryLockError::WouldBlock));
370+
// Unlock shared lock
371+
file1.unlock().unwrap();
372+
file2.unlock().unwrap();
373+
// Take exclusive lock
374+
file1.lock().unwrap();
375+
// Test that shared lock prevents exclusive and shared locks
376+
assert!(matches!(file2.try_lock().unwrap_err(), fs::TryLockError::WouldBlock));
377+
assert!(matches!(file2.try_lock_shared().unwrap_err(), fs::TryLockError::WouldBlock));
378+
// Unlock exclusive lock
379+
file1.unlock().unwrap();
380+
}

0 commit comments

Comments
 (0)