freelist.rs (7682B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 //! A generic backing store for caches. 6 //! 7 //! `FreeList` is a simple vector-backed data structure where each entry in the 8 //! vector contains an Option<T>. It maintains an index-based (rather than 9 //! pointer-based) free list to efficiently locate the next unused entry. If all 10 //! entries are occupied, insertion appends a new element to the vector. 11 //! 12 //! It also supports both strong and weak handle semantics. There is exactly one 13 //! (non-Clonable) strong handle per occupied entry, which must be passed by 14 //! value into `free()` to release an entry. Strong handles can produce an 15 //! unlimited number of (Clonable) weak handles, which are used to perform 16 //! lookups which may fail of the entry has been freed. A per-entry epoch ensures 17 //! that weak handle lookups properly fail even if the entry has been freed and 18 //! reused. 19 //! 20 //! TODO(gw): Add an occupied list head, for fast iteration of the occupied list 21 //! to implement retain() style functionality. 22 23 use std::{fmt, u32}; 24 use std::marker::PhantomData; 25 26 #[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)] 27 #[cfg_attr(feature = "capture", derive(Serialize))] 28 #[cfg_attr(feature = "replay", derive(Deserialize))] 29 struct Epoch(u32); 30 31 impl Epoch { 32 /// Mints a new epoch. 33 /// 34 /// We start at 1 so that 0 is always an invalid epoch. 35 fn new() -> Self { 36 Epoch(1) 37 } 38 39 /// Returns an always-invalid epoch. 40 fn invalid() -> Self { 41 Epoch(0) 42 } 43 } 44 45 #[cfg_attr(feature = "capture", derive(Serialize))] 46 #[cfg_attr(feature = "replay", derive(Deserialize))] 47 #[derive(MallocSizeOf)] 48 pub struct FreeListHandle<M> { 49 index: u32, 50 epoch: Epoch, 51 _marker: PhantomData<M>, 52 } 53 54 /// More-compact textual representation for debug logging. 55 impl<M> fmt::Debug for FreeListHandle<M> { 56 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 57 f.debug_struct("StrongHandle") 58 .field("index", &self.index) 59 .field("epoch", &self.epoch.0) 60 .finish() 61 } 62 } 63 64 impl<M> FreeListHandle<M> { 65 pub fn weak(&self) -> WeakFreeListHandle<M> { 66 WeakFreeListHandle { 67 index: self.index, 68 epoch: self.epoch, 69 _marker: PhantomData, 70 } 71 } 72 73 pub fn invalid() -> Self { 74 Self { 75 index: 0, 76 epoch: Epoch::invalid(), 77 _marker: PhantomData, 78 } 79 } 80 81 /// Returns true if this handle and the supplied weak handle reference 82 /// the same underlying location in the freelist. 83 pub fn matches(&self, weak_handle: &WeakFreeListHandle<M>) -> bool { 84 self.index == weak_handle.index && 85 self.epoch == weak_handle.epoch 86 } 87 } 88 89 impl<M> Clone for WeakFreeListHandle<M> { 90 fn clone(&self) -> Self { 91 WeakFreeListHandle { 92 index: self.index, 93 epoch: self.epoch, 94 _marker: PhantomData, 95 } 96 } 97 } 98 99 impl<M> PartialEq for WeakFreeListHandle<M> { 100 fn eq(&self, other: &Self) -> bool { 101 self.index == other.index && self.epoch == other.epoch 102 } 103 } 104 105 #[cfg_attr(feature = "capture", derive(Serialize))] 106 #[cfg_attr(feature = "replay", derive(Deserialize))] 107 #[derive(MallocSizeOf)] 108 pub struct WeakFreeListHandle<M> { 109 index: u32, 110 epoch: Epoch, 111 _marker: PhantomData<M>, 112 } 113 114 /// More-compact textual representation for debug logging. 115 impl<M> fmt::Debug for WeakFreeListHandle<M> { 116 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 117 f.debug_struct("WeakHandle") 118 .field("index", &self.index) 119 .field("epoch", &self.epoch.0) 120 .finish() 121 } 122 } 123 124 impl<M> WeakFreeListHandle<M> { 125 /// Returns an always-invalid handle. 126 pub fn invalid() -> Self { 127 Self { 128 index: 0, 129 epoch: Epoch::invalid(), 130 _marker: PhantomData, 131 } 132 } 133 } 134 135 #[derive(Debug, MallocSizeOf)] 136 #[cfg_attr(feature = "capture", derive(Serialize))] 137 #[cfg_attr(feature = "replay", derive(Deserialize))] 138 struct Slot<T> { 139 next: Option<u32>, 140 epoch: Epoch, 141 value: Option<T>, 142 } 143 144 #[derive(Debug, MallocSizeOf)] 145 #[cfg_attr(feature = "capture", derive(Serialize))] 146 #[cfg_attr(feature = "replay", derive(Deserialize))] 147 pub struct FreeList<T, M> { 148 slots: Vec<Slot<T>>, 149 free_list_head: Option<u32>, 150 active_count: usize, 151 _marker: PhantomData<M>, 152 } 153 154 impl<T, M> FreeList<T, M> { 155 /// Mints a new `FreeList` with no entries. 156 /// 157 /// Triggers a 1-entry allocation. 158 pub fn new() -> Self { 159 // We guarantee that we never have zero entries by starting with one 160 // free entry. This allows WeakFreeListHandle::invalid() to work 161 // without adding any additional branches. 162 let first_slot = Slot { 163 next: None, 164 epoch: Epoch::new(), 165 value: None, 166 }; 167 FreeList { 168 slots: vec![first_slot], 169 free_list_head: Some(0), 170 active_count: 0, 171 _marker: PhantomData, 172 } 173 } 174 175 pub fn clear(&mut self) { 176 self.slots.truncate(1); 177 self.slots[0] = Slot { 178 next: None, 179 epoch: Epoch::new(), 180 value: None, 181 }; 182 self.free_list_head = Some(0); 183 self.active_count = 0; 184 } 185 186 #[allow(dead_code)] 187 pub fn get(&self, id: &FreeListHandle<M>) -> &T { 188 self.slots[id.index as usize].value.as_ref().unwrap() 189 } 190 191 #[allow(dead_code)] 192 pub fn get_mut(&mut self, id: &FreeListHandle<M>) -> &mut T { 193 self.slots[id.index as usize].value.as_mut().unwrap() 194 } 195 196 pub fn get_opt(&self, id: &WeakFreeListHandle<M>) -> Option<&T> { 197 let slot = &self.slots[id.index as usize]; 198 if slot.epoch == id.epoch { 199 slot.value.as_ref() 200 } else { 201 None 202 } 203 } 204 205 pub fn get_opt_mut(&mut self, id: &WeakFreeListHandle<M>) -> Option<&mut T> { 206 let slot = &mut self.slots[id.index as usize]; 207 if slot.epoch == id.epoch { 208 slot.value.as_mut() 209 } else { 210 None 211 } 212 } 213 214 pub fn insert(&mut self, item: T) -> FreeListHandle<M> { 215 self.active_count += 1; 216 217 match self.free_list_head { 218 Some(free_index) => { 219 let slot = &mut self.slots[free_index as usize]; 220 221 // Remove from free list. 222 self.free_list_head = slot.next; 223 slot.next = None; 224 slot.value = Some(item); 225 226 FreeListHandle { 227 index: free_index, 228 epoch: slot.epoch, 229 _marker: PhantomData, 230 } 231 } 232 None => { 233 let index = self.slots.len() as u32; 234 let epoch = Epoch::new(); 235 236 self.slots.push(Slot { 237 next: None, 238 epoch, 239 value: Some(item), 240 }); 241 242 FreeListHandle { 243 index, 244 epoch, 245 _marker: PhantomData, 246 } 247 } 248 } 249 } 250 251 pub fn free(&mut self, id: FreeListHandle<M>) -> T { 252 self.active_count -= 1; 253 let slot = &mut self.slots[id.index as usize]; 254 slot.next = self.free_list_head; 255 slot.epoch = Epoch(slot.epoch.0 + 1); 256 self.free_list_head = Some(id.index); 257 slot.value.take().unwrap() 258 } 259 260 #[allow(dead_code)] 261 pub fn len(&self) -> usize { 262 self.active_count 263 } 264 }