Skip to content

Perf CRITICAL: F12 (onDefinition) took 54s with a 38s synchronous event-loop freeze on the real solution #360

Description

@msarson

Symptom ([358 BUILD] retest, 2026-07-14 — F12 on IBSCommon.clw, ~1 min after startup settled)

[11:38:54.524] LSP slow handler | kind=request, method=onDefinition, ms=53899
[11:38:54.524] EventLoop lag | max_blocked_ms=38388, since_module_load_ms=90318

A single Go To Definition took 53.9 seconds, of which 38.4 seconds was one contiguous synchronous event-loop block — the editor was frozen solid for 38s. The F12 resolved to SelectJobNumber_IBSCommon.clw (opened immediately after), so it was a real navigation, but the cost is catastrophic and it is the single worst interactive stall observed in any retest (prior banked note had F12 at 14.9s QUEUED; this is far worse and largely SYNCHRONOUS, not just queued).

What is known

  • connection.onDefinition (server.ts:2830) is a thin wrapper over definitionProvider.provideDefinition(document, position). The 38s synchronous block is inside that call (or something it invokes) — no yield points, so the whole editor froze.
  • Concrete gap: onDefinition does NOT pass a cancellation token to provideDefinition, whereas onImplementation DOES pass one to provideImplementation (server.ts:2858-2873). So a slow F12 cannot be cancelled even if the user gives up. (Cancellation only helps if the provider has await/yield points — see below.)
  • The definition provider has many resolution tiers (local → MEMBER parent → include chain → SDI → cross-file). One of them did ~38s of unyielded synchronous work on this symbol. Likely suspects given prior findings: repeated synchronous tokenization of the 873KB IBSCommon parent, an unbounded include-chain / candidate walk, or a FAR-style whole-solution scan on a procedure name.

Fix direction (investigate-first — do NOT guess the tier)

  1. Instrument provideDefinition with a per-tier perf breakdown (mirror the undeclaredVar augment detail line: which tier, which symbol, ms each). Ship the instrumentation, get one more F12 log — that names the 38s culprit precisely.
  2. Pass a cancellation token to provideDefinition (parity with onImplementation) so a giving-up user aborts it.
  3. Once the culprit tier is known: add yield points / a bound / a cache so it can never block the loop for tens of seconds (the interactive-request-priority principle — F12/hover must never freeze the editor).

Priority

This is now the top user-facing pathology — a 38s frozen editor on a routine F12 is worse than any startup cost. Surfaced by the #358 retest (which fixed the undeclaredVar cost that previously dominated). Related: banked "interactive-request priority" item; #295 (off-thread); #359 (doc concurrency).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions