terminal: make error sets more explicit, capture explicit errors

This commit is contained in:
Mitchell Hashimoto
2024-08-31 09:47:05 -07:00
parent 3807ee34c1
commit 9d08ed32ee
4 changed files with 73 additions and 23 deletions

View File

@ -1758,6 +1758,8 @@ pub const AdjustCapacity = struct {
string_bytes: ?usize = null, string_bytes: ?usize = null,
}; };
pub const AdjustCapacityError = Allocator.Error || Page.CloneFromError;
/// Adjust the capcaity of the given page in the list. This should /// Adjust the capcaity of the given page in the list. This should
/// be used in cases where OutOfMemory is returned by some operation /// be used in cases where OutOfMemory is returned by some operation
/// i.e to increase style counts, grapheme counts, etc. /// i.e to increase style counts, grapheme counts, etc.
@ -1778,25 +1780,31 @@ pub fn adjustCapacity(
self: *PageList, self: *PageList,
page: *List.Node, page: *List.Node,
adjustment: AdjustCapacity, adjustment: AdjustCapacity,
) !*List.Node { ) AdjustCapacityError!*List.Node {
// We always start with the base capacity of the existing page. This // We always start with the base capacity of the existing page. This
// ensures we never shrink from what we need. // ensures we never shrink from what we need.
var cap = page.data.capacity; var cap = page.data.capacity;
// All ceilPowerOfTwo is unreachable because we're always same or less
// bit width so maxInt is always possible.
if (adjustment.styles) |v| { if (adjustment.styles) |v| {
const aligned = try std.math.ceilPowerOfTwo(usize, v); comptime assert(@bitSizeOf(@TypeOf(v)) <= @bitSizeOf(usize));
const aligned = std.math.ceilPowerOfTwo(usize, v) catch unreachable;
cap.styles = @max(cap.styles, aligned); cap.styles = @max(cap.styles, aligned);
} }
if (adjustment.grapheme_bytes) |v| { if (adjustment.grapheme_bytes) |v| {
const aligned = try std.math.ceilPowerOfTwo(usize, v); comptime assert(@bitSizeOf(@TypeOf(v)) <= @bitSizeOf(usize));
const aligned = std.math.ceilPowerOfTwo(usize, v) catch unreachable;
cap.grapheme_bytes = @max(cap.grapheme_bytes, aligned); cap.grapheme_bytes = @max(cap.grapheme_bytes, aligned);
} }
if (adjustment.hyperlink_bytes) |v| { if (adjustment.hyperlink_bytes) |v| {
const aligned = try std.math.ceilPowerOfTwo(usize, v); comptime assert(@bitSizeOf(@TypeOf(v)) <= @bitSizeOf(usize));
const aligned = std.math.ceilPowerOfTwo(usize, v) catch unreachable;
cap.hyperlink_bytes = @max(cap.hyperlink_bytes, aligned); cap.hyperlink_bytes = @max(cap.hyperlink_bytes, aligned);
} }
if (adjustment.string_bytes) |v| { if (adjustment.string_bytes) |v| {
const aligned = try std.math.ceilPowerOfTwo(usize, v); comptime assert(@bitSizeOf(@TypeOf(v)) <= @bitSizeOf(usize));
const aligned = std.math.ceilPowerOfTwo(usize, v) catch unreachable;
cap.string_bytes = @max(cap.string_bytes, aligned); cap.string_bytes = @max(cap.string_bytes, aligned);
} }
@ -1830,7 +1838,7 @@ pub fn adjustCapacity(
fn createPage( fn createPage(
self: *PageList, self: *PageList,
cap: Capacity, cap: Capacity,
) !*List.Node { ) Allocator.Error!*List.Node {
// log.debug("create page cap={}", .{cap}); // log.debug("create page cap={}", .{cap});
return try createPageExt(&self.pool, cap, &self.page_size); return try createPageExt(&self.pool, cap, &self.page_size);
} }
@ -1839,7 +1847,7 @@ fn createPageExt(
pool: *MemoryPool, pool: *MemoryPool,
cap: Capacity, cap: Capacity,
total_size: ?*usize, total_size: ?*usize,
) !*List.Node { ) Allocator.Error!*List.Node {
var page = try pool.nodes.create(); var page = try pool.nodes.create();
errdefer pool.nodes.destroy(page); errdefer pool.nodes.destroy(page);
@ -2292,7 +2300,7 @@ pub fn pin(self: *const PageList, pt: point.Point) ?Pin {
/// automatically updated as the pagelist is modified. If the point the /// automatically updated as the pagelist is modified. If the point the
/// pin points to is removed completely, the tracked pin will be updated /// pin points to is removed completely, the tracked pin will be updated
/// to the top-left of the screen. /// to the top-left of the screen.
pub fn trackPin(self: *PageList, p: Pin) !*Pin { pub fn trackPin(self: *PageList, p: Pin) Allocator.Error!*Pin {
if (comptime std.debug.runtime_safety) assert(self.pinIsValid(p)); if (comptime std.debug.runtime_safety) assert(self.pinIsValid(p));
// Create our tracked pin // Create our tracked pin

View File

@ -437,7 +437,7 @@ pub fn adjustCapacity(
self: *Screen, self: *Screen,
page: *PageList.List.Node, page: *PageList.List.Node,
adjustment: PageList.AdjustCapacity, adjustment: PageList.AdjustCapacity,
) !*PageList.List.Node { ) PageList.AdjustCapacityError!*PageList.List.Node {
// If the page being modified isn't our cursor page then // If the page being modified isn't our cursor page then
// this is a quick operation because we have no additional // this is a quick operation because we have no additional
// accounting. // accounting.

View File

@ -1444,6 +1444,7 @@ pub fn insertLines(self: *Terminal, count: usize) void {
const start_y = self.screen.cursor.y; const start_y = self.screen.cursor.y;
defer { defer {
self.screen.cursorAbsolute(self.scrolling_region.left, start_y); self.screen.cursorAbsolute(self.scrolling_region.left, start_y);
// Always unset pending wrap // Always unset pending wrap
self.screen.cursor.pending_wrap = false; self.screen.cursor.pending_wrap = false;
} }
@ -1464,8 +1465,15 @@ pub fn insertLines(self: *Terminal, count: usize) void {
var cur_p = self.screen.pages.trackPin( var cur_p = self.screen.pages.trackPin(
self.screen.cursor.page_pin.down(rem - 1).?, self.screen.cursor.page_pin.down(rem - 1).?,
) catch |err| { ) catch |err| {
std.log.err("TODO: insertLines handle trackPin error err={}", .{err}); comptime assert(@TypeOf(err) == error{OutOfMemory});
@panic("TODO: insertLines handle trackPin error");
// This error scenario means that our GPA is OOM. This is not a
// situation we can gracefully handle. We can't just ignore insertLines
// because it'll result in a corrupted screen. Ideally in the future
// we flag the state as broken and show an error message to the user.
// For now, we panic.
log.err("insertLines trackPin error err={}", .{err});
@panic("insertLines trackPin OOM");
}; };
defer self.screen.pages.untrackPin(cur_p); defer self.screen.pages.untrackPin(cur_p);
@ -1525,7 +1533,7 @@ pub fn insertLines(self: *Terminal, count: usize) void {
// Increase style memory // Increase style memory
error.StyleSetOutOfMemory, error.StyleSetOutOfMemory,
=> .{.styles = cap.styles * 2 }, => .{ .styles = cap.styles * 2 },
// Increase string memory // Increase string memory
error.StringAllocOutOfMemory, error.StringAllocOutOfMemory,
@ -1541,10 +1549,27 @@ pub fn insertLines(self: *Terminal, count: usize) void {
error.GraphemeAllocOutOfMemory, error.GraphemeAllocOutOfMemory,
=> .{ .grapheme_bytes = cap.grapheme_bytes * 2 }, => .{ .grapheme_bytes = cap.grapheme_bytes * 2 },
}, },
) catch |e| { ) catch |e| switch (e) {
std.log.err("TODO: insertLines handle adjustCapacity error err={}", .{e}); // This shouldn't be possible because above we're only
@panic("TODO: insertLines handle adjustCapacity error"); // adjusting capacity _upwards_. So it should have all
// the existing capacity it had to fit the adjusted
// data. Panic since we don't expect this.
error.StyleSetOutOfMemory,
error.StyleSetNeedsRehash,
error.StringAllocOutOfMemory,
error.HyperlinkSetOutOfMemory,
error.HyperlinkSetNeedsRehash,
error.HyperlinkMapOutOfMemory,
error.GraphemeMapOutOfMemory,
error.GraphemeAllocOutOfMemory,
=> @panic("adjustCapacity resulted in capacity errors"),
// The system allocator is OOM. We can't currently do
// anything graceful here. We panic.
error.OutOfMemory,
=> @panic("adjustCapacity system allocator OOM"),
}; };
// Continue the loop to try handling this row again. // Continue the loop to try handling this row again.
continue; continue;
}; };
@ -1643,8 +1668,10 @@ pub fn deleteLines(self: *Terminal, count: usize) void {
var cur_p = self.screen.pages.trackPin( var cur_p = self.screen.pages.trackPin(
self.screen.cursor.page_pin.*, self.screen.cursor.page_pin.*,
) catch |err| { ) catch |err| {
std.log.err("TODO: deleteLines handle trackPin error err={}", .{err}); // See insertLines
@panic("TODO: deleteLines handle trackPin error"); comptime assert(@TypeOf(err) == error{OutOfMemory});
log.err("deleteLines trackPin error err={}", .{err});
@panic("deleteLines trackPin OOM");
}; };
defer self.screen.pages.untrackPin(cur_p); defer self.screen.pages.untrackPin(cur_p);
@ -1704,7 +1731,7 @@ pub fn deleteLines(self: *Terminal, count: usize) void {
// Increase style memory // Increase style memory
error.StyleSetOutOfMemory, error.StyleSetOutOfMemory,
=> .{.styles = cap.styles * 2 }, => .{ .styles = cap.styles * 2 },
// Increase string memory // Increase string memory
error.StringAllocOutOfMemory, error.StringAllocOutOfMemory,
@ -1720,10 +1747,22 @@ pub fn deleteLines(self: *Terminal, count: usize) void {
error.GraphemeAllocOutOfMemory, error.GraphemeAllocOutOfMemory,
=> .{ .grapheme_bytes = cap.grapheme_bytes * 2 }, => .{ .grapheme_bytes = cap.grapheme_bytes * 2 },
}, },
) catch |e| { ) catch |e| switch (e) {
std.log.err("TODO: deleteLines handle adjustCapacity error err={}", .{e}); // See insertLines which has the same error capture.
@panic("TODO: deleteLines handle adjustCapacity error"); error.StyleSetOutOfMemory,
error.StyleSetNeedsRehash,
error.StringAllocOutOfMemory,
error.HyperlinkSetOutOfMemory,
error.HyperlinkSetNeedsRehash,
error.HyperlinkMapOutOfMemory,
error.GraphemeMapOutOfMemory,
error.GraphemeAllocOutOfMemory,
=> @panic("adjustCapacity resulted in capacity errors"),
error.OutOfMemory,
=> @panic("adjustCapacity system allocator OOM"),
}; };
// Continue the loop to try handling this row again. // Continue the loop to try handling this row again.
continue; continue;
}; };

View File

@ -708,7 +708,7 @@ pub const Page = struct {
const cells = dst_row.cells.ptr(self.memory)[x_start..x_end]; const cells = dst_row.cells.ptr(self.memory)[x_start..x_end];
// If our destination has styles or graphemes then we need to // If our destination has styles or graphemes then we need to
// clear some state. // clear some state. This will free up the managed memory as well.
if (dst_row.managedMemory()) self.clearCells(dst_row, x_start, x_end); if (dst_row.managedMemory()) self.clearCells(dst_row, x_start, x_end);
// Copy all the row metadata but keep our cells offset // Copy all the row metadata but keep our cells offset
@ -771,6 +771,8 @@ pub const Page = struct {
// Copy the grapheme codepoints // Copy the grapheme codepoints
const cps = other.lookupGrapheme(src_cell).?; const cps = other.lookupGrapheme(src_cell).?;
// Safe to use setGraphemes because we cleared all
// managed memory for our destination cell range.
try self.setGraphemes(dst_row, dst_cell, cps); try self.setGraphemes(dst_row, dst_cell, cps);
} }
if (src_cell.hyperlink) hyperlink: { if (src_cell.hyperlink) hyperlink: {
@ -801,10 +803,11 @@ pub const Page = struct {
if (self.hyperlink_set.lookupContext( if (self.hyperlink_set.lookupContext(
self.memory, self.memory,
other_link.*, other_link.*,
.{ .page = @constCast(other) },
// `lookupContext` uses the context for hashing, and // `lookupContext` uses the context for hashing, and
// that doesn't write to the page, so this constCast // that doesn't write to the page, so this constCast
// is completely safe. // is completely safe.
.{ .page = @constCast(other) },
)) |i| { )) |i| {
self.hyperlink_set.use(self.memory, i); self.hyperlink_set.use(self.memory, i);
break :dst_id i; break :dst_id i;