libghostty: detach tracked grid refs on free

Tracked grid references previously held a raw terminal wrapper pointer and
were required to be freed before the terminal. If callers kept one past
terminal destruction, later tracked-ref calls could dereference freed
terminal or page-list memory before detecting that the reference was no
longer meaningful.

Track live C tracked-grid-ref handles from the terminal wrapper and detach
them before tearing down terminal storage. Detached refs now report no
value through the tracked-ref APIs and can still be freed by the caller.
Update the C API docs to describe this lifetime behavior and add a
regression test for using a tracked ref after terminal free.

This introduces some overhead but tracked pins shouldn't be numerous
and this dramatically improves safety.
This commit is contained in:
Mitchell Hashimoto
2026-05-24 14:09:28 -07:00
parent d5d8cef4d3
commit 03df613e39
6 changed files with 75 additions and 13 deletions

View File

@@ -70,8 +70,9 @@ extern "C" {
* the tracked reference between screens.
*
* Tracked references are owned by the caller and must be freed with
* ghostty_tracked_grid_ref_free() before the terminal that created them is
* freed.
* ghostty_tracked_grid_ref_free(). If the terminal that created a tracked
* reference is freed first, the handle remains valid only for tracked-grid-ref
* APIs: it reports no value and can still be freed.
*
* Each tracked reference adds bookkeeping to terminal mutations. Use them
* sparingly for long-lived anchors such as selections, search state, marks,
@@ -85,8 +86,8 @@ extern "C" {
* operation (including free).
*
* A tracked reference is allocated and must be freed when it is no
* longer needed. All tracked references must be freed prior to the
* terminal being freed.
* longer needed. A tracked reference may outlive the terminal that created it;
* after terminal free, it reports no value and can still be freed.
*
* ## Examples
*

View File

@@ -27,8 +27,8 @@ extern "C" {
/**
* Free a tracked grid reference.
*
* Passing NULL is allowed and has no effect. The reference must be freed before
* the terminal that created it is freed.
* Passing NULL is allowed and has no effect. A tracked reference may be freed
* after the terminal that created it is freed.
*
* @param ref Tracked grid reference to free.
*
@@ -39,6 +39,9 @@ GHOSTTY_API void ghostty_tracked_grid_ref_free(GhosttyTrackedGridRef ref);
/**
* Return whether a tracked grid reference currently has a meaningful value.
*
* If the terminal that created the tracked reference has been freed, this
* returns false.
*
* @param ref Tracked grid reference.
* @return true if the reference currently has a meaningful value.
*
@@ -62,7 +65,8 @@ GHOSTTY_API bool ghostty_tracked_grid_ref_has_value(
*
* If the tracked reference no longer has a meaningful value, this returns
* GHOSTTY_NO_VALUE. GHOSTTY_NO_VALUE is also returned when the reference cannot
* be represented in the requested coordinate space.
* be represented in the requested coordinate space, including after the
* terminal that created the tracked reference has been freed.
*
* @param ref Tracked grid reference.
* @param tag Coordinate space to convert into.
@@ -114,7 +118,8 @@ GHOSTTY_API GhosttyResult ghostty_tracked_grid_ref_set(
* ghostty_grid_ref_style().
*
* If the tracked reference no longer has a meaningful value, this returns
* GHOSTTY_NO_VALUE.
* GHOSTTY_NO_VALUE. This includes references whose owning terminal has been
* freed.
*
* @param ref Tracked grid reference.
* @param[out] out_ref On success, receives an untracked snapshot. May be NULL.

View File

@@ -1134,8 +1134,9 @@ GHOSTTY_API GhosttyResult ghostty_terminal_grid_ref(GhosttyTerminal terminal,
* If the point is outside the requested coordinate space, this returns
* GHOSTTY_INVALID_VALUE and writes NULL to out_ref.
*
* The returned handle must be freed with ghostty_tracked_grid_ref_free() before
* the terminal is freed.
* The returned handle must be freed with ghostty_tracked_grid_ref_free(). If
* the terminal is freed first, the handle remains valid only for
* tracked-grid-ref APIs: it reports no value and can still be freed.
*
* @param terminal Terminal instance.
* @param point Point to track.

View File

@@ -98,7 +98,9 @@ typedef struct GhosttyTerminalImpl* GhosttyTerminal;
* Opaque handle to a tracked grid reference.
*
* A tracked grid reference is owned by the caller and must be freed with
* ghostty_tracked_grid_ref_free() before the terminal that created it is freed.
* ghostty_tracked_grid_ref_free(). If the terminal that created it is freed
* first, the handle remains valid only for tracked-grid-ref APIs: it reports no
* value and can still be freed.
*
* @ingroup grid_ref
*/