wgpu_core/
registry.rs

1use std::{mem::size_of, sync::Arc};
2
3use crate::{
4    id::Id,
5    identity::IdentityManager,
6    lock::{rank, RwLock, RwLockReadGuard, RwLockWriteGuard},
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    pub fn id(&self) -> Id<T::Marker> {
59        self.id
60    }
61
62    /// Assign a new resource to this ID.
63    ///
64    /// Registers it with the registry.
65    pub fn assign(self, value: T) -> Id<T::Marker> {
66        let mut data = self.data.write();
67        data.insert(self.id, value);
68        self.id
69    }
70}
71
72impl<T: StorageItem> Registry<T> {
73    pub(crate) fn prepare(&self, id_in: Option<Id<T::Marker>>) -> FutureId<T> {
74        FutureId {
75            id: match id_in {
76                Some(id_in) => {
77                    self.identity.mark_as_used(id_in);
78                    id_in
79                }
80                None => self.identity.process(),
81            },
82            data: &self.storage,
83        }
84    }
85
86    #[track_caller]
87    pub(crate) fn read<'a>(&'a self) -> RwLockReadGuard<'a, Storage<T>> {
88        self.storage.read()
89    }
90    #[track_caller]
91    pub(crate) fn write<'a>(&'a self) -> RwLockWriteGuard<'a, Storage<T>> {
92        self.storage.write()
93    }
94    pub(crate) fn remove(&self, id: Id<T::Marker>) -> T {
95        let value = self.storage.write().remove(id);
96        // This needs to happen *after* removing it from the storage, to maintain the
97        // invariant that `self.identity` only contains ids which are actually available
98        // See https://github.com/gfx-rs/wgpu/issues/5372
99        self.identity.free(id);
100        //Returning None is legal if it's an error ID
101        value
102    }
103
104    pub(crate) fn generate_report(&self) -> RegistryReport {
105        let storage = self.storage.read();
106        let mut report = RegistryReport {
107            element_size: size_of::<T>(),
108            ..Default::default()
109        };
110        report.num_allocated = self.identity.values.lock().count();
111        for element in storage.map.iter() {
112            match *element {
113                Element::Occupied(..) => report.num_kept_from_user += 1,
114                Element::Vacant => report.num_released_from_user += 1,
115            }
116        }
117        report
118    }
119}
120
121impl<T: StorageItem + Clone> Registry<T> {
122    pub(crate) fn get(&self, id: Id<T::Marker>) -> T {
123        self.read().get(id)
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use std::sync::Arc;
130
131    use crate::{id::Marker, resource::ResourceType, storage::StorageItem};
132
133    use super::Registry;
134    struct TestData;
135    struct TestDataId;
136    impl Marker for TestDataId {}
137
138    impl ResourceType for TestData {
139        const TYPE: &'static str = "TestData";
140    }
141    impl StorageItem for TestData {
142        type Marker = TestDataId;
143    }
144
145    #[test]
146    fn simultaneous_registration() {
147        let registry = Registry::new();
148        std::thread::scope(|s| {
149            for _ in 0..5 {
150                s.spawn(|| {
151                    for _ in 0..1000 {
152                        let value = Arc::new(TestData);
153                        let new_id = registry.prepare(None);
154                        let id = new_id.assign(value);
155                        registry.remove(id);
156                    }
157                });
158            }
159        })
160    }
161}