Skip to content

Conversation

@Felixoid
Copy link

@Felixoid Felixoid commented Jan 1, 2026

PR Prelude

Thank you for working on YCM! :)

Please complete these steps and check these boxes (by putting an x inside
the brackets) before filing your PR:

  • I have read and understood YCM's CONTRIBUTING document.
  • I have read and understood YCM's CODE_OF_CONDUCT document.
  • I have included tests for the changes in my PR. If not, I have included a
    rationale for why I haven't.
  • I understand my PR may be closed if it becomes obvious I didn't
    actually perform all of these steps.

Why this change is necessary and useful

Vim hangs when editing Rust files with YCM + rust-analyzer. The PR fixes #4272.

MessagesPoll.Poll uses blocking HandleFuture, that's why it happens. This causes the main vim process to hang for a few seconds. HandleFuture() performs heavy synchronous operations:

  1. User interaction dialogs - UnknownExtraConf prompts that block waiting for user input.
  2. Vim UI updates - DisplayServerException() status line updates.
  3. Complex exception handling - multiple try/except layers with logging.
  4. Python evaluation overhead - the parf+flamegraph showed 649k+ CPU cycles in 5 seconds while debugging.

All of this runs synchronously in vim's main thread during timer callbacks, freezing the UI.

The fix still calls response.read(), which is technically blocking I/O. However, this is acceptable because:

  • Future is already done() - we only call read() after done() returns True, meaning the HTTP response is complete
  • Localhost communication - ycmd server runs on localhost, so network latency is negligible
  • Data already received - read() just copies from the response buffer to memory, no actual network wait
  • Minimal processing - just JSON parsing, no user interaction or complex exception handling

This change is Reviewable

response = self.HandleFuture( self._response_future,
display_message = False )
# Non-blocking extraction since done() is True. We avoid HandleFuture()
# because it calls response.read() which blocks on large responses.
Copy link
Member

Choose a reason for hiding this comment

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

this code also calls response.read() so please can you explain in the comment and in detail in the PR description why this fixes the issue?

@puremourning
Copy link
Member

Thanks for the PR!

I'm still a bit confused why this change actually fixes it though. I get that if there's a huge message we have to block to read it, but this code still has to read it and it's still going to run in the vim main thread.

@codecov
Copy link

codecov bot commented Jan 1, 2026

Codecov Report

❌ Patch coverage is 90.90909% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 75.38%. Comparing base (d11e24d) to head (8043981).
⚠️ Report is 12 commits behind head on master.

❌ Your changes status has failed because you have indirect coverage changes. Learn more about Unexpected Coverage Changes and reasons for indirect coverage changes.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4325      +/-   ##
==========================================
+ Coverage   75.29%   75.38%   +0.09%     
==========================================
  Files          30       30              
  Lines        2898     2913      +15     
==========================================
+ Hits         2182     2196      +14     
- Misses        716      717       +1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Felixoid
Copy link
Author

Felixoid commented Jan 1, 2026

Sure, added it to the PR description and extended the code comment.

display_message = False )
# Avoid HandleFuture() to prevent blocking in timer callbacks.
# HandleFuture() does:
# 1. Complex exception handling (UnknownExtraConf, DisplayServerException)
Copy link
Member

Choose a reason for hiding this comment

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

so? why is this a problem in this case? do we have a trivial/minimal repro case?

# Avoid HandleFuture() to prevent blocking in timer callbacks.
# HandleFuture() does:
# 1. Complex exception handling (UnknownExtraConf, DisplayServerException)
# 2. User dialogs that can block waiting for input
Copy link
Member

Choose a reason for hiding this comment

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

No it doesn't, at least, not in any relevant codepath.

# HandleFuture() does:
# 1. Complex exception handling (UnknownExtraConf, DisplayServerException)
# 2. User dialogs that can block waiting for input
# 3. Vim UI updates during callback execution
Copy link
Member

Choose a reason for hiding this comment

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

So does this code. I mean, that's its literal job : HandlePollResponse will do UI updates, that's what it's for.

# 2. User dialogs that can block waiting for input
# 3. Vim UI updates during callback execution
# By extracting the response directly with minimal error handling, we avoid
# blocking vim's main thread. Note that response.read() is still technically
Copy link
Member

Choose a reason for hiding this comment

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

citation needed?

If response.done() is True then HandleFuture (which just calls JsonFromFuture) will just do the exact thing this code does, except this code skips HMAC validation, which is absolute Nope. Skipping HMAC validation is a security issue and we can't accept it.

I'm honestly not convinced by this change, and the explanation seems ... synthetic to me.

I'm willing to believe there is indeed an issue when there are very big notification messages (such as huge diagnostics) but I'd like to have a more convincing explanation and maybe the flame graph, profile, or whatever was mentioned in the description.

Copy link
Author

Choose a reason for hiding this comment

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

Thank you for the detailed review. I'm sorry you're upset with a generated answer.

I didn't test the fix well enough. Most likely, I concluded it was fixed before the entire dataset was indexed by rust-analyzer, and it still freezes, but I didn't reproduce it before submitting the PR.

flamegraph-1.tar.gz

Here's the original flamegraph + perf.script data I used for it.

flamegraph-2.tar.gz

The second flamegraph looks similar, since I waited until the data was loaded. For the one that showed an improvement, I only have a flamegraph; the perf data has been overwritten.

flamegraph-3.tar.gz

I'll dig a bit deeper into it, but it looks like there's no easy solution.

Thank you for your time, and happy New Year 2026!

@Felixoid
Copy link
Author

Felixoid commented Jan 2, 2026

Sorry for submitting trash. There's no easy fix for this particular issue.

@Felixoid Felixoid closed this Jan 2, 2026
@puremourning
Copy link
Member

Hey, no worries! Thanks for the contribution!

Actually, you've provided some good data here.

I have experienced something similar myself and I actually just remembered that I made a hack in my fork to work around it. The hack was to restrict the length of any given diagnostic range. IIRC the problem was adding text properties that spanned entire functions led to really really bad results client side.

If I get some time I might try to reproduce and fix it.

@Felixoid
Copy link
Author

Felixoid commented Jan 2, 2026

The repro is relatively trivial.

  • Install YCM as Plug 'ycm-core/YouCompleteMe', { 'do': 'git submodule update --init --recursive && ./install.py --rust-completer' }
  • clone https://github.com/mozilla/sccache/
  • open any file in the repo, for example, src/util.rs
  • After the data is loaded and indexed (around 10-40 seconds), editing the file becomes challenging
  • Adding an empty line with a comment // or removing it causes freezes for a second or more

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Vim hangs when YouCompleteMe handles many syntax errors over many lines

2 participants