Fix bug where Buffers were never freed

The issue was that, when reiniting Buffers, the intrusive linkedlist
node was clobbered and we lost reference to it. That meant most of the
Buffers would be memory leaks. Now, we save the node during
reinitializion.
This commit is contained in:
Ben Buhse 2026-03-06 11:37:21 -06:00
commit 411f679491
No known key found for this signature in database
GPG key ID: 7916ACFCD38FD0B4
3 changed files with 14 additions and 5 deletions

View file

@ -2,6 +2,7 @@
These are in rough order of my priority, though no promises I do them in this order. These are in rough order of my priority, though no promises I do them in this order.
- [ ] Fix mouse resizing
- [ ] Add gap support - [ ] Add gap support
- [ ] Support window tag/order caching between WM restarts (within a river session) - [ ] Support window tag/order caching between WM restarts (within a river session)
- [ ] Add build-time options for including the wallpaper (and maybe bar) - [ ] Add build-time options for including the wallpaper (and maybe bar)

View file

@ -76,6 +76,15 @@ pub fn init(shm: *wl.Shm, width: u31, height: u31) !Buffer {
}; };
} }
/// Re-initialize this buffer with new dimensions, preserving its position in
/// any intrusive linked list. Equivalent to deinit + init but keeps node intact.
pub fn reinit(buffer: *Buffer, shm: *wl.Shm, width: u31, height: u31) !void {
const saved_node = buffer.node;
buffer.deinit();
buffer.* = try Buffer.init(shm, width, height);
buffer.node = saved_node;
}
pub fn deinit(buffer: *Buffer) void { pub fn deinit(buffer: *Buffer) void {
_ = buffer.pixman_image.unref(); _ = buffer.pixman_image.unref();
buffer.wl_buffer.destroy(); buffer.wl_buffer.destroy();

View file

@ -37,7 +37,7 @@ pub fn deinit(buffer_pool: *BufferPool) void {
/// Get a buffer with the specified dimensions. If possible, an idle buffer is /// Get a buffer with the specified dimensions. If possible, an idle buffer is
/// reused, otherwise a new one is created. /// reused, otherwise a new one is created.
pub fn nextBuffer(buffer_pool: *BufferPool, wl_shm: *wl.Shm, width: u31, height: u31) !*Buffer { pub fn nextBuffer(buffer_pool: *BufferPool, wl_shm: *wl.Shm, width: u31, height: u31) !*Buffer {
log.debug("looking for buffer with dimensions {}x{}, total existing buffers: {}", .{ width, height, buffer_pool.len }); log.debug("Looking for buffer with dimensions {}x{}, total existing buffers: {}", .{ width, height, buffer_pool.len });
defer { defer {
// Clear up extra buffers // Clear up extra buffers
if (buffer_pool.len > max_buffer_multiplicity * buffer_pool.surface_count) { if (buffer_pool.len > max_buffer_multiplicity * buffer_pool.surface_count) {
@ -71,8 +71,7 @@ fn findSuitableBuffer(buffer_pool: *BufferPool, wl_shm: *wl.Shm, width: u31, hei
// No buffer has matching dimensions, however we do have an unbusy // No buffer has matching dimensions, however we do have an unbusy
// buffer which we can just re-init. // buffer which we can just re-init.
if (first_unbusy_buffer) |buffer| { if (first_unbusy_buffer) |buffer| {
buffer.deinit(); try buffer.reinit(wl_shm, width, height);
buffer.* = try Buffer.init(wl_shm, width, height);
buffer.setListener(); buffer.setListener();
return buffer; return buffer;
} }
@ -81,7 +80,7 @@ fn findSuitableBuffer(buffer_pool: *BufferPool, wl_shm: *wl.Shm, width: u31, hei
} }
fn newBuffer(buffer_pool: *BufferPool, wl_shm: *wl.Shm, width: u31, height: u31) !*Buffer { fn newBuffer(buffer_pool: *BufferPool, wl_shm: *wl.Shm, width: u31, height: u31) !*Buffer {
log.debug("creating new buffer {}x{}", .{ width, height }); log.debug("Creating new {}x{} buffer", .{ width, height });
const buffer = try utils.gpa.create(Buffer); const buffer = try utils.gpa.create(Buffer);
errdefer utils.gpa.destroy(buffer); errdefer utils.gpa.destroy(buffer);
buffer.* = try Buffer.init(wl_shm, width, height); buffer.* = try Buffer.init(wl_shm, width, height);
@ -92,8 +91,8 @@ fn newBuffer(buffer_pool: *BufferPool, wl_shm: *wl.Shm, width: u31, height: u31)
} }
fn cullBuffers(buffer_pool: *BufferPool) void { fn cullBuffers(buffer_pool: *BufferPool) void {
log.debug("culling extra buffers", .{});
var overhead = buffer_pool.len - max_buffer_multiplicity * buffer_pool.surface_count; var overhead = buffer_pool.len - max_buffer_multiplicity * buffer_pool.surface_count;
log.debug("Culling extra buffers {d}->{d}", .{ buffer_pool.len, buffer_pool.surface_count });
var it = buffer_pool.buffers.first; var it = buffer_pool.buffers.first;
while (it) |node| { while (it) |node| {
if (overhead == 0) break; if (overhead == 0) break;