Main daemon socket is unavailable until expensive startup finishes
# Issue draft: main daemon socket can remain unavailable for a long time during startup
## Title
Main daemon socket is unavailable until expensive startup finishes, causing `failed to connect to socket` even while the daemon process is alive
## Body
Hi,
I dug into a Tahoe-era `failed to connect to socket` / “process alive but daemon unusable” case on my machine and found that at least one major class of these reports is a startup critical-path problem rather than an immediate socket/listener failure.
## Environment
- macOS 26.4 Tahoe (`25E246`)
- Apple Silicon
- yabai `v7.1.18`
- many apps/windows open across multiple displays
## Related issues / context
- `#2535` — `failed to connect to socket`
- `#2555` — debug output shows startup deep in `window_manager_begin(..)` while socket/connect failures are reported
- `#2560` — daemon becomes unresponsive
- `#2676` / `#2706` — Tahoe `failed to connect to socket`
- `#2377` — responsiveness issues when some applications are slow/frozen
- `#2595` — app observation can fail or stall randomly
- `#439` — `kAXErrorCannotComplete` / problematic AX observation cases
## What I observed locally
With a source build of `v7.1.18`, starting yabai in the foreground on a machine with many apps open:
```bash
./bin/yabai -V -c /dev/null
```
Before patching:
- the process starts and stays alive
- `/tmp/yabai_$USER.lock` appears quickly
- `/tmp/yabai_$USER.socket` may not appear for a long time (I observed >25s)
- `yabai -m query --displays` fails with `failed to connect to socket`
So the user-visible symptom looks like “daemon alive, socket dead”, but in this case the daemon simply does not expose the main socket until very late in startup.
## Likely reason
`message_loop_begin(g_socket_file)` happens after a long startup sequence, including:
- process manager startup
- display manager startup
- window manager initialization / enumeration
- existing application/window discovery
On a busy machine, that path can take a long time.
After instrumenting startup locally, `window_manager_begin(..)` was the major blocker. Example observations from my system:
- `Google Drive` took ~7s
- `LinkedIn Learning` took ~7s
- several other apps took ~1–2s
- total time before “ready” could exceed 20–45s
## Proposed fix
Expose the message socket earlier in startup, before expensive application/window discovery finishes, and gate command handling until the daemon is ready.
Concretely:
1. start `message_loop_begin(g_socket_file)` immediately after `event_loop_begin(..)`
2. keep a `g_daemon_is_ready` flag
3. if a client connects before startup is complete, return a clean failure response such as:
- `daemon is still initializing; retry shortly.`
This changes the user-visible behavior from:
- `failed to connect to socket`
to:
- reachable daemon with deterministic “still initializing” response
## Local result
After applying that change:
- `/tmp/yabai_$USER.socket` appears within a few seconds on my machine (typically ~2–3s in repeated short runs)
- early `yabai -m query --displays` no longer fails to connect once the socket is present
- it returns `daemon is still initializing; retry shortly.` instead
This does **not** solve the deeper slow-startup cause by itself, but it makes the daemon much more truthful, debuggable, and usable while startup is still in progress.
## Patch / PR
I prepared a patch locally and can open a PR if this approach makes sense upstream.
I can also post a follow-up issue specifically about the heavy `window_manager_begin(..)` critical path if that would be preferable.
1 条评论