mirror of
https://github.com/devmobasa/wayscriber.git
synced 2026-11-01 07:36:42 +01:00
refactor: take advisory locks through rustix
Add rustix as a direct dependency. It is already compiled through the Wayland stack, so this adds no new crate. Use its typed flock operations for the durable, daemon protocol, and action journal locks, which removes their unsafe blocks. Deadline locks still always retry without blocking.
This commit is contained in:
1 parent
d3b67cae56
commit
572bc3ba44
14 files changed
+96
-76
No files matched your search
Generated
+1
@@ -1894,6 +1894,7 @@ dependencies = [
|
||||
"pango",
|
||||
"pangocairo",
|
||||
"png",
|
||||
"rustix",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_ignored",
|
||||
|
||||
@@ -93,6 +93,8 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_ignored = "0.1"
|
||||
schemars = { version = "1.2", features = ["derive"], optional = true }
|
||||
libc = "0.2"
|
||||
# Safe wrappers for the kernel interfaces that the std library does not cover.
|
||||
rustix = { version = "1", features = ["event", "fs", "net", "pipe", "process", "stdio", "termios", "thread", "time"] }
|
||||
# Kernel random bytes for portal handle tokens and daemon protocol identifiers.
|
||||
getrandom = { version = "0.4", features = ["std"] }
|
||||
# Securely created, self-deleting temporary files for the OCR engine handoff.
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::{self, ErrorKind};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
|
||||
use std::path::{Path, PathBuf};
|
||||
#[cfg(test)]
|
||||
@@ -11,6 +10,7 @@ use std::sync::LazyLock;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use rustix::fs::{self as rustix_fs, FlockOperation};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -224,10 +224,13 @@ fn try_open_journal_lock(root: &Path, nonblocking: bool) -> Result<Option<File>>
|
||||
if !file.metadata()?.is_file() {
|
||||
bail!("action journal lock is not a regular file");
|
||||
}
|
||||
let operation = libc::LOCK_EX | if nonblocking { libc::LOCK_NB } else { 0 };
|
||||
// SAFETY: file owns the descriptor; flock retains no pointer.
|
||||
if unsafe { libc::flock(file.as_raw_fd(), operation) } != 0 {
|
||||
let error = io::Error::last_os_error();
|
||||
let operation = if nonblocking {
|
||||
FlockOperation::NonBlockingLockExclusive
|
||||
} else {
|
||||
FlockOperation::LockExclusive
|
||||
};
|
||||
if let Err(errno) = rustix_fs::flock(&file, operation) {
|
||||
let error = io::Error::from(errno);
|
||||
if nonblocking && error.kind() == ErrorKind::WouldBlock {
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -237,12 +240,9 @@ fn try_open_journal_lock(root: &Path, nonblocking: bool) -> Result<Option<File>>
|
||||
}
|
||||
|
||||
fn unlock(file: &File) -> Result<()> {
|
||||
// SAFETY: file owns the descriptor; flock retains no pointer.
|
||||
if unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_UN) } == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error()).context("failed to unlock action journal")
|
||||
}
|
||||
rustix_fs::flock(file, FlockOperation::Unlock)
|
||||
.map_err(io::Error::from)
|
||||
.context("failed to unlock action journal")
|
||||
}
|
||||
|
||||
fn write_record<T: Serialize>(path: &Path, value: &T) -> Result<()> {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::{env, thread};
|
||||
|
||||
use rustix::fs::{self as rustix_fs, FlockOperation};
|
||||
|
||||
use crate::{test_env, test_temp};
|
||||
|
||||
use super::*;
|
||||
@@ -179,21 +181,11 @@ fn event_loop_claim_and_finish_defer_instead_of_waiting_for_locks() {
|
||||
.write(true)
|
||||
.open(decision_path)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
// SAFETY: `held_decision` keeps the descriptor open for the call,
|
||||
// and flock retains no pointer.
|
||||
unsafe { libc::flock(held_decision.as_raw_fd(), libc::LOCK_EX) },
|
||||
0
|
||||
);
|
||||
rustix_fs::flock(&held_decision, FlockOperation::LockExclusive).unwrap();
|
||||
let ActionFinishOutcome::Deferred(action) = action.try_finish(true, None).unwrap() else {
|
||||
panic!("contended command finish should defer");
|
||||
};
|
||||
assert_eq!(
|
||||
// SAFETY: `held_decision` keeps the descriptor open for the call,
|
||||
// and flock retains no pointer.
|
||||
unsafe { libc::flock(held_decision.as_raw_fd(), libc::LOCK_UN) },
|
||||
0
|
||||
);
|
||||
rustix_fs::flock(&held_decision, FlockOperation::Unlock).unwrap();
|
||||
assert!(matches!(
|
||||
action.try_finish(true, None).unwrap(),
|
||||
ActionFinishOutcome::Complete
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::io::ErrorKind;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use rustix::fs::FlockOperation;
|
||||
|
||||
use super::super::BootClock;
|
||||
use super::super::action::PreparedAction;
|
||||
@@ -37,14 +38,14 @@ fn claim_command_action_inner(
|
||||
let path = control_path(&command_root(), command_identity);
|
||||
let decision = open_lock(&path.join("decision.lock"), false)?;
|
||||
if nonblocking {
|
||||
match flock(&decision, libc::LOCK_EX | libc::LOCK_NB) {
|
||||
match flock(&decision, FlockOperation::NonBlockingLockExclusive) {
|
||||
Ok(()) => {}
|
||||
Err(error) if error.kind() == ErrorKind::WouldBlock => return Ok(None),
|
||||
Err(error) => return Err(error).context("failed to try command-action decision lock"),
|
||||
}
|
||||
} else {
|
||||
let deadline = BootClock::now()?.checked_add(Duration::from_millis(200))?;
|
||||
lock_until(&decision, libc::LOCK_EX, deadline)?;
|
||||
lock_until(&decision, FlockOperation::LockExclusive, deadline)?;
|
||||
}
|
||||
let mut control = read_control(&path)?;
|
||||
let exact = |status: &ActionStatus| match status {
|
||||
@@ -193,14 +194,14 @@ fn finish_command_action_inner(
|
||||
let path = control_path(&command_root(), command_identity);
|
||||
let decision = open_lock(&path.join("decision.lock"), false)?;
|
||||
if nonblocking {
|
||||
match flock(&decision, libc::LOCK_EX | libc::LOCK_NB) {
|
||||
match flock(&decision, FlockOperation::NonBlockingLockExclusive) {
|
||||
Ok(()) => {}
|
||||
Err(error) if error.kind() == ErrorKind::WouldBlock => return Ok(false),
|
||||
Err(error) => return Err(error).context("failed to try command-action decision lock"),
|
||||
}
|
||||
} else {
|
||||
let deadline = BootClock::now()?.checked_add(Duration::from_millis(200))?;
|
||||
lock_until(&decision, libc::LOCK_EX, deadline)?;
|
||||
lock_until(&decision, FlockOperation::LockExclusive, deadline)?;
|
||||
}
|
||||
let mut control = read_control(&path)?;
|
||||
let applied = result == CommandActionResult::Applied;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Result, anyhow, bail};
|
||||
use rustix::fs::FlockOperation;
|
||||
|
||||
#[cfg(test)]
|
||||
use super::super::wire::CommandControl;
|
||||
@@ -61,7 +62,8 @@ impl ClaimedCommand {
|
||||
journal.prepare_command(&expected.identity, &expected.target_daemon_token, action)?;
|
||||
let deadline =
|
||||
BootDeadline::from_nanos(expected.submission_clock.authorization_deadline_boottime_ns);
|
||||
if let Err(error) = lock_until(&self.decision_lock, libc::LOCK_EX, deadline) {
|
||||
if let Err(error) = lock_until(&self.decision_lock, FlockOperation::LockExclusive, deadline)
|
||||
{
|
||||
let _ = journal.abandon(&prepared, "command decision lock was not reacquired");
|
||||
return Err(error);
|
||||
}
|
||||
@@ -71,7 +73,11 @@ impl ClaimedCommand {
|
||||
let reason = "command was canceled during action preparation";
|
||||
journal.abandon(&prepared, reason)?;
|
||||
let recovery_deadline = BootClock::now()?.checked_add(Duration::from_millis(200))?;
|
||||
lock_until(&self.decision_lock, libc::LOCK_EX, recovery_deadline)?;
|
||||
lock_until(
|
||||
&self.decision_lock,
|
||||
FlockOperation::LockExclusive,
|
||||
recovery_deadline,
|
||||
)?;
|
||||
let mut latest = read_control(&self.control_path)?;
|
||||
if latest != current
|
||||
|| !matches!(
|
||||
@@ -221,6 +227,6 @@ impl ClaimedCommand {
|
||||
|
||||
impl Drop for ClaimedCommand {
|
||||
fn drop(&mut self) {
|
||||
let _ = flock(&self.decision_lock, libc::LOCK_UN);
|
||||
let _ = flock(&self.decision_lock, FlockOperation::Unlock);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ use std::time::Duration;
|
||||
use std::{fs, process, thread};
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use rustix::fs::FlockOperation;
|
||||
|
||||
#[cfg(test)]
|
||||
use anyhow::anyhow;
|
||||
@@ -62,8 +63,16 @@ impl ClientCommand {
|
||||
create_private_directory(&stage)?;
|
||||
let decision_lock = open_lock(&stage.join("decision.lock"), true)?;
|
||||
let caller_lease = open_lock(&stage.join("caller.lease"), true)?;
|
||||
lock_until(&caller_lease, libc::LOCK_SH, authorization_deadline)?;
|
||||
lock_until(&decision_lock, libc::LOCK_EX, authorization_deadline)?;
|
||||
lock_until(
|
||||
&caller_lease,
|
||||
FlockOperation::LockShared,
|
||||
authorization_deadline,
|
||||
)?;
|
||||
lock_until(
|
||||
&decision_lock,
|
||||
FlockOperation::LockExclusive,
|
||||
authorization_deadline,
|
||||
)?;
|
||||
|
||||
let mut control = CommandControl {
|
||||
protocol_version: DAEMON_COMMAND_PROTOCOL_VERSION,
|
||||
@@ -154,7 +163,7 @@ impl ClientCommand {
|
||||
let slice = now
|
||||
.checked_add(Duration::from_millis(20))?
|
||||
.min(self.response_deadline);
|
||||
if !try_lock_until(&self.decision_lock, libc::LOCK_EX, slice)? {
|
||||
if !try_lock_until(&self.decision_lock, FlockOperation::LockExclusive, slice)? {
|
||||
if slice < self.response_deadline {
|
||||
continue;
|
||||
}
|
||||
@@ -201,7 +210,11 @@ impl ClientCommand {
|
||||
let _ = unlock(&self.caller_lease);
|
||||
return Ok(TerminalCommandResult::AdmittedIndeterminate(reason));
|
||||
}
|
||||
lock_until(&self.decision_lock, libc::LOCK_EX, self.response_deadline)?;
|
||||
lock_until(
|
||||
&self.decision_lock,
|
||||
FlockOperation::LockExclusive,
|
||||
self.response_deadline,
|
||||
)?;
|
||||
let mut control = read_control(&self.control_path)?;
|
||||
if cancel_open(&mut control)? {
|
||||
write_control(&self.control_path, &control)?;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::{self, ErrorKind};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use rustix::fs::{self as rustix_fs, FlockOperation};
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::durable_io::AtomicWriteOptions;
|
||||
@@ -279,22 +279,25 @@ pub(super) fn open_lock(path: &Path, create_new: bool) -> Result<File> {
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
pub(super) fn flock(file: &File, operation: libc::c_int) -> io::Result<()> {
|
||||
// SAFETY: file owns a valid descriptor and flock does not retain it.
|
||||
if unsafe { libc::flock(file.as_raw_fd(), operation) } == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
pub(super) fn flock(file: &File, operation: FlockOperation) -> io::Result<()> {
|
||||
rustix_fs::flock(file, operation).map_err(io::Error::from)
|
||||
}
|
||||
|
||||
fn nonblocking(operation: FlockOperation) -> FlockOperation {
|
||||
match operation {
|
||||
FlockOperation::LockShared => FlockOperation::NonBlockingLockShared,
|
||||
FlockOperation::LockExclusive => FlockOperation::NonBlockingLockExclusive,
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn unlock(file: &File) -> Result<()> {
|
||||
flock(file, libc::LOCK_UN).context("failed to unlock protocol descriptor")
|
||||
flock(file, FlockOperation::Unlock).context("failed to unlock protocol descriptor")
|
||||
}
|
||||
|
||||
pub(super) fn lock_until(
|
||||
file: &File,
|
||||
operation: libc::c_int,
|
||||
operation: FlockOperation,
|
||||
deadline: BootDeadline,
|
||||
) -> Result<()> {
|
||||
if try_lock_until(file, operation, deadline)? {
|
||||
@@ -306,11 +309,11 @@ pub(super) fn lock_until(
|
||||
|
||||
pub(super) fn try_lock_until(
|
||||
file: &File,
|
||||
operation: libc::c_int,
|
||||
operation: FlockOperation,
|
||||
deadline: BootDeadline,
|
||||
) -> Result<bool> {
|
||||
loop {
|
||||
match flock(file, operation | libc::LOCK_NB) {
|
||||
match flock(file, nonblocking(operation)) {
|
||||
Ok(()) => return Ok(true),
|
||||
Err(error) if error.kind() == ErrorKind::WouldBlock => {
|
||||
if BootClock::now()? >= deadline {
|
||||
@@ -341,7 +344,7 @@ pub(super) fn try_admission_lock(root: &Path, deadline: BootDeadline) -> Result<
|
||||
}
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
if try_lock_until(&file, libc::LOCK_EX, deadline)? {
|
||||
if try_lock_until(&file, FlockOperation::LockExclusive, deadline)? {
|
||||
Ok(Some(file))
|
||||
} else {
|
||||
Ok(None)
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use rustix::fs::FlockOperation;
|
||||
|
||||
use super::super::wire::{
|
||||
ActionStatus, CallerDisposition, ChildReportStatus, CommandDecision, CommandResponse,
|
||||
@@ -191,7 +192,11 @@ impl CommandOwner {
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let retry_deadline = BootClock::now()?.checked_add(Duration::from_millis(200))?;
|
||||
if !try_lock_until(&decision_lock, libc::LOCK_EX, retry_deadline)? {
|
||||
if !try_lock_until(
|
||||
&decision_lock,
|
||||
FlockOperation::LockExclusive,
|
||||
retry_deadline,
|
||||
)? {
|
||||
return Ok(None);
|
||||
}
|
||||
let Ok(mut control) = read_control(&path) else {
|
||||
@@ -316,7 +321,7 @@ impl CommandOwner {
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
let deadline = BootClock::now()?.checked_add(Duration::from_millis(200))?;
|
||||
lock_until(&decision, libc::LOCK_EX, deadline)?;
|
||||
lock_until(&decision, FlockOperation::LockExclusive, deadline)?;
|
||||
let mut control = read_control(&path)?;
|
||||
if control.identity == reference.command_identity
|
||||
&& control.queue_order == reference.queue_order
|
||||
@@ -341,13 +346,13 @@ impl CommandOwner {
|
||||
observed: &super::super::wire::CommandControl,
|
||||
) -> Result<()> {
|
||||
let caller_lease = open_lock(&path.join("caller.lease"), false)?;
|
||||
match flock(&caller_lease, libc::LOCK_EX | libc::LOCK_NB) {
|
||||
match flock(&caller_lease, FlockOperation::NonBlockingLockExclusive) {
|
||||
Ok(()) => {}
|
||||
Err(error) if error.kind() == ErrorKind::WouldBlock => return Ok(()),
|
||||
Err(error) => return Err(error).context("failed to inspect unqueued caller lease"),
|
||||
}
|
||||
let decision = open_lock(&path.join("decision.lock"), false)?;
|
||||
match flock(&decision, libc::LOCK_EX | libc::LOCK_NB) {
|
||||
match flock(&decision, FlockOperation::NonBlockingLockExclusive) {
|
||||
Ok(()) => {}
|
||||
Err(error) if error.kind() == ErrorKind::WouldBlock => {
|
||||
unlock(&caller_lease)?;
|
||||
@@ -390,7 +395,7 @@ impl CommandOwner {
|
||||
}
|
||||
let decision_lock = open_lock(&path.join("decision.lock"), false)?;
|
||||
let deadline = BootClock::now()?.checked_add(Duration::from_millis(200))?;
|
||||
if !try_lock_until(&decision_lock, libc::LOCK_EX, deadline)? {
|
||||
if !try_lock_until(&decision_lock, FlockOperation::LockExclusive, deadline)? {
|
||||
return Ok(None);
|
||||
}
|
||||
let mut control = read_control(&path)?;
|
||||
@@ -440,11 +445,11 @@ impl CommandOwner {
|
||||
for entry in read_dir_bounded(&controls_dir(&self.root), MAX_DIRECTORY_ENTRIES_PER_SCAN)? {
|
||||
let path = entry.path();
|
||||
let lease = open_lock(&path.join("caller.lease"), false)?;
|
||||
if flock(&lease, libc::LOCK_EX | libc::LOCK_NB).is_err() {
|
||||
if flock(&lease, FlockOperation::NonBlockingLockExclusive).is_err() {
|
||||
continue;
|
||||
}
|
||||
let decision = open_lock(&path.join("decision.lock"), false)?;
|
||||
if flock(&decision, libc::LOCK_EX | libc::LOCK_NB).is_err() {
|
||||
if flock(&decision, FlockOperation::NonBlockingLockExclusive).is_err() {
|
||||
unlock(&lease)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::path::Path;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Result, anyhow, bail};
|
||||
use rustix::fs::FlockOperation;
|
||||
|
||||
use super::super::BootClock;
|
||||
use super::super::wire::{
|
||||
@@ -32,7 +33,7 @@ pub(super) fn recover_previous_generation(root: &Path, daemon_token: &str) -> Re
|
||||
continue;
|
||||
};
|
||||
let deadline = BootClock::now()?.checked_add(Duration::from_millis(200))?;
|
||||
lock_until(&decision_lock, libc::LOCK_EX, deadline)?;
|
||||
lock_until(&decision_lock, FlockOperation::LockExclusive, deadline)?;
|
||||
let Ok(mut control) = read_control(&path) else {
|
||||
unlock(&decision_lock)?;
|
||||
quarantine_entry(root, &path, QuarantineKind::Control)?;
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::path::Path;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use rustix::fs::FlockOperation;
|
||||
|
||||
use super::super::{BootClock, BootIdentity};
|
||||
use super::MAX_COMMAND_STAGING_DIRECTORIES;
|
||||
@@ -24,7 +25,7 @@ pub(super) fn recover_staging(root: &Path) -> Result<()> {
|
||||
let _ = fs::remove_dir_all(path);
|
||||
continue;
|
||||
};
|
||||
if flock(&lease, libc::LOCK_EX | libc::LOCK_NB).is_ok() {
|
||||
if flock(&lease, FlockOperation::NonBlockingLockExclusive).is_ok() {
|
||||
fs::remove_dir_all(path)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::env;
|
||||
use std::time::Duration;
|
||||
|
||||
use rustix::fs::FlockOperation;
|
||||
|
||||
use crate::{test_env, test_temp};
|
||||
|
||||
use super::super::BootClock;
|
||||
@@ -208,7 +210,7 @@ fn contended_published_command_claim_is_deferred_and_retried() {
|
||||
&token,
|
||||
)
|
||||
.unwrap();
|
||||
flock(&client.decision_lock, libc::LOCK_EX).unwrap();
|
||||
flock(&client.decision_lock, FlockOperation::LockExclusive).unwrap();
|
||||
|
||||
assert!(owner.claim_next().unwrap().is_none());
|
||||
let now = BootClock::now().unwrap();
|
||||
@@ -252,7 +254,7 @@ fn contended_ref_less_recovery_is_deferred_and_retried() {
|
||||
&control.identity,
|
||||
))
|
||||
.unwrap();
|
||||
flock(&client.decision_lock, libc::LOCK_EX).unwrap();
|
||||
flock(&client.decision_lock, FlockOperation::LockExclusive).unwrap();
|
||||
|
||||
assert!(owner.claim_next().unwrap().is_none());
|
||||
unlock(&client.decision_lock).unwrap();
|
||||
@@ -287,7 +289,7 @@ fn terminal_collection_defers_while_admission_is_busy() {
|
||||
drop(client);
|
||||
|
||||
let admission = open_lock(&owner.root.join("admission.lock"), false).unwrap();
|
||||
flock(&admission, libc::LOCK_EX).unwrap();
|
||||
flock(&admission, FlockOperation::LockExclusive).unwrap();
|
||||
assert_eq!(owner.collect_terminal().unwrap(), 0);
|
||||
let now = BootClock::now().unwrap();
|
||||
let retry = owner
|
||||
@@ -715,20 +717,20 @@ fn lock_deadline_is_reported_without_parsing_error_text() {
|
||||
let path = root.join("structured-timeout.lock");
|
||||
let held = open_lock(&path, true).unwrap();
|
||||
let contender = open_lock(&path, false).unwrap();
|
||||
flock(&held, libc::LOCK_EX).unwrap();
|
||||
flock(&held, FlockOperation::LockExclusive).unwrap();
|
||||
|
||||
let deadline = BootClock::now()
|
||||
.unwrap()
|
||||
.checked_add(Duration::from_millis(5))
|
||||
.unwrap();
|
||||
assert!(!try_lock_until(&contender, libc::LOCK_EX, deadline).unwrap());
|
||||
assert!(!try_lock_until(&contender, FlockOperation::LockExclusive, deadline).unwrap());
|
||||
|
||||
unlock(&held).unwrap();
|
||||
let deadline = BootClock::now()
|
||||
.unwrap()
|
||||
.checked_add(Duration::from_millis(5))
|
||||
.unwrap();
|
||||
assert!(try_lock_until(&contender, libc::LOCK_EX, deadline).unwrap());
|
||||
assert!(try_lock_until(&contender, FlockOperation::LockExclusive, deadline).unwrap());
|
||||
unlock(&contender).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
+7
-15
@@ -4,19 +4,11 @@ use std::fs::File;
|
||||
use std::io;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use rustix::fs::{self as rustix_fs, FlockOperation};
|
||||
|
||||
#[cfg(unix)]
|
||||
fn flock(file: &File, op: libc::c_int) -> io::Result<()> {
|
||||
let fd = file.as_raw_fd();
|
||||
// SAFETY: `fd` stays open for the borrowed `file`, and flock takes no
|
||||
// pointers or ownership of the descriptor.
|
||||
let result = unsafe { libc::flock(fd, op) };
|
||||
if result == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
fn flock(file: &File, operation: FlockOperation) -> io::Result<()> {
|
||||
rustix_fs::flock(file, operation).map_err(io::Error::from)
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
@@ -30,7 +22,7 @@ fn flock(_file: &File, _op: i32) -> io::Result<()> {
|
||||
pub fn lock_shared(file: &File) -> io::Result<()> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
flock(file, libc::LOCK_SH)
|
||||
flock(file, FlockOperation::LockShared)
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
@@ -41,7 +33,7 @@ pub fn lock_shared(file: &File) -> io::Result<()> {
|
||||
pub fn lock_exclusive(file: &File) -> io::Result<()> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
flock(file, libc::LOCK_EX)
|
||||
flock(file, FlockOperation::LockExclusive)
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
@@ -52,7 +44,7 @@ pub fn lock_exclusive(file: &File) -> io::Result<()> {
|
||||
pub fn try_lock_exclusive(file: &File) -> io::Result<()> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
flock(file, libc::LOCK_EX | libc::LOCK_NB)
|
||||
flock(file, FlockOperation::NonBlockingLockExclusive)
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
@@ -63,7 +55,7 @@ pub fn try_lock_exclusive(file: &File) -> io::Result<()> {
|
||||
pub fn unlock(file: &File) -> io::Result<()> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
flock(file, libc::LOCK_UN)
|
||||
flock(file, FlockOperation::Unlock)
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
|
||||
@@ -340,6 +340,7 @@ internal static class ChecksCommand
|
||||
["pango"] = ["pango"],
|
||||
["pangocairo"] = ["pango", "cairo"],
|
||||
["png"] = [],
|
||||
["rustix"] = [],
|
||||
["schemars"] = [],
|
||||
["serde"] = [],
|
||||
["serde_ignored"] = [],
|
||||
|
||||
Reference in new issue
Block a user