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"
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",
+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-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"] }
+16 -17
View File
@@ -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<RawFd> {
}
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<Geometry>,
}
@@ -94,7 +96,6 @@ impl ShmBuffer {
height: i32,
stride: i32,
format: wl_shm::Format,
id: u32,
) -> io::Result<ShmBuffer>
where
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) };
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<ShmBuffer>,
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<wl_shm_pool::WlShmPool, ()> + Dispatch<wl_buffer::WlBuffer, ()> + '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!(