forked from openzfs/zfs
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Because we can have concurrent "read block" requests for the same block, we can also have concurrent accesses to the same block of the zettacache. In particular, we may have 2 "read block"s of the same block, each of which will lookup() in the zettacache, find the entry not there, and then insert() to the zettacache. This can lead to several problems, especially in subtle cases like where one of the insert()'s has been flushed to the index before the 2nd insert() is called. This PR solves this issue by adding per-block locking to the zettacache. A failed lookup() will return with the entry locked, blocking concurrent lookup()'s of the same block until the first thread either insert()'s the value or drop()'s the LockedKey.
- Loading branch information
Showing
5 changed files
with
115 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
use log::*; | ||
use std::fmt::Debug; | ||
use std::{ | ||
collections::{hash_map, HashMap}, | ||
hash::Hash, | ||
sync::{Arc, Mutex}, | ||
}; | ||
use tokio::sync::watch; | ||
|
||
#[derive(Default, Debug, Clone)] | ||
pub struct LockSet<V: Hash + Eq + Copy + Debug> { | ||
locks: Arc<Mutex<HashMap<V, watch::Receiver<()>>>>, | ||
} | ||
|
||
pub struct LockedItem<V: Hash + Eq + Copy + Debug> { | ||
value: V, | ||
tx: watch::Sender<()>, | ||
set: LockSet<V>, | ||
} | ||
|
||
impl<V: Hash + Eq + Copy + Debug> Drop for LockedItem<V> { | ||
fn drop(&mut self) { | ||
trace!("{:?}: removing lock", self.value); | ||
let rx = self.set.locks.lock().unwrap().remove(&self.value); | ||
assert!(rx.is_some()); | ||
// This unwrap can't fail because there is still a receiver, `rx`. | ||
self.tx.send(()).unwrap(); | ||
} | ||
} | ||
|
||
impl<V: Hash + Eq + Copy + Debug> LockedItem<V> { | ||
pub fn value(&self) -> &V { | ||
&self.value | ||
} | ||
} | ||
|
||
impl<V: Hash + Eq + Copy + Debug> LockSet<V> { | ||
pub fn new() -> Self { | ||
Self { | ||
locks: Default::default(), | ||
} | ||
} | ||
|
||
pub async fn lock(&self, value: V) -> LockedItem<V> { | ||
let tx = loop { | ||
let mut rx = { | ||
match self.locks.lock().unwrap().entry(value) { | ||
hash_map::Entry::Occupied(oe) => oe.get().clone(), | ||
hash_map::Entry::Vacant(ve) => { | ||
let (tx, rx) = watch::channel(()); | ||
ve.insert(rx); | ||
break tx; | ||
} | ||
} | ||
}; | ||
trace!("{:?}: waiting for existing lock", value); | ||
// Note: since we don't hold the locks mutex now, the corresponding | ||
// LockedItem may have been dropped, in which case the sender was | ||
// dropped. In this case, the changed() Result will be an Err, | ||
// which we ignore with ok(). | ||
rx.changed().await.ok(); | ||
}; | ||
trace!("{:?}: inserted new lock", value); | ||
|
||
LockedItem { | ||
value, | ||
tx, | ||
set: self.clone(), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters