From 6af81b587c29ee442a950e7ef858305d2cb90e73 Mon Sep 17 00:00:00 2001 From: Bendik Aagaard Lynghaug Date: Sat, 3 Oct 2026 09:15:54 +0000 Subject: [PATCH] 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 --- Cargo.lock | 19 +------------------ Cargo.toml | 3 +-- src/buffer.rs | 33 ++++++++++++++++----------------- 3 files changed, 18 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47f0cef..916ffff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,16 +462,6 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "memmap2" version = "0.9.5" @@ -809,12 +799,6 @@ dependencies = [ "digest", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "simd-json" version = "0.13.8" @@ -1289,13 +1273,12 @@ dependencies = [ "fontconfig", "fuzzy-matcher", "libpulse-binding", - "memmap", + "memmap2", "nix 0.26.4", "rcalc_lib", "rusttype", "serde", "serde_yaml", - "shlex", "simd-json", "unicode-segmentation", "walkdir", diff --git a/Cargo.toml b/Cargo.toml index 801a689..a07b1ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ wayland-client = { version = "0.30", features = ["calloop", "log"] } wayland-protocols = { version = "0.30", features = ["client", "unstable", "staging"] } wayland-protocols-wlr = { version = "0.1", features = ["client"] } nix = "0.26" # 0.27 is breaking -memmap = "0.7" +memmap2 = "0.9" fontconfig = { version = "0.8.0", optional = true } rusttype = "0.9" # fontdue = "0.7" @@ -27,7 +27,6 @@ walkdir = "2.4" unicode-segmentation = "1.10" fuzzy-matcher = "0.3" -shlex = "1.3" xkbcommon = { version = "0.8", features = ["wayland"] } diff --git a/src/buffer.rs b/src/buffer.rs index f48077c..8a9200e 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -16,7 +16,7 @@ use nix::{ unistd, }; -use memmap::MmapMut; +use memmap2::MmapMut; use wayland_client::{ protocol::{wl_buffer, wl_shm, wl_shm_pool}, @@ -78,11 +78,13 @@ fn create_shm_fd() -> io::Result { } pub struct ShmBuffer { + // Keeps the memfd alive for as long as the mapping and pool exist. + #[allow(dead_code)] pub file: File, pub mmap: MmapMut, + pub pool: wl_shm_pool::WlShmPool, pub buffer: wl_buffer::WlBuffer, pub refcnt: u32, - pub id: u32, pub last_damage: Vec, } @@ -94,7 +96,6 @@ impl ShmBuffer { height: i32, stride: i32, format: wl_shm::Format, - id: u32, ) -> io::Result where D: Dispatch + Dispatch + 'static, @@ -103,15 +104,15 @@ impl ShmBuffer { let file = unsafe { File::from_raw_fd(mem_fd) }; file.set_len((height * stride) as u64)?; 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, ()); Ok(ShmBuffer { refcnt: 0, file, mmap, + pool, buffer, - id, last_damage: Vec::new(), }) } @@ -121,26 +122,27 @@ impl ShmBuffer { } 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 buffers: Vec, - pub next_id: u32, } impl BufferManager { pub fn new() -> BufferManager { BufferManager { buffers: Vec::new(), - next_id: 0, } } pub fn clear_buffers(&mut self) { for buf in self.buffers.iter_mut() { - buf.buffer.destroy() + buf.buffer.destroy(); + buf.pool.destroy(); } self.buffers.clear() } @@ -162,18 +164,15 @@ impl BufferManager { ) where D: Dispatch + Dispatch + 'static, { - let mut buf = ShmBuffer::new( + let buf = ShmBuffer::new( wl_shm, qh, dimensions.0, dimensions.1, dimensions.0 * 4, wl_shm::Format::Argb8888, - self.next_id, ) .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); } } @@ -185,7 +184,7 @@ pub struct 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 { std::slice::from_raw_parts_mut( 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(); if cfg!(debug_assertions) && (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)) } - pub fn offset(&mut self, offset: (u32, u32)) -> BufferView { + pub fn offset(&mut self, offset: (u32, u32)) -> BufferView<'_> { let bounds = self.get_bounds(); if cfg!(debug_assertions) && (offset.0 > bounds.2 || offset.1 > bounds.3) { panic!(