-
Notifications
You must be signed in to change notification settings - Fork 383
[circt-lsp-verilog] "Debounce" onDidChange calls; update in worker #9046
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
a81eac8
to
b3a2645
Compare
Super cool, I didn't think about debounce but definitely right direction for scalability! I'll try taking a look at more closely. FYI clangd has similar mechanism so maybe we can learn the architecture https://github.com/llvm/llvm-project/blob/main/clang-tools-extra/clangd/TUScheduler.h |
Nice! I think we are already somewhat close; clangd uses similar debouncing semantics (minimum delay, maximum wait) with adaptive resizing of those delays. It also uses a centralized thread pool to control concurrency. I think we can either push all of this into this PR, or separate it out into individual commits. I think separating it might be nicer, since this PR is already quite large and cerebral 😅 WDYT? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for taking a look! Yes let's separate commits.
e9f3721
to
d5cf3e7
Compare
@uenoku, If you have some more time, please have another look 😄 I factored out the actual scheduling so we can later plug this into a threadpool and refine debouncing semantics further; the LSPServer-side interface should only change marginally. Also added some unit tests. I'd be very grateful about any and all pointers about GTest integration; I gave it a go, but maybe there is a better way. |
Cool! Will take a look! https://github.com/llvm/circt/tree/main/unittests has gtests unittests so writing unittests under that directories would be easier (maybe |
e1969cb
to
44c5480
Compare
I moved the tests to In my opinion I would leave the header files in |
/// Thread-safe function to cancel any outstanding updates | ||
void cancelUpdate() { | ||
if (deb) | ||
deb->cancel(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we using cancelUpdate
and deb->cancel
in non-test code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's used onShutdown
to cancel running updates and through erase
of BucketRegistry
in onDocumentDidClose
for the same reason 😄
lib/Tools/circt-verilog-lsp-server/VerilogServerImpl/VerilogTextFile.cpp
Outdated
Show resolved
Hide resolved
Thanks for giving this a closer look! I appreciate the feedback. I think your concern is valid and it's possible we don't need fine-grained concurrency / multiple docs updating at the same time, I didn't really profile this 😅. What you sketched seems reasonable to me; the main concern I have is that multiple (more or less useless) re-compilations might happen in parallel (e.g. if min delay / max wait are set too optimistically). What we can do is go full-on clangd One way or another I would suggest to extract the asynchronous scheduling parts of this so we can unit test them on their own - I'll give it a go, and we can see which version we like best 😄 |
e3d2c1c
to
f8fea86
Compare
I built a version of the structure you suggested; I took the liberty to pack it into a data structure that lives out I also took the liberty of extracting construction of I also moved the handling of un-debounced calls back to the main thread; this is needed for the I like this version better; I think you were right, it has less surface area for things to go wrong. |
761056c
to
ad9f5a0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you for updating code structure and adding comments about mutex ownership :) It makes much easier to follow the logic. Mostly minor code style comment.
lib/Tools/circt-verilog-lsp-server/VerilogServerImpl/VerilogTextFile.cpp
Show resolved
Hide resolved
lib/Tools/circt-verilog-lsp-server/VerilogServerImpl/VerilogTextFile.cpp
Outdated
Show resolved
Hide resolved
lib/Tools/circt-verilog-lsp-server/VerilogServerImpl/VerilogTextFile.cpp
Outdated
Show resolved
Hide resolved
lib/Tools/circt-verilog-lsp-server/VerilogServerImpl/VerilogTextFile.cpp
Outdated
Show resolved
Hide resolved
lib/Tools/circt-verilog-lsp-server/VerilogServerImpl/VerilogTextFile.cpp
Outdated
Show resolved
Hide resolved
fa11d00
to
54e21aa
Compare
This commit introduces onChange debouncing and thread-safe diagnostics handling for the Verilog LSP server. The main motivation for this is project-scale LSP work, where many files are needed per compilation, and many file buffers are open at the same time; this PR limits the recompile frequency from per-keystroke to per time bin. It also runs the re-elaboration in a separate thread, which avoids locking down everything during elaboration. - Introduces **`LSPServerOptions`** (with `disableDebounce`, `debounceMinMs`, `debounceMaxMs`) and threads it through: - `CirctVerilogLspServerMain` signature now takes `(const LSPServerOptions &, const VerilogServerOptions &, JSONTransport &)`. - `runVerilogLSPServer` signature updated similarly. - Adds **debounced change handling** via new utility: - New `Utils/PendingChanges.h` with: - `DebounceOptions` (+ factory `fromLSPOptions`). - `PendingChanges` (accumulated edits + timestamps). - `PendingChangesMap` (thread-safe accumulator + pool-based `debounceAndThen` / `debounceAndUpdate`). - `LSPServer` now owns `PendingChangesMap` + `DebounceOptions`. - Makes **diagnostic publishing thread-safe**: - `LSPServer` adds `sendDiagnostics()` (mutex-guarded) and a `diagnosticsMutex`. - Changes **document change flow**: - `onDocumentDidChange` now calls `pendingChanges.debounceAndUpdate(...)` and only rebuilds/publishes when the debounce callback fires (skips obsolete updates). - Adds **CLI flags** to tune debouncing in `circt-verilog-lsp-server.cpp`: - `--no-debounce`, `--debounce-min-ms`, `--debounce-max-ms`. - Test mode (`--test`) forces synchronous behavior (no debounce). - Improves **VerilogTextFile** thread-safety and lifetime: - Protects `contents` and `document` with `std::shared_mutex`. - Switches `document` to `std::shared_ptr`, adds `getDocument()` / `setDocument()`. - `update()` now locks, applies changes, and reinitializes via `initialize()`. - Call sites use `getDocument()` before accessing. - Wires **LSP options** into server construction: - `CirctVerilogLspServerMain.cpp` passes `LSPServerOptions` into `runVerilogLSPServer`. - `LSPServer` constructor accepts `LSPServerOptions`, builds `DebounceOptions` from it. - Adds **unit tests** for debouncing: - New `unittests/Tools/circt-verilog-lsp-server/Utils/PendingChangesTest.cpp`. - Tests immediate flush (no debounce), quiet-window flush, obsolescence when newer edits arrive, cap-based flush during continuous typing, and missing-key behavior. - CMake wiring for tests (`unittests/*/CMakeLists.txt`) and a header-only utils target (`CIRCTVerilogLspUtilsHeaders`). - Minor/CMake updates: - Exposes `Utils` headers to tests via interface target. - Includes `Utils/PendingChanges.h` in the `Utils` library. - Small includes and namespace adjustments in `LSPServer.cpp`/`.h`. - Smooths out rapid change events into a single rebuild pass. - Prevents transport corruption by serializing outbound notifications. - Eliminates race conditions on document lifetime during concurrent access. - Provides clean shutdown with all debounced tasks canceled safely. Currently the `VerilogTextfile` resolves definition/reference calls on the "current" VerilogDocument without taking into account changes that have been committed in the meantime. It might lead to a nicer user experience to heuristically update the in-flight definition/reference requests with the changebuffer queue.
54e21aa
to
dc6fddd
Compare
Cool! |
This commit introduces onChange debouncing and thread-safe diagnostics handling
for the Verilog LSP server. The main motivation for this is project-scale LSP work,
where many files are needed per compilation, and many file buffers are open at the same
time; this PR limits the recompile frequency from per-keystroke to per time bin.
It also runs the re-elaboration in a separate thread, which avoids locking down everything during elaboration.
Introduces
LSPServerOptions
(withdisableDebounce
,debounceMinMs
,debounceMaxMs
) and threads it through:CirctVerilogLspServerMain
signature now takes(const LSPServerOptions &, const VerilogServerOptions &, JSONTransport &)
.runVerilogLSPServer
signature updated similarly.Adds debounced change handling via new utility:
Utils/PendingChanges.h
with:DebounceOptions
(+ factoryfromLSPOptions
).PendingChanges
(accumulated edits + timestamps).PendingChangesMap
(thread-safe accumulator + pool-baseddebounceAndThen
/debounceAndUpdate
).LSPServer
now ownsPendingChangesMap
+DebounceOptions
.Makes diagnostic publishing thread-safe:
LSPServer
addssendDiagnostics()
(mutex-guarded) and adiagnosticsMutex
.Changes document change flow:
onDocumentDidChange
now callspendingChanges.debounceAndUpdate(...)
and only rebuilds/publishes when the debounce callback fires (skips obsolete updates).Adds CLI flags to tune debouncing in
circt-verilog-lsp-server.cpp
:--no-debounce
,--debounce-min-ms
,--debounce-max-ms
.--test
) forces synchronous behavior (no debounce).Improves VerilogTextFile thread-safety and lifetime:
contents
anddocument
withstd::shared_mutex
.document
tostd::shared_ptr
, addsgetDocument()
/setDocument()
.update()
now locks, applies changes, and reinitializes viainitialize()
.getDocument()
before accessing.Wires LSP options into server construction:
CirctVerilogLspServerMain.cpp
passesLSPServerOptions
intorunVerilogLSPServer
.LSPServer
constructor acceptsLSPServerOptions
, buildsDebounceOptions
from it.Adds unit tests for debouncing:
unittests/Tools/circt-verilog-lsp-server/Utils/PendingChangesTest.cpp
.unittests/*/CMakeLists.txt
) and a header-only utils target (CIRCTVerilogLspUtilsHeaders
).Minor/CMake updates:
Utils
headers to tests via interface target.Utils/PendingChanges.h
in theUtils
library.LSPServer.cpp
/.h
.Smooths out rapid change events into a single rebuild pass.
Prevents transport corruption by serializing outbound notifications.
Eliminates race conditions on document lifetime during concurrent access.
Currently the
VerilogTextfile
resolves definition/reference calls on the"current" VerilogDocument without taking into account changes that have been committed
in the meantime. It might lead to a nicer user experience to heuristically
update the in-flight definition/reference requests with the changebuffer queue.