镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

[api] Answer nested requests on the sync connection in stack order - #64639

Merged
Jake Bailey (jakebailey) merged 2 commits into
microsoft:mainfrom
cplieger:fix-api-sync-conn-stack-order
Oct 9, 2026
Merged

Jake Bailey (jakebailey) merged 2 commits into
microsoft:mainfrom
cplieger:fix-api-sync-conn-stack-order

Conversation

@cplieger

@cplieger Christopher Plieger (cplieger) commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

While SyncConn.Call handles a nested request from a client callback, it releases its lock, so another goroutine's call can start inside the client's pending nested request. Responses are matched by method name, so the nested answer and both callback answers then reach the wrong requests.

Impact: a resolver callback that delegates to another resolver, the pattern the API's own test shows, binds most imports to the wrong module once the loader uses more than one thread. Every type and diagnostic the tool reads from that program is then wrong, and no error says so. In the reproduction, 873 of 900 nested answers went to another request.

The sync connection now answers exchanges in the order a synchronous client can handle them. It counts the calls in flight and records whether the innermost one is reading its response. A new call waits while one is reading. A message the client sends from inside a callback clears that flag, so the handler's own calls go through, and the call that received it reads again only once every call made above it has returned. A request is answered at the same point, which is what keeps nested answers from crossing.

TestSyncConnAnswersNestedRequestsInStackOrder drives two overlapping callbacks against a fake synchronous client under testing/synctest. It fails on each of 20 runs without the change. TestSyncConnNotificationHandlerCanCall keeps a call made from a notification handler working, as it does on main. With a tsc built from this branch, the reproduction in the issue gives 900 right answers of 900.

I met this while building deadset-ts, the TypeScript analyzer of deadset, on the TypeScript 7 API, as part of a small set of defects that work hit, which is why there are a few related reports from me.

An AI coding agent wrote this patch. I have read, built and tested it and will handle the review.

  • There is an associated issue in the Backlog milestone (required)
  • Code is up-to-date with the main branch
  • You've successfully run npx hereby test
  • You've successfully run npx hereby lint
  • You've successfully run npx hereby check:format
  • There are new or updated tests validating the change

Fixes #64631

Copilot AI balanced review requested due to automatic review settings October 5, 2026 10:28
@typescript-automation typescript-automation Bot added For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Oct 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The bidirectional synchronization and panic-recovery behavior warrant final human review despite no concrete blocking findings.

Review effort: Balanced
Findings: None

What changed in this PR

Fixes #64631 by coordinating synchronous IPC exchanges in stack order so overlapping callbacks receive the correct responses.

Changes:

  • Tracks in-flight exchanges and waits before starting calls or answering requests.
  • Adds regression tests for nested response ordering and response-write panic handling.
File Description
tsc/​internal/​ipc/​conn_sync.go Adds stack-based coordination for calls and responses.
tsc/​internal/​ipc/​conn_sync_test.go Tests overlapping nested requests and panic propagation.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is correct and I don't see a simpler way to do it, but Jake Bailey (@jakebailey) has talked me out of using a sync.Cond before, so he may want to take a look.

@jakebailey

Copy link
Copy Markdown
Member

Yeah, I blindly assigned this to you, but now that I look, I suspect there's a better design here

@andrewbranch

Copy link
Copy Markdown
Member

I had tried to think about blocking "unrelated" client callbacks until the current request stack is clear, but it turns out it's kind of hard to determine what's "unrelated" vs. what's truly reentrant here. I think you could do something with context plumbing, but I think that would end up being a lot more complicated.

@cplieger

Copy link
Copy Markdown
Contributor Author

Jake Bailey (@jakebailey) Andrew Branch (@andrewbranch) thanks both, I pushed a smaller version.

  • Smaller shape (as Jake suspected): the stack, push/pop and the answered flag are gone. Two fields, a call count and a reading flag, plus one lockTurn carry the same rule.
  • Context plumbing (as Andrew suggested): I tried it first. The filesystem callbacks go through vfs.FS, which takes no context, so a file read made while serving a nested request looks unrelated and waits on the call that is waiting for it. Serialising top-level calls hits the same question.
  • sync.Cond (following Andrew's note): a channel version came out as a larger hand-written condition variable, so I kept the Cond. Its Wait is no less cancellable than the Lock beside it. Happy to switch if you still prefer channels.
  • Notifications: a call from a notification handler no longer blocks. One limit stays exactly as on main: while a notification handler runs, an unrelated call can still start. msgpack has no notifications, so the API server cannot reach it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Reentrant IPC synchronization needs human review of concurrent interleavings and error paths without runtime validation here.

Review effort: Balanced
Findings: None

@jakebailey Jake Bailey (jakebailey) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the latest push, I think this is fine.

@andrewbranch
Andrew Branch (andrewbranch) added this pull request to the merge queue Oct 9, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 9, 2026
@jakebailey
Jake Bailey (jakebailey) added this pull request to the merge queue Oct 9, 2026
Merged via the queue into microsoft:main with commit aad4c72 Oct 9, 2026
29 checks passed
@cplieger
Christopher Plieger (cplieger) deleted the fix-api-sync-conn-stack-order branch October 9, 2026 19:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[api] A nested request from a resolveModuleName callback gets another request's answer

4 participants