Disk Buckets: cleanup api (is_free) (#22068)
This commit is contained in:
parent
37f6777ceb
commit
2be139ca61
@ -105,7 +105,7 @@ impl<T: Clone + Copy> Bucket<T> {
|
|||||||
pub fn keys(&self) -> Vec<Pubkey> {
|
pub fn keys(&self) -> Vec<Pubkey> {
|
||||||
let mut rv = vec![];
|
let mut rv = vec![];
|
||||||
for i in 0..self.index.capacity() {
|
for i in 0..self.index.capacity() {
|
||||||
if self.index.uid(i).is_none() {
|
if self.index.is_free(i) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let ix: &IndexEntry = self.index.get(i);
|
let ix: &IndexEntry = self.index.get(i);
|
||||||
@ -121,7 +121,7 @@ impl<T: Clone + Copy> Bucket<T> {
|
|||||||
let mut result = Vec::with_capacity(self.index.count.load(Ordering::Relaxed) as usize);
|
let mut result = Vec::with_capacity(self.index.count.load(Ordering::Relaxed) as usize);
|
||||||
for i in 0..self.index.capacity() {
|
for i in 0..self.index.capacity() {
|
||||||
let ii = i % self.index.capacity();
|
let ii = i % self.index.capacity();
|
||||||
if self.index.uid(ii).is_none() {
|
if self.index.is_free(ii) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let ix: &IndexEntry = self.index.get(ii);
|
let ix: &IndexEntry = self.index.get(ii);
|
||||||
@ -154,7 +154,7 @@ impl<T: Clone + Copy> Bucket<T> {
|
|||||||
let ix = Self::bucket_index_ix(index, key, random);
|
let ix = Self::bucket_index_ix(index, key, random);
|
||||||
for i in ix..ix + index.max_search() {
|
for i in ix..ix + index.max_search() {
|
||||||
let ii = i % index.capacity();
|
let ii = i % index.capacity();
|
||||||
if index.uid(ii).is_none() {
|
if index.is_free(ii) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let elem: &mut IndexEntry = index.get_mut(ii);
|
let elem: &mut IndexEntry = index.get_mut(ii);
|
||||||
@ -173,7 +173,7 @@ impl<T: Clone + Copy> Bucket<T> {
|
|||||||
let ix = Self::bucket_index_ix(index, key, random);
|
let ix = Self::bucket_index_ix(index, key, random);
|
||||||
for i in ix..ix + index.max_search() {
|
for i in ix..ix + index.max_search() {
|
||||||
let ii = i % index.capacity();
|
let ii = i % index.capacity();
|
||||||
if index.uid(ii).is_none() {
|
if index.is_free(ii) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let elem: &IndexEntry = index.get(ii);
|
let elem: &IndexEntry = index.get(ii);
|
||||||
@ -194,7 +194,7 @@ impl<T: Clone + Copy> Bucket<T> {
|
|||||||
let ix = Self::bucket_index_ix(index, key, random);
|
let ix = Self::bucket_index_ix(index, key, random);
|
||||||
for i in ix..ix + index.max_search() {
|
for i in ix..ix + index.max_search() {
|
||||||
let ii = i as u64 % index.capacity();
|
let ii = i as u64 % index.capacity();
|
||||||
if index.uid(ii).is_some() {
|
if !index.is_free(ii) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
index.allocate(ii, elem_uid, is_resizing).unwrap();
|
index.allocate(ii, elem_uid, is_resizing).unwrap();
|
||||||
@ -277,7 +277,7 @@ impl<T: Clone + Copy> Bucket<T> {
|
|||||||
let pos = thread_rng().gen_range(0, cap);
|
let pos = thread_rng().gen_range(0, cap);
|
||||||
for i in pos..pos + self.index.max_search() {
|
for i in pos..pos + self.index.max_search() {
|
||||||
let ix = i % cap;
|
let ix = i % cap;
|
||||||
if best_bucket.uid(ix).is_none() {
|
if best_bucket.is_free(ix) {
|
||||||
let elem_loc = elem.data_loc(current_bucket);
|
let elem_loc = elem.data_loc(current_bucket);
|
||||||
let old_slots = elem.num_slots;
|
let old_slots = elem.num_slots;
|
||||||
elem.set_storage_offset(ix);
|
elem.set_storage_offset(ix);
|
||||||
|
@ -45,6 +45,8 @@ struct Header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Header {
|
impl Header {
|
||||||
|
/// try to lock this entry with 'uid'
|
||||||
|
/// return true if it could be locked
|
||||||
fn try_lock(&mut self, uid: Uid) -> bool {
|
fn try_lock(&mut self, uid: Uid) -> bool {
|
||||||
if self.lock == UID_UNLOCKED {
|
if self.lock == UID_UNLOCKED {
|
||||||
self.lock = uid;
|
self.lock = uid;
|
||||||
@ -53,10 +55,12 @@ impl Header {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// mark this entry as unlocked
|
||||||
fn unlock(&mut self, expected: Uid) {
|
fn unlock(&mut self, expected: Uid) {
|
||||||
assert_eq!(expected, self.lock);
|
assert_eq!(expected, self.lock);
|
||||||
self.lock = UID_UNLOCKED;
|
self.lock = UID_UNLOCKED;
|
||||||
}
|
}
|
||||||
|
/// uid that has locked this entry or None if unlocked
|
||||||
fn uid(&self) -> Option<Uid> {
|
fn uid(&self) -> Option<Uid> {
|
||||||
if self.lock == UID_UNLOCKED {
|
if self.lock == UID_UNLOCKED {
|
||||||
None
|
None
|
||||||
@ -64,6 +68,10 @@ impl Header {
|
|||||||
Some(self.lock)
|
Some(self.lock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// true if this entry is unlocked
|
||||||
|
fn is_unlocked(&self) -> bool {
|
||||||
|
self.lock == UID_UNLOCKED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BucketStorage {
|
pub struct BucketStorage {
|
||||||
@ -153,11 +161,19 @@ impl BucketStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// return uid allocated at index 'ix' or None if vacant
|
||||||
pub fn uid(&self, ix: u64) -> Option<Uid> {
|
pub fn uid(&self, ix: u64) -> Option<Uid> {
|
||||||
assert!(ix < self.capacity(), "bad index size");
|
assert!(ix < self.capacity(), "bad index size");
|
||||||
self.header_ptr(ix).uid()
|
self.header_ptr(ix).uid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// true if the entry at index 'ix' is free (as opposed to being allocated)
|
||||||
|
pub fn is_free(&self, ix: u64) -> bool {
|
||||||
|
// note that the terminology in the implementation is locked or unlocked.
|
||||||
|
// but our api is allocate/free
|
||||||
|
self.header_ptr(ix).is_unlocked()
|
||||||
|
}
|
||||||
|
|
||||||
/// caller knows id is not empty
|
/// caller knows id is not empty
|
||||||
pub fn uid_unchecked(&self, ix: u64) -> Uid {
|
pub fn uid_unchecked(&self, ix: u64) -> Uid {
|
||||||
self.uid(ix).unwrap()
|
self.uid(ix).unwrap()
|
||||||
@ -366,3 +382,43 @@ impl BucketStorage {
|
|||||||
1 << self.capacity_pow2
|
1 << self.capacity_pow2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bucket_storage() {
|
||||||
|
let tmpdir1 = std::env::temp_dir().join("bucket_map_test_mt");
|
||||||
|
let paths: Vec<PathBuf> = [tmpdir1]
|
||||||
|
.iter()
|
||||||
|
.filter(|x| std::fs::create_dir_all(x).is_ok())
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
assert!(!paths.is_empty());
|
||||||
|
|
||||||
|
let mut storage =
|
||||||
|
BucketStorage::new(Arc::new(paths), 1, 1, 1, Arc::default(), Arc::default());
|
||||||
|
let ix = 0;
|
||||||
|
let uid = Uid::MAX;
|
||||||
|
assert!(storage.is_free(ix));
|
||||||
|
assert!(storage.allocate(ix, uid, false).is_ok());
|
||||||
|
assert!(storage.allocate(ix, uid, false).is_err());
|
||||||
|
assert!(!storage.is_free(ix));
|
||||||
|
assert_eq!(storage.uid(ix), Some(uid));
|
||||||
|
assert_eq!(storage.uid_unchecked(ix), uid);
|
||||||
|
storage.free(ix, uid);
|
||||||
|
assert!(storage.is_free(ix));
|
||||||
|
assert_eq!(storage.uid(ix), None);
|
||||||
|
let uid = 1;
|
||||||
|
assert!(storage.is_free(ix));
|
||||||
|
assert!(storage.allocate(ix, uid, false).is_ok());
|
||||||
|
assert!(storage.allocate(ix, uid, false).is_err());
|
||||||
|
assert!(!storage.is_free(ix));
|
||||||
|
assert_eq!(storage.uid(ix), Some(uid));
|
||||||
|
assert_eq!(storage.uid_unchecked(ix), uid);
|
||||||
|
storage.free(ix, uid);
|
||||||
|
assert!(storage.is_free(ix));
|
||||||
|
assert_eq!(storage.uid(ix), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user