xiaofei 1f37291957 gpui_macos: Release the accessibility adapter when a window is dropped (#64143)
## Objective

Closing a GPUI window on macOS does not release that window's native
view tree, so every
open/close cycle leaves a `GPUIView`, its `CAMetalLayer` and the
renderer's command queue
alive in the process. A long-running app that opens and closes windows
grows without bound;
this is the mechanism described in #50737.

The reason is a retain cycle:

```
MacWindowState
  -> accesskit_adapter                       (crates/gpui_macos/src/window.rs:710)
  -> NSWindow.contentView                    (strongly retained by accesskit_macos)
  -> GPUIView                                (a subview of that content view)
  -> Arc<Mutex<MacWindowState>> in its ivar  (back to the start)
```

`accesskit_macos::SubclassingAdapter::for_window` keeps a strong
`Id<NSView>` of the
window's content view, and both the `NSWindow` and the `GPUIView` store
an
`Arc<Mutex<MacWindowState>>` in their `windowState` ivar.
`MacWindow::drop` resets the
delegate and tears down the frame source and the visibility callback,
but nothing drops the
adapter, so the native view tree outlives the window.

Worth noting while reading the existing teardown:
`gpui_apple::metal_renderer::destroy()`
is currently a no-op (`crates/gpui_apple/src/metal_renderer.rs`), so the
`renderer.destroy()`
call that is already there is not evidence that GPU-side resources were
released — what
matters is whether the state that owns the renderer can run its
destructor at all.

## Solution

One line in `MacWindow::drop`:

```rust
let mut this = self.0.lock();
drop(this.accesskit_adapter.take());
this.renderer.destroy();
```

Nothing else in the teardown changes:

- `frame_source.take()` stays. `WindowFrameSource`'s `Drop` cancels
`frame_requests`, and
that dispatch source's context points at the native view, so it has to
be cancelled while
  the view is still valid.
- `visibility_callback.take()` stays, as do the sheet handling and the
delegate reset.
- No view or layer is released by hand. The native view is already
handed to its parent view
  via `autorelease`; the problem is the cycle, not the ownership.

## Testing

Done locally on macOS (aarch64), with `stable`:

- `cargo fmt -p gpui_macos -- --check` — clean.
- `cargo test -p gpui_macos --lib` — 7 passed, 0 failed.
- `cargo clippy -p gpui_macos --release --all-targets --all-features --
--deny warnings` — clean.

Note: the adapter is only created when Zed runs with
`ZED_EXPERIMENTAL_A11Y=1`, so the leak
only affects that path and the fix is a no-op otherwise.

What isn't covered:

- **No automated test.** The cycle needs a real `NSWindow`/`contentView`
pair, and the change
is a `Drop` impl; a unit test here would be testing `NSView` ownership
rather than this
crate's logic. `gpui_macos` has no window-lifecycle test harness today,
and adding one is
  more than this fix should carry.
- **No memory measurement by me.** I did not run
Instruments/`Allocations` against this
change, and the minimal-repro numbers quoted in #50737 were measured on
a different fork
revision, not this one. So the leak here is established from the
ownership graph above
rather than from a footprint curve I produced. The follow-up that would
settle it is a
before/after count of live `GPUIView` / `CAMetalLayer` / command-queue
objects after closing
a window, with accessibility enabled and disabled; I have not run that,
and I am not
  claiming this explains every memory-growth report.
- Only the macOS backend is touched, so there is nothing
Windows/Linux-specific here.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards (UI/UX and icon
guidelines) — no UI change
- [ ] Tests cover the new/changed behavior — not applicable, see "What
isn't covered"
- [x] Performance impact has been considered and is acceptable

## AI disclosure

Built with substantial LLM assistance: I had help reading the
`accesskit_macos` and
`gpui_macos` ownership graph and drafting this description, and I
reviewed every line of the
diff myself. The change is one line plus a comment. English isn't my
first language, so the
prose here was machine-polished; happy to restate anything in plain
terms.

---

Release Notes:

- [GPUI] Fixed a memory leak on macOS where closing a window with
accessibility enabled left its native view, `CAMetalLayer` and the
renderer's resources alive.
- Fixed a memory leak on macOS where closing a window left its native
view, `CAMetalLayer` and the renderer's resources alive.
2026-09-20 08:26:45 +00:00
2026-09-17 09:59:21 +00:00
2026-01-22 23:57:26 +05:30
2026-08-28 20:04:24 +00:00
2026-09-11 09:13:44 +00:00
2026-09-11 09:13:44 +00:00
2025-02-04 09:02:59 -05:00
2025-10-27 13:27:59 -04:00

Zed

Zed CI

Welcome to Zed, a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.


Installation

On macOS, Linux, and Windows you can download Zed directly or install Zed via your local package manager (macOS/Linux/Windows).

Other platforms are not yet available:

Developing Zed

Contributing

See CONTRIBUTING.md for ways you can contribute to Zed.

Also... we're hiring! Check out our jobs page for open roles.

Licensing

Zed source code is licensed primarily under GPL-3.0-or-later, with Apache-2.0 components where marked.

License information for third party dependencies must be correctly provided for CI to pass.

We use cargo-about to automatically comply with open source licenses. If CI is failing, check the following:

  • Is it showing a no license specified error for a crate you've created? If so, add publish = false under [package] in your crate's Cargo.toml.
  • Is the error failed to satisfy license requirements for a dependency? If so, first determine what license the project has and whether this system is sufficient to comply with this license's requirements. If you're unsure, ask a lawyer. Once you've verified that this system is acceptable add the license's SPDX identifier to the accepted array in script/licenses/zed-licenses.toml.
  • Is cargo-about unable to find the license for a dependency? If so, add a clarification field at the end of script/licenses/zed-licenses.toml, as specified in the cargo-about book.

Sponsorship

Zed is developed by Zed Industries, Inc., a for-profit company.

If you’d like to financially support the project, you can do so via GitHub Sponsors. Sponsorships go directly to Zed Industries and are used as general company revenue. There are no perks or entitlements associated with sponsorship.

S
Description
No description provided
Readme
411 MiB
0 Stars 1 Watchers 0 Forks
Languages
Rust 94.9%
JSON-with-Comments 2.7%
Python 0.8%
Inno Setup 0.3%
Tree-sitter Query 0.3%
Other 0.5%