buffer: switch to memmap2, destroy shm pools, never underflow refcounts

memmap 0.7 is unmaintained (RUSTSEC-2020-0077); memmap2 is a drop-in
replacement. Drop the unused shlex dependency.

Each resize created a new wl_shm_pool that was never destroyed; keep the
pool on the buffer and destroy it with it. A wl_buffer.release for a
buffer we didn't consider acquired used to underflow refcnt and wedge
rendering. Fix the lifetime-elision warnings while here.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-10-03 09:15:54 +00:00
co-authored by Claude Fable 5.1
parent c033335912
commit 6af81b587c
3 changed files with 18 additions and 37 deletions
Generated
+1 -18
View File
@@ -462,16 +462,6 @@ version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "memmap"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "memmap2" name = "memmap2"
version = "0.9.5" version = "0.9.5"
@@ -809,12 +799,6 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "simd-json" name = "simd-json"
version = "0.13.8" version = "0.13.8"
@@ -1289,13 +1273,12 @@ dependencies = [
"fontconfig", "fontconfig",
"fuzzy-matcher", "fuzzy-matcher",
"libpulse-binding", "libpulse-binding",
"memmap", "memmap2",
"nix 0.26.4", "nix 0.26.4",
"rcalc_lib", "rcalc_lib",
"rusttype", "rusttype",
"serde", "serde",
"serde_yaml", "serde_yaml",
"shlex",
"simd-json", "simd-json",
"unicode-segmentation", "unicode-segmentation",
"walkdir", "walkdir",
+1 -2
View File
@@ -16,7 +16,7 @@ wayland-client = { version = "0.30", features = ["calloop", "log"] }
wayland-protocols = { version = "0.30", features = ["client", "unstable", "staging"] } wayland-protocols = { version = "0.30", features = ["client", "unstable", "staging"] }
wayland-protocols-wlr = { version = "0.1", features = ["client"] } wayland-protocols-wlr = { version = "0.1", features = ["client"] }
nix = "0.26" # 0.27 is breaking nix = "0.26" # 0.27 is breaking
memmap = "0.7" memmap2 = "0.9"
fontconfig = { version = "0.8.0", optional = true } fontconfig = { version = "0.8.0", optional = true }
rusttype = "0.9" rusttype = "0.9"
# fontdue = "0.7" # fontdue = "0.7"
@@ -27,7 +27,6 @@ walkdir = "2.4"
unicode-segmentation = "1.10" unicode-segmentation = "1.10"
fuzzy-matcher = "0.3" fuzzy-matcher = "0.3"
shlex = "1.3"
xkbcommon = { version = "0.8", features = ["wayland"] } xkbcommon = { version = "0.8", features = ["wayland"] }
+16 -17
View File
@@ -16,7 +16,7 @@ use nix::{
unistd, unistd,
}; };
use memmap::MmapMut; use memmap2::MmapMut;
use wayland_client::{ use wayland_client::{
protocol::{wl_buffer, wl_shm, wl_shm_pool}, protocol::{wl_buffer, wl_shm, wl_shm_pool},
@@ -78,11 +78,13 @@ fn create_shm_fd() -> io::Result<RawFd> {
} }
pub struct ShmBuffer { pub struct ShmBuffer {
// Keeps the memfd alive for as long as the mapping and pool exist.
#[allow(dead_code)]
pub file: File, pub file: File,
pub mmap: MmapMut, pub mmap: MmapMut,
pub pool: wl_shm_pool::WlShmPool,
pub buffer: wl_buffer::WlBuffer, pub buffer: wl_buffer::WlBuffer,
pub refcnt: u32, pub refcnt: u32,
pub id: u32,
pub last_damage: Vec<Geometry>, pub last_damage: Vec<Geometry>,
} }
@@ -94,7 +96,6 @@ impl ShmBuffer {
height: i32, height: i32,
stride: i32, stride: i32,
format: wl_shm::Format, format: wl_shm::Format,
id: u32,
) -> io::Result<ShmBuffer> ) -> io::Result<ShmBuffer>
where where
D: Dispatch<wl_shm_pool::WlShmPool, ()> + Dispatch<wl_buffer::WlBuffer, ()> + 'static, D: Dispatch<wl_shm_pool::WlShmPool, ()> + Dispatch<wl_buffer::WlBuffer, ()> + 'static,
@@ -103,15 +104,15 @@ impl ShmBuffer {
let file = unsafe { File::from_raw_fd(mem_fd) }; let file = unsafe { File::from_raw_fd(mem_fd) };
file.set_len((height * stride) as u64)?; file.set_len((height * stride) as u64)?;
let pool = shm.create_pool(mem_fd, height * stride, &qh, ()); let pool = shm.create_pool(mem_fd, height * stride, &qh, ());
let mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; let mmap = unsafe { MmapMut::map_mut(&file)? };
let buffer = pool.create_buffer(0, width, height, stride, format, &qh, ()); let buffer = pool.create_buffer(0, width, height, stride, format, &qh, ());
Ok(ShmBuffer { Ok(ShmBuffer {
refcnt: 0, refcnt: 0,
file, file,
mmap, mmap,
pool,
buffer, buffer,
id,
last_damage: Vec::new(), last_damage: Vec::new(),
}) })
} }
@@ -121,26 +122,27 @@ impl ShmBuffer {
} }
pub fn release(&mut self) { pub fn release(&mut self) {
self.refcnt -= 1; // A compositor may release a buffer we never considered acquired
// (e.g. after a resize); never underflow and wedge the buffer.
self.refcnt = self.refcnt.saturating_sub(1);
} }
} }
pub struct BufferManager { pub struct BufferManager {
pub buffers: Vec<ShmBuffer>, pub buffers: Vec<ShmBuffer>,
pub next_id: u32,
} }
impl BufferManager { impl BufferManager {
pub fn new() -> BufferManager { pub fn new() -> BufferManager {
BufferManager { BufferManager {
buffers: Vec::new(), buffers: Vec::new(),
next_id: 0,
} }
} }
pub fn clear_buffers(&mut self) { pub fn clear_buffers(&mut self) {
for buf in self.buffers.iter_mut() { for buf in self.buffers.iter_mut() {
buf.buffer.destroy() buf.buffer.destroy();
buf.pool.destroy();
} }
self.buffers.clear() self.buffers.clear()
} }
@@ -162,18 +164,15 @@ impl BufferManager {
) where ) where
D: Dispatch<wl_shm_pool::WlShmPool, ()> + Dispatch<wl_buffer::WlBuffer, ()> + 'static, D: Dispatch<wl_shm_pool::WlShmPool, ()> + Dispatch<wl_buffer::WlBuffer, ()> + 'static,
{ {
let mut buf = ShmBuffer::new( let buf = ShmBuffer::new(
wl_shm, wl_shm,
qh, qh,
dimensions.0, dimensions.0,
dimensions.1, dimensions.1,
dimensions.0 * 4, dimensions.0 * 4,
wl_shm::Format::Argb8888, wl_shm::Format::Argb8888,
self.next_id,
) )
.expect("unable to add buffer"); .expect("unable to add buffer");
self.next_id += 1;
BufferView::new(&mut buf.mmap, (dimensions.0 as u32, dimensions.1 as u32));
self.buffers.push(buf); self.buffers.push(buf);
} }
} }
@@ -185,7 +184,7 @@ pub struct BufferView<'a> {
} }
impl<'a> BufferView<'a> { impl<'a> BufferView<'a> {
pub fn new(buf: &'a mut MmapMut, dimensions: (u32, u32)) -> BufferView { pub fn new(buf: &'a mut MmapMut, dimensions: (u32, u32)) -> BufferView<'a> {
let view = unsafe { let view = unsafe {
std::slice::from_raw_parts_mut( std::slice::from_raw_parts_mut(
buf.as_mut_ptr() as *mut u32, buf.as_mut_ptr() as *mut u32,
@@ -208,7 +207,7 @@ impl<'a> BufferView<'a> {
} }
} }
pub fn subdimensions(&mut self, subdimensions: (u32, u32, u32, u32)) -> BufferView { pub fn subdimensions(&mut self, subdimensions: (u32, u32, u32, u32)) -> BufferView<'_> {
let bounds = self.get_bounds(); let bounds = self.get_bounds();
if cfg!(debug_assertions) if cfg!(debug_assertions)
&& (subdimensions.0 + subdimensions.2 > bounds.2 && (subdimensions.0 + subdimensions.2 > bounds.2
@@ -232,11 +231,11 @@ impl<'a> BufferView<'a> {
} }
} }
pub fn subgeometry(&mut self, geo: Geometry) -> BufferView { pub fn subgeometry(&mut self, geo: Geometry) -> BufferView<'_> {
self.subdimensions((geo.x, geo.y, geo.width, geo.height)) self.subdimensions((geo.x, geo.y, geo.width, geo.height))
} }
pub fn offset(&mut self, offset: (u32, u32)) -> BufferView { pub fn offset(&mut self, offset: (u32, u32)) -> BufferView<'_> {
let bounds = self.get_bounds(); let bounds = self.get_bounds();
if cfg!(debug_assertions) && (offset.0 > bounds.2 || offset.1 > bounds.3) { if cfg!(debug_assertions) && (offset.0 > bounds.2 || offset.1 > bounds.3) {
panic!( panic!(