webrender/
freelist.rs

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
23use std::{fmt, u32};
24use 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))]
29struct Epoch(u32);
30
31impl 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)]
48pub struct FreeListHandle<M> {
49    index: u32,
50    epoch: Epoch,
51    _marker: PhantomData<M>,
52}
53
54/// More-compact textual representation for debug logging.
55impl<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
64impl<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
89impl<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
99impl<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)]
108pub struct WeakFreeListHandle<M> {
109    index: u32,
110    epoch: Epoch,
111    _marker: PhantomData<M>,
112}
113
114/// More-compact textual representation for debug logging.
115impl<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
124impl<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))]
138struct 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))]
147pub 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
154impl<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}