Skip to content

Commit 2f50bd8

Browse files
authored
Remove excessive mutex synchronization (#3637)
Remove excessive mutex synchronization (#3637) ### Motivation With the improvements to the position scanners (#3612 and #3583), we're now seeing some cases of invalid location being raised, despite no reports of any issues regarding the state of the documents. I think we're making a mistake by trying to apply document operations with higher priority than feature requests. For example, if we receive a request for completion and the user immediately edits the file, the server might process the edit before finishing the completion request and there are no guarantees that the new state of the document can satisfy that original request. I want to propose pushing text synchronization operations to the queue, so that they are processed in order and without locking, which I believe will improve the situation. ### Implementation I start pushing text synchronization operations to the work queue and removed many of the mutex locks that we originally had. Since requests are processed in order, there should not be a chance of a feature request being processed with an incorrect document state. Co-authored-by: vinistock <[email protected]>
1 parent 505f469 commit 2f50bd8

File tree

3 files changed

+40
-53
lines changed

3 files changed

+40
-53
lines changed

lib/ruby_lsp/base_server.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ def start
8383
# The following requests need to be executed in the main thread directly to avoid concurrency issues. Everything
8484
# else is pushed into the incoming queue
8585
case method
86-
when "initialize", "initialized", "textDocument/didOpen", "textDocument/didClose", "textDocument/didChange",
87-
"rubyLsp/diagnoseState"
86+
when "initialize", "initialized", "rubyLsp/diagnoseState"
8887
process_message(message)
8988
when "shutdown"
9089
@global_state.synchronize do
@@ -94,7 +93,7 @@ def start
9493
@writer.write(Result.new(id: message[:id], response: nil).to_hash)
9594
end
9695
when "exit"
97-
@global_state.synchronize { exit(@incoming_queue.closed? ? 0 : 1) }
96+
exit(@incoming_queue.closed? ? 0 : 1)
9897
else
9998
@incoming_queue << message
10099
end

lib/ruby_lsp/document.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,10 @@ def past_expensive_limit?
139139

140140
#: (Hash[Symbol, untyped] start_pos, ?Hash[Symbol, untyped]? end_pos) -> [Integer, Integer?]
141141
def find_index_by_position(start_pos, end_pos = nil)
142-
@global_state.synchronize do
143-
scanner = create_scanner
144-
start_index = scanner.find_char_position(start_pos)
145-
end_index = scanner.find_char_position(end_pos) if end_pos
146-
[start_index, end_index]
147-
end
142+
scanner = create_scanner
143+
start_index = scanner.find_char_position(start_pos)
144+
end_index = scanner.find_char_position(end_pos) if end_pos
145+
[start_index, end_index]
148146
end
149147

150148
private

lib/ruby_lsp/server.rb

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -367,62 +367,56 @@ def run_initialized
367367

368368
#: (Hash[Symbol, untyped] message) -> void
369369
def text_document_did_open(message)
370-
@global_state.synchronize do
371-
text_document = message.dig(:params, :textDocument)
372-
language_id = case text_document[:languageId]
373-
when "erb", "eruby"
374-
:erb
375-
when "rbs"
376-
:rbs
377-
else
378-
:ruby
379-
end
370+
text_document = message.dig(:params, :textDocument)
371+
language_id = case text_document[:languageId]
372+
when "erb", "eruby"
373+
:erb
374+
when "rbs"
375+
:rbs
376+
else
377+
:ruby
378+
end
380379

381-
document = @store.set(
382-
uri: text_document[:uri],
383-
source: text_document[:text],
384-
version: text_document[:version],
385-
language_id: language_id,
386-
)
380+
document = @store.set(
381+
uri: text_document[:uri],
382+
source: text_document[:text],
383+
version: text_document[:version],
384+
language_id: language_id,
385+
)
387386

388-
if document.past_expensive_limit? && text_document[:uri].scheme == "file"
389-
log_message = <<~MESSAGE
390-
The file #{text_document[:uri].path} is too long. For performance reasons, semantic highlighting and
391-
diagnostics will be disabled.
392-
MESSAGE
387+
if document.past_expensive_limit? && text_document[:uri].scheme == "file"
388+
log_message = <<~MESSAGE
389+
The file #{text_document[:uri].path} is too long. For performance reasons, semantic highlighting and
390+
diagnostics will be disabled.
391+
MESSAGE
393392

394-
send_message(
395-
Notification.new(
396-
method: "window/logMessage",
397-
params: Interface::LogMessageParams.new(
398-
type: Constant::MessageType::WARNING,
399-
message: log_message,
400-
),
393+
send_message(
394+
Notification.new(
395+
method: "window/logMessage",
396+
params: Interface::LogMessageParams.new(
397+
type: Constant::MessageType::WARNING,
398+
message: log_message,
401399
),
402-
)
403-
end
400+
),
401+
)
404402
end
405403
end
406404

407405
#: (Hash[Symbol, untyped] message) -> void
408406
def text_document_did_close(message)
409-
@global_state.synchronize do
410-
uri = message.dig(:params, :textDocument, :uri)
411-
@store.delete(uri)
407+
uri = message.dig(:params, :textDocument, :uri)
408+
@store.delete(uri)
412409

413-
# Clear diagnostics for the closed file, so that they no longer appear in the problems tab
414-
send_message(Notification.publish_diagnostics(uri.to_s, []))
415-
end
410+
# Clear diagnostics for the closed file, so that they no longer appear in the problems tab
411+
send_message(Notification.publish_diagnostics(uri.to_s, []))
416412
end
417413

418414
#: (Hash[Symbol, untyped] message) -> void
419415
def text_document_did_change(message)
420416
params = message[:params]
421417
text_document = params[:textDocument]
422418

423-
@global_state.synchronize do
424-
@store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
425-
end
419+
@store.push_edits(uri: text_document[:uri], edits: params[:contentChanges], version: text_document[:version])
426420
end
427421

428422
#: (Hash[Symbol, untyped] message) -> void
@@ -1105,11 +1099,7 @@ def handle_rubocop_config_change(uri)
11051099
@global_state.register_formatter("rubocop_internal", Requests::Support::RuboCopFormatter.new)
11061100

11071101
# Clear all document caches for pull diagnostics
1108-
@global_state.synchronize do
1109-
@store.each do |_uri, document|
1110-
document.clear_cache("textDocument/diagnostic")
1111-
end
1112-
end
1102+
@store.each { |_uri, document| document.clear_cache("textDocument/diagnostic") }
11131103

11141104
# Request a pull diagnostic refresh from the editor
11151105
if @global_state.client_capabilities.supports_diagnostic_refresh

0 commit comments

Comments
 (0)