Skip to content

Conversation

@MichaReiser
Copy link
Member

@MichaReiser MichaReiser commented Jun 1, 2025

Summary

This fixes an error where the ty server didn't exit after receiving the shutdown request.

The root cause was the panic handler which hold on to a client sender channel, preventing the io thread from exiting (because there was still the chance that we would send a message).

I tested that the server now correctly exits with VS code. However, I never see an exit notification when closing VS code, only when restarting the server with the restart command. This is consistent with ruff.

@MichaReiser MichaReiser added server Related to the LSP server ty Multi-file analysis & type inference labels Jun 1, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Jun 1, 2025

mypy_primer results

No ecosystem changes detected ✅

>(
req, BackgroundSchedule::LatencySensitive
),
lsp_types::request::Shutdown::METHOD => {
Copy link
Member Author

Choose a reason for hiding this comment

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

I ended up rewriting the shutdown handling during my investigation and I sort of like this more.

Copy link
Member

Choose a reason for hiding this comment

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

Thank you! I like this a lot, I was wondering myself whether the custom Connection struct could be removed with your recent changes around cancellation / retry.

@MichaReiser MichaReiser marked this pull request as ready for review June 1, 2025 17:39
@MichaReiser MichaReiser changed the title [ty] Fix server doesn't exit after shutdown [ty] Fix server hang after shutdown request Jun 1, 2025
@AlexWaygood AlexWaygood removed their request for review June 1, 2025 17:56

// unregister any previously registered panic hook
// The hook will be restored when this function exits.
let _ = RestorePanicHook {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the main fix. Values assigned to _ are dropped immediately. That means, the panic hook was always restored immediately (and then overridden again).

Statements which assign an expression to an underscore causes the expression to immediately drop instead of extending the expression’s lifetime to the end of the scope. This is usually unintended, especially for types like MutexGuard, which are typically used to lock a mutex for the duration of an entire scope.

It looks like Rust 1.88 will add a lint for this source

The fix here is to assign the panic hook to a variable other than _. The Drop handler then restores the original panic hook, which in turn, drops our custom panic hook handler that holds on to the client

@MichaReiser MichaReiser force-pushed the micha/server-shutdown branch from ec40828 to 8a2a4fb Compare June 1, 2025 19:04
Copy link
Member

@dhruvmanila dhruvmanila left a comment

Choose a reason for hiding this comment

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

Thank you for the quick fix! The issue seems very subtle, I think we should enable the lint rule when we can at least for the server code.

I've been also wondering whether it would make sense to invest some time to having code sharing between the Ruff and ty language server where we can. It might be possible to share the the infrastructure code i.e., the ones that involves creating the event loop.

I tested this in Neovim, the server exists successfully:

   0.454553583s DEBUG     ty:main ty_server::server::api: Received shutdown request, waiting for shutdown notification.
   0.471809916s DEBUG     ty:main ty_server::server::main_loop: Received exit notification, exiting
   0.481823416s  INFO        main ty_server: Server shut down

I also looked at the running processes and it now only contains the server process from main.

}

// Handle the response from the client to a server request
Message::Response(response) => {
Copy link
Member

Choose a reason for hiding this comment

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

I'm assuming we don't need to modify anything in this branch mainly because the server would stop handling any request / notification from the client when shutdown has been initiated and so the server wouldn't try to send any request to the client which means there shouldn't be any client responses to handle during the shutdown process.

Copy link
Member Author

Choose a reason for hiding this comment

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

We could add the handling to notifications, because they're explicitly listed in the LSP specification. It's less clear to me if client responses are excluded too and it's quiet possible that the server might send a request from a background task when the shutdown was already initialized.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll leave it as is. The LSP specification only mentions:

If a server receives requests after a shutdown request those requests should error with InvalidRequest.

Processing notifications and responses is a bit wastefull but shouldn't harm too much (they are all very short)

>(
req, BackgroundSchedule::LatencySensitive
),
lsp_types::request::Shutdown::METHOD => {
Copy link
Member

Choose a reason for hiding this comment

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

Thank you! I like this a lot, I was wondering myself whether the custom Connection struct could be removed with your recent changes around cancellation / retry.

@MichaReiser
Copy link
Member Author

I've been also wondering whether it would make sense to invest some time to having code sharing between the Ruff and ty language server where we can. It might be possible to share the the infrastructure code i.e., the ones that involves creating the event loop.

It might be more work than it's worth it. The request handlers, session, etc look very different between ruff and ty

@MichaReiser MichaReiser enabled auto-merge (squash) June 2, 2025 06:55
@MichaReiser MichaReiser merged commit 1e6d76c into main Jun 2, 2025
31 checks passed
@MichaReiser MichaReiser deleted the micha/server-shutdown branch June 2, 2025 06:57
dcreager added a commit that referenced this pull request Jun 2, 2025
…aration

* origin/main:
  [ty] Treat lambda functions as instances of types.FunctionType (#18431)
  [ty] Fix false positives for legacy `ParamSpec`s inside `Callable` type expressions (#18426)
  [ty] Improve diagnostics if the user attempts to import a stdlib module that does not exist on their configured Python version (#18403)
  Update taiki-e/install-action action to v2.52.4 (#18420)
  Update docker/build-push-action action to v6.18.0 (#18422)
  [ty] Fix server hang after shutdown request (#18414)
  Update Rust crate libcst to v1.8.0 (#18424)
  Update Rust crate clap to v4.5.39 (#18419)
  Update cargo-bins/cargo-binstall action to v1.12.6 (#18416)
  Update dependency mdformat-mkdocs to v4.3.0 (#18421)
  Update pre-commit dependencies (#18418)
  Update dependency ruff to v0.11.12 (#18417)
  [ty] Ensure `Literal` types are considered assignable to anything their `Instance` supertypes are assignable to (#18351)
  [ty] Promote projects to good that now no longer hang (#18370)
  Sync vendored typeshed stubs (#18407)
  [ty] Fix multithreading related hangs and panics (#18238)
  Support relative `--ty-path` in ty-benchmark (#18385)
  [ty] Update docs for Python version inference (#18397)
  [ty] Infer the Python version from the environment if feasible (#18057)
  Implement template strings (#17851)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

server Related to the LSP server ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants