wgpu_core/
registry.rs

1use alloc::sync::Arc;
2
3use crate::{
4    id::Id,
5    identity::IdentityManager,
6    lock::{rank, RwLock, RwLockReadGuard},
7    storage::{Element, Storage, StorageItem},
8};
9
10#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
11pub struct RegistryReport {
12    pub num_allocated: usize,
13    pub num_kept_from_user: usize,
14    pub num_released_from_user: usize,
15    pub element_size: usize,
16}
17
18impl RegistryReport {
19    pub fn is_empty(&self) -> bool {
20        self.num_allocated + self.num_kept_from_user == 0
21    }
22}
23
24/// Registry is the primary holder of each resource type
25/// Every resource is now arcanized so the last arc released
26/// will in the end free the memory and release the inner raw resource
27///
28/// Registry act as the main entry point to keep resource alive
29/// when created and released from user land code
30///
31/// A resource may still be alive when released from user land code
32/// if it's used in active submission or anyway kept alive from
33/// any other dependent resource
34///
35#[derive(Debug)]
36pub(crate) struct Registry<T: StorageItem> {
37    // Must only contain an id which has either never been used or has been released from `storage`
38    identity: Arc<IdentityManager<T::Marker>>,
39    storage: RwLock<Storage<T>>,
40}
41
42impl<T: StorageItem> Registry<T> {
43    pub(crate) fn new() -> Self {
44        Self {
45            identity: Arc::new(IdentityManager::new()),
46            storage: RwLock::new(rank::REGISTRY_STORAGE, Storage::new()),
47        }
48    }
49}
50
51#[must_use]
52pub(crate) struct FutureId<'a, T: StorageItem> {
53    id: Id<T::Marker>,
54    data: &'a RwLock<Storage<T>>,
55}
56
57impl<T: StorageItem> FutureId<'_, T> {
58    /// Assign a new resource to this ID.
59    ///
60    /// Registers it with the registry.
61    pub fn assign(self, value: T) -> Id<T::Marker> {
62        let mut data = self.data.write();
63        data.insert(self.id, value);
64        self.id
65    }
66}
67
68impl<T: StorageItem> Registry<T> {
69    pub(crate) fn prepare(&self, id_in: Option<Id<T::Marker>>) -> FutureId<'_, T> {
70        FutureId {
71            id: match id_in {
72                Some(id_in) => {
73                    self.identity.mark_as_used(id_in);
74                    id_in
75                }
76                None => self.identity.process(),
77            },
78            data: &self.storage,
79        }
80    }
81
82    #[track_caller]
83    pub(crate) fn read<'a>(&'a self) -> RwLockReadGuard<'a, Storage<T>> {
84        self.storage.read()
85    }
86    pub(crate) fn remove(&self, id: Id<T::Marker>) -> T {
87        let value = self.storage.write().remove(id);
88        // This needs to happen *after* removing it from the storage, to maintain the
89        // invariant that `self.identity` only contains ids which are actually available
90        // See https://github.com/gfx-rs/wgpu/issues/5372
91        self.identity.free(id);
92        //Returning None is legal if it's an error ID
93        value
94    }
95
96    pub(crate) fn generate_report(&self) -> RegistryReport {
97        let storage = self.storage.read();
98        let mut report = RegistryReport {
99            element_size: size_of::<T>(),
100            ..Default::default()
101        };
102        report.num_allocated = self.identity.values.lock().count();
103        for element in storage.map.iter() {
104            match *element {
105                Element::Occupied(..) => report.num_kept_from_user += 1,
106                Element::Vacant => report.num_released_from_user += 1,
107            }
108        }
109        report
110    }
111}
112
113impl<T: StorageItem + Clone> Registry<T> {
114    pub(crate) fn get(&self, id: Id<T::Marker>) -> T {
115        self.read().get(id)
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::Registry;
122    use crate::{id::Marker, resource::ResourceType, storage::StorageItem};
123    use alloc::sync::Arc;
124
125    struct TestData;
126    struct TestDataId;
127    impl Marker for TestDataId {}
128
129    impl ResourceType for TestData {
130        const TYPE: &'static str = "TestData";
131    }
132    impl StorageItem for TestData {
133        type Marker = TestDataId;
134    }
135
136    #[test]
137    fn simultaneous_registration() {
138        let registry = Registry::new();
139        std::thread::scope(|s| {
140            for _ in 0..5 {
141                s.spawn(|| {
142                    for _ in 0..1000 {
143                        let value = Arc::new(TestData);
144                        let new_id = registry.prepare(None);
145                        let id = new_id.assign(value);
146                        registry.remove(id);
147                    }
148                });
149            }
150        })
151    }
152}