mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-21 19:33:16 +00:00
Auto merge of #132902 - matthiaskrgr:rollup-43qgg3t, r=matthiaskrgr
Rollup of 4 pull requests Successful merges: - #129627 (Ensure that tail expr receive lifetime extension) - #130999 (Implement file_lock feature) - #132873 (handle separate prefixes in clippy rules) - #132891 (Remove `rustc_session::config::rustc_short_optgroups`) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
81eef2d362
@ -934,9 +934,12 @@ pub fn version_at_macro_invocation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
|
fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
|
||||||
let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() };
|
|
||||||
let mut options = getopts::Options::new();
|
let mut options = getopts::Options::new();
|
||||||
for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) {
|
for option in config::rustc_optgroups()
|
||||||
|
.iter()
|
||||||
|
.filter(|x| verbose || !x.is_verbose_help_only)
|
||||||
|
.filter(|x| include_unstable_options || x.is_stable())
|
||||||
|
{
|
||||||
option.apply(&mut options);
|
option.apply(&mut options);
|
||||||
}
|
}
|
||||||
let message = "Usage: rustc [OPTIONS] INPUT";
|
let message = "Usage: rustc [OPTIONS] INPUT";
|
||||||
|
@ -1398,9 +1398,25 @@ pub enum OptionKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct RustcOptGroup {
|
pub struct RustcOptGroup {
|
||||||
apply: Box<dyn Fn(&mut getopts::Options) -> &mut getopts::Options>,
|
/// The "primary" name for this option. Normally equal to `long_name`,
|
||||||
|
/// except for options that don't have a long name, in which case
|
||||||
|
/// `short_name` is used.
|
||||||
|
///
|
||||||
|
/// This is needed when interacting with `getopts` in some situations,
|
||||||
|
/// because if an option has both forms, that library treats the long name
|
||||||
|
/// as primary and the short name as an alias.
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
stability: OptionStability,
|
stability: OptionStability,
|
||||||
|
kind: OptionKind,
|
||||||
|
|
||||||
|
short_name: &'static str,
|
||||||
|
long_name: &'static str,
|
||||||
|
desc: &'static str,
|
||||||
|
value_hint: &'static str,
|
||||||
|
|
||||||
|
/// If true, this option should not be printed by `rustc --help`, but
|
||||||
|
/// should still be printed by `rustc --help -v`.
|
||||||
|
pub is_verbose_help_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RustcOptGroup {
|
impl RustcOptGroup {
|
||||||
@ -1409,7 +1425,13 @@ impl RustcOptGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply(&self, options: &mut getopts::Options) {
|
pub fn apply(&self, options: &mut getopts::Options) {
|
||||||
(self.apply)(options);
|
let &Self { short_name, long_name, desc, value_hint, .. } = self;
|
||||||
|
match self.kind {
|
||||||
|
OptionKind::Opt => options.optopt(short_name, long_name, desc, value_hint),
|
||||||
|
OptionKind::Multi => options.optmulti(short_name, long_name, desc, value_hint),
|
||||||
|
OptionKind::Flag => options.optflag(short_name, long_name, desc),
|
||||||
|
OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1419,31 +1441,22 @@ pub fn make_opt(
|
|||||||
short_name: &'static str,
|
short_name: &'static str,
|
||||||
long_name: &'static str,
|
long_name: &'static str,
|
||||||
desc: &'static str,
|
desc: &'static str,
|
||||||
hint: &'static str,
|
value_hint: &'static str,
|
||||||
) -> RustcOptGroup {
|
) -> RustcOptGroup {
|
||||||
|
// "Flag" options don't have a value, and therefore don't have a value hint.
|
||||||
|
match kind {
|
||||||
|
OptionKind::Opt | OptionKind::Multi => {}
|
||||||
|
OptionKind::Flag | OptionKind::FlagMulti => assert_eq!(value_hint, ""),
|
||||||
|
}
|
||||||
RustcOptGroup {
|
RustcOptGroup {
|
||||||
name: cmp::max_by_key(short_name, long_name, |s| s.len()),
|
name: cmp::max_by_key(short_name, long_name, |s| s.len()),
|
||||||
stability,
|
stability,
|
||||||
apply: match kind {
|
kind,
|
||||||
OptionKind::Opt => Box::new(move |opts: &mut getopts::Options| {
|
short_name,
|
||||||
opts.optopt(short_name, long_name, desc, hint)
|
long_name,
|
||||||
}),
|
desc,
|
||||||
OptionKind::Multi => Box::new(move |opts: &mut getopts::Options| {
|
value_hint,
|
||||||
opts.optmulti(short_name, long_name, desc, hint)
|
is_verbose_help_only: false,
|
||||||
}),
|
|
||||||
OptionKind::Flag => {
|
|
||||||
assert_eq!(hint, "");
|
|
||||||
Box::new(move |opts: &mut getopts::Options| {
|
|
||||||
opts.optflag(short_name, long_name, desc)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
OptionKind::FlagMulti => {
|
|
||||||
assert_eq!(hint, "");
|
|
||||||
Box::new(move |opts: &mut getopts::Options| {
|
|
||||||
opts.optflagmulti(short_name, long_name, desc)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1454,16 +1467,15 @@ The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Returns the "short" subset of the rustc command line options,
|
/// Returns all rustc command line options, including metadata for
|
||||||
/// including metadata for each option, such as whether the option is
|
/// each option, such as whether the option is stable.
|
||||||
/// part of the stable long-term interface for rustc.
|
pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
|
||||||
pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
|
|
||||||
use OptionKind::{Flag, FlagMulti, Multi, Opt};
|
use OptionKind::{Flag, FlagMulti, Multi, Opt};
|
||||||
use OptionStability::Stable;
|
use OptionStability::{Stable, Unstable};
|
||||||
|
|
||||||
use self::make_opt as opt;
|
use self::make_opt as opt;
|
||||||
|
|
||||||
vec![
|
let mut options = vec![
|
||||||
opt(Stable, Flag, "h", "help", "Display this message", ""),
|
opt(Stable, Flag, "h", "help", "Display this message", ""),
|
||||||
opt(
|
opt(
|
||||||
Stable,
|
Stable,
|
||||||
@ -1550,21 +1562,11 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
|
|||||||
opt(Stable, Multi, "C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
|
opt(Stable, Multi, "C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
|
||||||
opt(Stable, Flag, "V", "version", "Print version info and exit", ""),
|
opt(Stable, Flag, "V", "version", "Print version info and exit", ""),
|
||||||
opt(Stable, Flag, "v", "verbose", "Use verbose output", ""),
|
opt(Stable, Flag, "v", "verbose", "Use verbose output", ""),
|
||||||
]
|
];
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all rustc command line options, including metadata for
|
// Options in this list are hidden from `rustc --help` by default, but are
|
||||||
/// each option, such as whether the option is part of the stable
|
// shown by `rustc --help -v`.
|
||||||
/// long-term interface for rustc.
|
let verbose_only = [
|
||||||
pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
|
|
||||||
use OptionKind::{Multi, Opt};
|
|
||||||
use OptionStability::{Stable, Unstable};
|
|
||||||
|
|
||||||
use self::make_opt as opt;
|
|
||||||
|
|
||||||
let mut opts = rustc_short_optgroups();
|
|
||||||
// FIXME: none of these descriptions are actually used
|
|
||||||
opts.extend(vec![
|
|
||||||
opt(
|
opt(
|
||||||
Stable,
|
Stable,
|
||||||
Multi,
|
Multi,
|
||||||
@ -1590,9 +1592,9 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
|
|||||||
"",
|
"",
|
||||||
"color",
|
"color",
|
||||||
"Configure coloring of output:
|
"Configure coloring of output:
|
||||||
auto = colorize, if output goes to a tty (default);
|
auto = colorize, if output goes to a tty (default);
|
||||||
always = always colorize output;
|
always = always colorize output;
|
||||||
never = never colorize output",
|
never = never colorize output",
|
||||||
"auto|always|never",
|
"auto|always|never",
|
||||||
),
|
),
|
||||||
opt(
|
opt(
|
||||||
@ -1612,8 +1614,13 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
|
|||||||
"FROM=TO",
|
"FROM=TO",
|
||||||
),
|
),
|
||||||
opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "VAR=VALUE"),
|
opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "VAR=VALUE"),
|
||||||
]);
|
];
|
||||||
opts
|
options.extend(verbose_only.into_iter().map(|mut opt| {
|
||||||
|
opt.is_verbose_help_only = true;
|
||||||
|
opt
|
||||||
|
}));
|
||||||
|
|
||||||
|
options
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cmd_lint_options(
|
pub fn get_cmd_lint_options(
|
||||||
|
@ -624,6 +624,223 @@ impl File {
|
|||||||
self.inner.datasync()
|
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
|
/// Truncates or extends the underlying file, updating the size of
|
||||||
/// this file to become `size`.
|
/// this file to become `size`.
|
||||||
///
|
///
|
||||||
|
@ -203,6 +203,124 @@ fn file_test_io_seek_and_write() {
|
|||||||
assert!(read_str == final_msg);
|
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]
|
||||||
|
#[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]
|
#[test]
|
||||||
fn file_test_io_seek_shakedown() {
|
fn file_test_io_seek_shakedown() {
|
||||||
// 01234567890123
|
// 01234567890123
|
||||||
|
@ -364,6 +364,26 @@ impl File {
|
|||||||
self.fsync()
|
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<()> {
|
pub fn truncate(&self, _size: u64) -> io::Result<()> {
|
||||||
Err(Error::from_raw_os_error(22))
|
Err(Error::from_raw_os_error(22))
|
||||||
}
|
}
|
||||||
|
@ -350,6 +350,26 @@ impl File {
|
|||||||
self.flush()
|
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<()> {
|
pub fn truncate(&self, _size: u64) -> io::Result<()> {
|
||||||
unsupported()
|
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<()> {
|
pub fn truncate(&self, size: u64) -> io::Result<()> {
|
||||||
let size: off64_t =
|
let size: off64_t =
|
||||||
size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
|
size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
|
||||||
|
@ -198,6 +198,26 @@ impl File {
|
|||||||
self.0
|
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<()> {
|
pub fn truncate(&self, _size: u64) -> io::Result<()> {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
@ -420,6 +420,26 @@ impl File {
|
|||||||
self.fd.datasync()
|
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<()> {
|
pub fn truncate(&self, size: u64) -> io::Result<()> {
|
||||||
self.fd.filestat_set_size(size)
|
self.fd.filestat_set_size(size)
|
||||||
}
|
}
|
||||||
|
@ -2351,6 +2351,9 @@ Windows.Win32.Storage.FileSystem.GetFinalPathNameByHandleW
|
|||||||
Windows.Win32.Storage.FileSystem.GetFullPathNameW
|
Windows.Win32.Storage.FileSystem.GetFullPathNameW
|
||||||
Windows.Win32.Storage.FileSystem.GetTempPathW
|
Windows.Win32.Storage.FileSystem.GetTempPathW
|
||||||
Windows.Win32.Storage.FileSystem.INVALID_FILE_ATTRIBUTES
|
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
|
||||||
Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE_CALLBACK_REASON
|
Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE_CALLBACK_REASON
|
||||||
Windows.Win32.Storage.FileSystem.MAXIMUM_REPARSE_DATA_BUFFER_SIZE
|
Windows.Win32.Storage.FileSystem.MAXIMUM_REPARSE_DATA_BUFFER_SIZE
|
||||||
@ -2396,6 +2399,7 @@ Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAG_DIRECTORY
|
|||||||
Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAGS
|
Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAGS
|
||||||
Windows.Win32.Storage.FileSystem.SYNCHRONIZE
|
Windows.Win32.Storage.FileSystem.SYNCHRONIZE
|
||||||
Windows.Win32.Storage.FileSystem.TRUNCATE_EXISTING
|
Windows.Win32.Storage.FileSystem.TRUNCATE_EXISTING
|
||||||
|
Windows.Win32.Storage.FileSystem.UnlockFile
|
||||||
Windows.Win32.Storage.FileSystem.VOLUME_NAME_DOS
|
Windows.Win32.Storage.FileSystem.VOLUME_NAME_DOS
|
||||||
Windows.Win32.Storage.FileSystem.VOLUME_NAME_GUID
|
Windows.Win32.Storage.FileSystem.VOLUME_NAME_GUID
|
||||||
Windows.Win32.Storage.FileSystem.VOLUME_NAME_NONE
|
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 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 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 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 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 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);
|
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 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 TryAcquireSRWLockExclusive(srwlock : *mut SRWLOCK) -> BOOLEAN);
|
||||||
windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockShared(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 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 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);
|
windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT);
|
||||||
@ -2730,6 +2732,9 @@ pub struct LINGER {
|
|||||||
pub l_onoff: u16,
|
pub l_onoff: u16,
|
||||||
pub l_linger: 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<
|
pub type LPOVERLAPPED_COMPLETION_ROUTINE = Option<
|
||||||
unsafe extern "system" fn(
|
unsafe extern "system" fn(
|
||||||
dwerrorcode: u32,
|
dwerrorcode: u32,
|
||||||
|
@ -346,6 +346,120 @@ impl File {
|
|||||||
self.fsync()
|
self.fsync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
flags,
|
||||||
|
0,
|
||||||
|
u32::MAX,
|
||||||
|
u32::MAX,
|
||||||
|
&mut overlapped,
|
||||||
|
));
|
||||||
|
|
||||||
|
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, and get the lock operation status.
|
||||||
|
// This can happen asynchronously, if the file handle was opened for async IO
|
||||||
|
let mut bytes_transferred = 0;
|
||||||
|
cvt(c::GetOverlappedResult(
|
||||||
|
self.handle.as_raw_handle(),
|
||||||
|
&mut overlapped,
|
||||||
|
&mut bytes_transferred,
|
||||||
|
c::TRUE,
|
||||||
|
))
|
||||||
|
.map(|_| ())
|
||||||
|
} 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<()> {
|
||||||
|
self.acquire_lock(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(err)
|
||||||
|
if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32)
|
||||||
|
|| err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) =>
|
||||||
|
{
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(err)
|
||||||
|
if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32)
|
||||||
|
|| err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) =>
|
||||||
|
{
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) });
|
||||||
|
match result {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(err) if err.raw_os_error() == Some(c::ERROR_NOT_LOCKED as i32) => Ok(()),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn truncate(&self, size: u64) -> io::Result<()> {
|
pub fn truncate(&self, size: u64) -> io::Result<()> {
|
||||||
let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 };
|
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()
|
api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
|
||||||
|
@ -82,7 +82,7 @@ pub(crate) fn get_clippy_rules_in_order(
|
|||||||
{
|
{
|
||||||
item.iter().for_each(|v| {
|
item.iter().for_each(|v| {
|
||||||
let rule = format!("{prefix}{v}");
|
let rule = format!("{prefix}{v}");
|
||||||
let position = all_args.iter().position(|t| t == &rule).unwrap();
|
let position = all_args.iter().position(|t| t == &rule || t == v).unwrap();
|
||||||
result.push((position, rule));
|
result.push((position, rule));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -323,6 +323,23 @@ fn order_of_clippy_rules() {
|
|||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clippy_rule_separate_prefix() {
|
||||||
|
let args =
|
||||||
|
vec!["clippy".to_string(), "-A clippy:all".to_string(), "-W clippy::style".to_string()];
|
||||||
|
let config = Config::parse(Flags::parse(&args));
|
||||||
|
|
||||||
|
let actual = match &config.cmd {
|
||||||
|
crate::Subcommand::Clippy { allow, deny, warn, forbid, .. } => {
|
||||||
|
get_clippy_rules_in_order(&args, &allow, &deny, &warn, &forbid)
|
||||||
|
}
|
||||||
|
_ => panic!("invalid subcommand"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected = vec!["-A clippy:all".to_string(), "-W clippy::style".to_string()];
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verbose_tests_default_value() {
|
fn verbose_tests_default_value() {
|
||||||
let config = Config::parse(Flags::parse(&["build".into(), "compiler".into()]));
|
let config = Config::parse(Flags::parse(&["build".into(), "compiler".into()]));
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
("Hello", 1) [(("Hello", 1),)] "Hello" "Hello" "Hello" ("Hello", 1) ("Hello", 1) ("Hello", 1)
|
@ -0,0 +1 @@
|
|||||||
|
("Hello", 1) [(("Hello", 1),)] "Hello" "Hello" "Hello" ("Hello", 1) ("Hello", 1) ("Hello", 1)
|
@ -1,4 +1,21 @@
|
|||||||
//@ check-pass
|
// This is a test for the new temporary lifetime behaviour as implemented for RFC 3606.
|
||||||
|
// In essence, with #3606 we can write the following variable initialisation without
|
||||||
|
// a borrow checking error because the temporary lifetime is automatically extended.
|
||||||
|
// ```rust
|
||||||
|
// let x = if condition() {
|
||||||
|
// &something()
|
||||||
|
// } else {
|
||||||
|
// &something_else()
|
||||||
|
// };
|
||||||
|
// ```
|
||||||
|
// More details can be found in https://github.com/rust-lang/rfcs/pull/3606
|
||||||
|
|
||||||
|
//@ run-pass
|
||||||
|
//@ check-run-results
|
||||||
|
//@ revisions: edition2021 edition2024
|
||||||
|
//@ [edition2021] edition: 2021
|
||||||
|
//@ [edition2024] edition: 2024
|
||||||
|
//@ [edition2024] compile-flags: -Z unstable-options
|
||||||
|
|
||||||
fn temp() -> (String, i32) {
|
fn temp() -> (String, i32) {
|
||||||
(String::from("Hello"), 1)
|
(String::from("Hello"), 1)
|
||||||
@ -13,11 +30,7 @@ fn main() {
|
|||||||
let _ = 123;
|
let _ = 123;
|
||||||
&(*temp().0)[..]
|
&(*temp().0)[..]
|
||||||
};
|
};
|
||||||
let f = if true {
|
let f = if true { &temp() } else { &temp() };
|
||||||
&temp()
|
|
||||||
} else {
|
|
||||||
&temp()
|
|
||||||
};
|
|
||||||
let g = match true {
|
let g = match true {
|
||||||
true => &temp(),
|
true => &temp(),
|
||||||
false => {
|
false => {
|
||||||
|
Loading…
Reference in New Issue
Block a user