Skip to main content

aen_window/
memory.rs

1use crate::{invalid, MAX_MEMORY_BYTES};
2use std::io;
3use std::ops::Deref;
4use std::sync::atomic::{AtomicUsize, Ordering};
5use std::sync::Arc;
6
7#[derive(Clone, Debug)]
8pub(crate) struct Budget(Arc<AtomicUsize>);
9impl Budget {
10    #[cfg(any(feature = "server", test))]
11    pub(crate) fn new() -> Self {
12        Self(Arc::new(AtomicUsize::new(0)))
13    }
14    pub(crate) fn reserve(&self, bytes: usize) -> io::Result<Permit> {
15        self.0
16            .fetch_update(Ordering::AcqRel, Ordering::Acquire, |old| {
17                old.checked_add(bytes)
18                    .filter(|next| *next <= MAX_MEMORY_BYTES)
19            })
20            .map_err(|_| invalid("native window aggregate memory exhausted"))?;
21        Ok(Permit {
22            budget: self.clone(),
23            bytes,
24        })
25    }
26    #[cfg(any(feature = "server", test))]
27    pub(crate) fn used(&self) -> usize {
28        self.0.load(Ordering::Acquire)
29    }
30}
31#[derive(Debug)]
32pub(crate) struct Permit {
33    budget: Budget,
34    bytes: usize,
35}
36impl Drop for Permit {
37    fn drop(&mut self) {
38        self.budget.0.fetch_sub(self.bytes, Ordering::AcqRel);
39    }
40}
41
42#[derive(Debug)]
43struct Payload {
44    bytes: Vec<u8>,
45    offset: usize,
46    _permit: Option<Permit>,
47}
48/// Immutable shared pixels. A server-side upload retains its aggregate memory
49/// reservation until the final compositor/upload reference has been dropped.
50#[derive(Clone, Debug)]
51pub struct PixelBuffer(Arc<Payload>);
52impl PixelBuffer {
53    pub fn new(bytes: Vec<u8>) -> Self {
54        Self(Arc::new(Payload {
55            bytes,
56            offset: 0,
57            _permit: None,
58        }))
59    }
60    pub(crate) fn received(bytes: Vec<u8>, offset: usize, permit: Option<Permit>) -> Self {
61        Self(Arc::new(Payload {
62            bytes,
63            offset,
64            _permit: permit,
65        }))
66    }
67}
68impl From<Vec<u8>> for PixelBuffer {
69    fn from(bytes: Vec<u8>) -> Self {
70        Self::new(bytes)
71    }
72}
73impl Deref for PixelBuffer {
74    type Target = [u8];
75    fn deref(&self) -> &[u8] {
76        &self.0.bytes[self.0.offset..]
77    }
78}
79impl AsRef<[u8]> for PixelBuffer {
80    fn as_ref(&self) -> &[u8] {
81        self
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    #[test]
89    fn permit_follows_shared_pixels_and_releases_only_at_last_owner() {
90        let budget = Budget::new();
91        let permit = budget.reserve(28).unwrap();
92        let pixels = PixelBuffer::received(vec![0; 28], 24, Some(permit));
93        let adopted = pixels.clone();
94        drop(pixels);
95        assert_eq!(budget.used(), 28);
96        assert_eq!(adopted.len(), 4);
97        drop(adopted);
98        assert_eq!(budget.used(), 0);
99    }
100    #[test]
101    fn aggregate_reservation_fails_before_allocation_and_recovers() {
102        let budget = Budget::new();
103        let permit = budget.reserve(MAX_MEMORY_BYTES).unwrap();
104        assert!(budget.reserve(1).is_err());
105        assert_eq!(budget.used(), MAX_MEMORY_BYTES);
106        drop(permit);
107        assert_eq!(budget.reserve(1).unwrap().bytes, 1);
108        assert_eq!(budget.used(), 0);
109    }
110}