a73x

aa5e977b

refactor: centralize native attachment ownership before lifecycle sprint

a73x   2026-09-05 10:09

Commit message
refactor: centralize native attachment ownership before lifecycle sprint

src/gui/runtime.zig
Old New
@@ -17,6 +17,23 @@ pub const Live = struct {
17 notify: Notify, 17 notify: Notify,
18 pending: std.atomic.Value(bool) = .init(false), 18 pending: std.atomic.Value(bool) = .init(false),
19 bell_until: i64 = 0, 19 bell_until: i64 = 0,
20 /// The pane owns the transport strings and must outlive this attachment.
21 /// Allocate the wake context at its final address before starting its pump.
22 fn start(alloc: std.mem.Allocator, pane: *const model.Pane, size: term.protocol.Size, notify: Notify, existing_only: bool) !*Live {
23 const self = try alloc.create(Live);
24 errdefer alloc.destroy(self);
25 const grid = try term.grid.Grid.init(alloc, 1, 1);
26 errdefer grid.deinit();
27 self.* = .{ .key = .{ .pane = pane.id, .generation = pane.generation }, .pump = undefined, .snapshot = grid, .size = size, .notify = notify };
28 self.pump = try Pump.start(alloc, .{ .target = pane.identity.target, .session = pane.identity.session, .cols = size.cols, .rows = size.rows, .existing_only = existing_only, .wake = wake, .wake_ctx = self });
29 return self;
30 }
31 fn destroy(self: *Live, alloc: std.mem.Allocator) void {
32 // Join before freeing the wake context or the pane's borrowed identity.
33 self.pump.stop();
34 self.snapshot.deinit();
35 alloc.destroy(self);
36 }
20 fn wake(ctx: ?*anyopaque) void { 37 fn wake(ctx: ?*anyopaque) void {
21 const self: *Live = @ptrCast(@alignCast(ctx.?)); 38 const self: *Live = @ptrCast(@alignCast(ctx.?));
22 if (self.pending.swap(true, .acq_rel)) return; 39 if (self.pending.swap(true, .acq_rel)) return;
@@ -56,9 +73,7 @@ pub const Runtime = struct {
56 live.pump.say(.quit) catch unreachable; 73 live.pump.say(.quit) catch unreachable;
57 }; 74 };
58 for (self.lives) |p| if (p) |live| { 75 for (self.lives) |p| if (p) |live| {
59 live.pump.stop(); 76 live.destroy(self.alloc);
60 live.snapshot.deinit();
61 self.alloc.destroy(live);
62 }; 77 };
63 self.workspace.deinit(); 78 self.workspace.deinit();
64 } 79 }
@@ -78,12 +93,7 @@ pub const Runtime = struct {
78 pub fn addWithPolicy(self: *Runtime, target: client.Target, session: []const u8, width: u32, height: u32, metrics: model.Metrics, existing_only: bool) !model.PaneId { 93 pub fn addWithPolicy(self: *Runtime, target: client.Target, session: []const u8, width: u32, height: u32, metrics: model.Metrics, existing_only: bool) !model.PaneId {
79 var prepared = try self.workspace.prepare(target, session, width, height, metrics); 94 var prepared = try self.workspace.prepare(target, session, width, height, metrics);
80 errdefer prepared.discard(self.alloc); 95 errdefer prepared.discard(self.alloc);
81 const live = try self.alloc.create(Live); 96 const live = try Live.start(self.alloc, prepared.pane, .{ .cols = prepared.placement.cols, .rows = prepared.placement.rows }, self.notify, existing_only);
82 errdefer self.alloc.destroy(live);
83 const grid = try term.grid.Grid.init(self.alloc, 1, 1);
84 errdefer grid.deinit();
85 live.* = .{ .key = .{ .pane = prepared.pane.id, .generation = prepared.pane.generation }, .pump = undefined, .snapshot = grid, .size = .{ .cols = prepared.placement.cols, .rows = prepared.placement.rows }, .notify = self.notify };
86 live.pump = try Pump.start(self.alloc, .{ .target = prepared.pane.identity.target, .session = prepared.pane.identity.session, .cols = live.size.cols, .rows = live.size.rows, .existing_only = existing_only, .wake = Live.wake, .wake_ctx = live });
87 for (&self.lives) |*slot| if (slot.* == null) { 97 for (&self.lives) |*slot| if (slot.* == null) {
88 slot.* = live; 98 slot.* = live;
89 break; 99 break;
@@ -94,9 +104,7 @@ pub const Runtime = struct {
94 pub fn remove(self: *Runtime, id: model.PaneId) void { 104 pub fn remove(self: *Runtime, id: model.PaneId) void {
95 for (&self.lives) |*slot| if (slot.*) |live| { 105 for (&self.lives) |*slot| if (slot.*) |live| {
96 if (live.key.pane == id) { 106 if (live.key.pane == id) {
97 live.pump.stop(); 107 live.destroy(self.alloc);
98 live.snapshot.deinit();
99 self.alloc.destroy(live);
100 slot.* = null; 108 slot.* = null;
101 break; 109 break;
102 } 110 }