aa5e977b
refactor: centralize native attachment ownership before lifecycle sprint
a73x 2026-09-05 10:09
Commit message
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 | } |