mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 09:53:24 +00:00
Fix range_lock and flock to support signal interrupts and comply with wait API design
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
11382524d1
commit
7fbe997bb3
@ -2,7 +2,7 @@
|
|||||||
use alloc::fmt;
|
use alloc::fmt;
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
use ostd::sync::{WaitQueue, Waiter};
|
use ostd::sync::{WaitQueue, Waiter, Waker};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
fs::{file_handle::FileLike, inode_handle::InodeHandle},
|
fs::{file_handle::FileLike, inode_handle::InodeHandle},
|
||||||
@ -96,49 +96,65 @@ impl Debug for FlockItem {
|
|||||||
/// Represents a list of non-POSIX file advisory locks (FLOCK).
|
/// Represents a list of non-POSIX file advisory locks (FLOCK).
|
||||||
/// The list is used to manage file locks and resolve conflicts between them.
|
/// The list is used to manage file locks and resolve conflicts between them.
|
||||||
pub struct FlockList {
|
pub struct FlockList {
|
||||||
inner: Mutex<VecDeque<FlockItem>>,
|
inner: Mutex<Vec<FlockItem>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlockList {
|
impl FlockList {
|
||||||
/// Creates a new FlockList.
|
/// Creates a new FlockList.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Mutex::new(VecDeque::new()),
|
inner: Mutex::new(Vec::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to set a lock on the file.
|
/// Attempts to set a lock on the file.
|
||||||
/// If no conflicting locks exist, the lock is set and the function returns Ok(()).
|
///
|
||||||
/// If is_nonblocking is true and a conflicting lock exists, the function returns EAGAIN.
|
/// If no conflicting locks exist, the lock is set and the function returns `Ok(())`.
|
||||||
/// Otherwise, the function waits until the lock can be acquired.
|
/// If a conflicting lock exists:
|
||||||
pub fn set_lock(&self, mut req_lock: FlockItem, is_nonblocking: bool) -> Result<()> {
|
/// - If waker is not `None`, it is added to the conflicting lock's waitqueue, and the function returns `EAGAIN`.
|
||||||
|
/// - If waker is `None`, the function returns `EAGAIN`.
|
||||||
|
fn try_set_lock(&self, req_lock: &FlockItem, waker: Option<&Arc<Waker>>) -> Result<()> {
|
||||||
|
let mut list = self.inner.lock();
|
||||||
|
if let Some(conflict_lock) = list.iter().find(|l| req_lock.conflict_with(l)) {
|
||||||
|
if let Some(waker) = waker {
|
||||||
|
conflict_lock.waitqueue.enqueue(waker.clone());
|
||||||
|
}
|
||||||
|
return_errno_with_message!(Errno::EAGAIN, "the file is locked");
|
||||||
|
} else {
|
||||||
|
match list.iter().position(|l| req_lock.same_owner_with(l)) {
|
||||||
|
Some(idx) => {
|
||||||
|
list[idx] = req_lock.clone();
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
list.push(req_lock.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a lock on the file.
|
||||||
|
///
|
||||||
|
/// If no conflicting locks exist, the lock is set and the function returns `Ok(())`.
|
||||||
|
/// If is_nonblocking is true and a conflicting lock exists, the function returns `EAGAIN`.
|
||||||
|
/// Otherwise, the function waits until the lock can be acquired or until it is interrupted by a signal.
|
||||||
|
pub fn set_lock(&self, req_lock: FlockItem, is_nonblocking: bool) -> Result<()> {
|
||||||
debug!(
|
debug!(
|
||||||
"set_lock with Flock: {:?}, is_nonblocking: {}",
|
"set_lock with Flock: {:?}, is_nonblocking: {}",
|
||||||
req_lock, is_nonblocking
|
req_lock, is_nonblocking
|
||||||
);
|
);
|
||||||
loop {
|
if is_nonblocking {
|
||||||
let (waiter, waker);
|
self.try_set_lock(&req_lock, None)
|
||||||
{
|
} else {
|
||||||
let mut list = self.inner.lock();
|
let (waiter, waker) = Waiter::new_pair();
|
||||||
if let Some(existing_lock) = list.iter().find(|l| req_lock.conflict_with(l)) {
|
waiter.pause_until(|| {
|
||||||
if is_nonblocking {
|
let result = self.try_set_lock(&req_lock, Some(&waker));
|
||||||
return_errno_with_message!(Errno::EAGAIN, "the file is locked");
|
if result.is_err_and(|err| err.error() == Errno::EAGAIN) {
|
||||||
}
|
None
|
||||||
(waiter, waker) = Waiter::new_pair();
|
|
||||||
existing_lock.waitqueue.enqueue(waker);
|
|
||||||
} else {
|
} else {
|
||||||
match list.iter().position(|l| req_lock.same_owner_with(l)) {
|
Some(result)
|
||||||
Some(idx) => {
|
|
||||||
core::mem::swap(&mut req_lock, &mut list[idx]);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
list.push_front(req_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
}
|
})?
|
||||||
waiter.wait();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
use ostd::sync::{RwMutexWriteGuard, WaitQueue, Waiter};
|
use ostd::sync::{RwMutexWriteGuard, WaitQueue, Waiter, Waker};
|
||||||
|
|
||||||
use self::range::FileRangeChange;
|
use self::range::FileRangeChange;
|
||||||
pub use self::{
|
pub use self::{
|
||||||
@ -189,13 +189,13 @@ impl Clone for RangeLockItem {
|
|||||||
/// New locks with different type will replace or split the overlapping locks
|
/// New locks with different type will replace or split the overlapping locks
|
||||||
/// if they have same owner.
|
/// if they have same owner.
|
||||||
pub struct RangeLockList {
|
pub struct RangeLockList {
|
||||||
inner: RwMutex<VecDeque<RangeLockItem>>,
|
inner: RwMutex<Vec<RangeLockItem>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RangeLockList {
|
impl RangeLockList {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: RwMutex::new(VecDeque::new()),
|
inner: RwMutex::new(Vec::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,46 +219,59 @@ impl RangeLockList {
|
|||||||
req_lock
|
req_lock
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a lock on the file.
|
/// Attempts to set a lock on the file.
|
||||||
|
///
|
||||||
|
/// If no conflicting locks exist, the lock is set and the function returns `Ok(())`.
|
||||||
|
/// If a conflicting lock exists:
|
||||||
|
/// - If waker is not `None`, it is added to the conflicting lock's waitqueue, and the function returns `EAGAIN`.
|
||||||
|
/// - If waker is `None`, the function returns `EAGAIN`.
|
||||||
|
fn try_set_lock(&self, req_lock: &RangeLockItem, waker: Option<&Arc<Waker>>) -> Result<()> {
|
||||||
|
let mut list = self.inner.write();
|
||||||
|
if let Some(conflict_lock) = list.iter().find(|l| req_lock.conflict_with(l)) {
|
||||||
|
if let Some(waker) = waker {
|
||||||
|
conflict_lock.waitqueue.enqueue(waker.clone());
|
||||||
|
}
|
||||||
|
return_errno_with_message!(Errno::EAGAIN, "the file is locked");
|
||||||
|
} else {
|
||||||
|
Self::insert_lock_into_list(&mut list, req_lock);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a lock on the file.
|
||||||
///
|
///
|
||||||
/// If the lock is non-blocking and there is a conflict, return `Err(Errno::EAGAIN)`.
|
/// If the lock is non-blocking and there is a conflict, return `Err(Errno::EAGAIN)`.
|
||||||
/// Otherwise, block the current process until the lock can be set.
|
/// Otherwise, block the current process until the lock can be set or it is interrupted by a signal.
|
||||||
pub fn set_lock(&self, req_lock: &RangeLockItem, is_nonblocking: bool) -> Result<()> {
|
pub fn set_lock(&self, req_lock: &RangeLockItem, is_nonblocking: bool) -> Result<()> {
|
||||||
debug!(
|
debug!(
|
||||||
"set_lock with RangeLock: {:?}, is_nonblocking: {}",
|
"set_lock with RangeLock: {:?}, is_nonblocking: {}",
|
||||||
req_lock, is_nonblocking
|
req_lock, is_nonblocking
|
||||||
);
|
);
|
||||||
loop {
|
if is_nonblocking {
|
||||||
let (waiter, waker);
|
self.try_set_lock(req_lock, None)
|
||||||
|
} else {
|
||||||
{
|
let (waiter, waker) = Waiter::new_pair();
|
||||||
let mut list = self.inner.write();
|
waiter.pause_until(|| {
|
||||||
if let Some(existing_lock) = list.iter().find(|l| req_lock.conflict_with(l)) {
|
let result = self.try_set_lock(req_lock, Some(&waker));
|
||||||
if is_nonblocking {
|
if result.is_err_and(|err| err.error() == Errno::EAGAIN) {
|
||||||
return_errno_with_message!(Errno::EAGAIN, "the file is locked");
|
None
|
||||||
}
|
|
||||||
(waiter, waker) = Waiter::new_pair();
|
|
||||||
existing_lock.waitqueue.enqueue(waker);
|
|
||||||
} else {
|
} else {
|
||||||
Self::insert_lock_into_list(&mut list, req_lock);
|
Some(result)
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
}
|
})?
|
||||||
|
|
||||||
waiter.wait();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a lock into the list.
|
/// Insert a lock into the list.
|
||||||
fn insert_lock_into_list(
|
fn insert_lock_into_list(
|
||||||
list: &mut RwMutexWriteGuard<VecDeque<RangeLockItem>>,
|
list: &mut RwMutexWriteGuard<Vec<RangeLockItem>>,
|
||||||
lock: &RangeLockItem,
|
lock: &RangeLockItem,
|
||||||
) {
|
) {
|
||||||
let first_same_owner_idx = match list.iter().position(|lk| lk.owner() == lock.owner()) {
|
let first_same_owner_idx = match list.iter().position(|lk| lk.owner() == lock.owner()) {
|
||||||
Some(idx) => idx,
|
Some(idx) => idx,
|
||||||
None => {
|
None => {
|
||||||
// Can't find existing locks with same owner.
|
// Can't find existing locks with same owner.
|
||||||
list.push_front(lock.clone());
|
list.push(lock.clone());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -273,8 +286,10 @@ impl RangeLockList {
|
|||||||
if next_idx >= list.len() {
|
if next_idx >= list.len() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let pre_lock = list[pre_idx].clone();
|
|
||||||
let next_lock = list[next_idx].clone();
|
let (left, right) = list.split_at_mut(next_idx);
|
||||||
|
let pre_lock = &mut left[pre_idx];
|
||||||
|
let next_lock = &mut right[0];
|
||||||
|
|
||||||
if next_lock.owner() != pre_lock.owner() {
|
if next_lock.owner() != pre_lock.owner() {
|
||||||
break;
|
break;
|
||||||
@ -289,7 +304,7 @@ impl RangeLockList {
|
|||||||
next_idx += 1;
|
next_idx += 1;
|
||||||
} else {
|
} else {
|
||||||
// Merge adjacent or overlapping locks
|
// Merge adjacent or overlapping locks
|
||||||
list[next_idx].merge_with(&pre_lock);
|
next_lock.merge_with(pre_lock);
|
||||||
list.remove(pre_idx);
|
list.remove(pre_idx);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -302,10 +317,10 @@ impl RangeLockList {
|
|||||||
next_idx += 1;
|
next_idx += 1;
|
||||||
} else {
|
} else {
|
||||||
// Split overlapping locks
|
// Split overlapping locks
|
||||||
let overlap_with = pre_lock.overlap_with(&next_lock).unwrap();
|
let overlap_with = pre_lock.overlap_with(next_lock).unwrap();
|
||||||
match overlap_with {
|
match overlap_with {
|
||||||
OverlapWith::ToLeft => {
|
OverlapWith::ToLeft => {
|
||||||
list[next_idx].set_start(pre_lock.end());
|
next_lock.set_start(pre_lock.end());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
OverlapWith::InMiddle => {
|
OverlapWith::InMiddle => {
|
||||||
@ -314,13 +329,13 @@ impl RangeLockList {
|
|||||||
r_lk.set_start(pre_lock.end());
|
r_lk.set_start(pre_lock.end());
|
||||||
r_lk
|
r_lk
|
||||||
};
|
};
|
||||||
list[next_idx].set_end(pre_lock.start());
|
next_lock.set_end(pre_lock.start());
|
||||||
list.swap(pre_idx, next_idx);
|
list.swap(pre_idx, next_idx);
|
||||||
list.insert(next_idx + 1, right_lk);
|
list.insert(next_idx + 1, right_lk);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
OverlapWith::ToRight => {
|
OverlapWith::ToRight => {
|
||||||
list[next_idx].set_end(pre_lock.start());
|
next_lock.set_end(pre_lock.start());
|
||||||
list.swap(pre_idx, next_idx);
|
list.swap(pre_idx, next_idx);
|
||||||
pre_idx += 1;
|
pre_idx += 1;
|
||||||
next_idx += 1;
|
next_idx += 1;
|
||||||
|
Reference in New Issue
Block a user