Skip to content

Commit 89bba79

Browse files
committed
rework async request handling
use map instead of iterating over all pending requests demonitor cancelled requests
1 parent c9bbece commit 89bba79

File tree

3 files changed

+87
-60
lines changed

3 files changed

+87
-60
lines changed

apps/elixir_ls_debugger/lib/debugger/server.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ defmodule ElixirLS.Debugger.Server do
539539
{:noreply, %{state | task_ref: nil}}
540540
end
541541

542-
def handle_info({:DOWN, _ref, :process, pid, reason}, state = %__MODULE__{}) do
542+
def handle_info({:DOWN, ref, :process, pid, reason}, state = %__MODULE__{}) do
543543
paused_processes_count_before = map_size(state.paused_processes)
544544
state = handle_process_exit(state, pid)
545545
paused_processes_count_after = map_size(state.paused_processes)
@@ -556,7 +556,7 @@ defmodule ElixirLS.Debugger.Server do
556556

557557
{updated_requests, updated_progresses} =
558558
if seq do
559-
{{_pid, _ref, packet}, updated_requests} = Map.pop!(state.requests, seq)
559+
{{^pid, ^ref, packet}, updated_requests} = Map.pop!(state.requests, seq)
560560

561561
Output.send_error_response(
562562
packet,

apps/language_server/lib/language_server/server.ex

Lines changed: 84 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ defmodule ElixirLS.LanguageServer.Server do
5858
analysis_ready?: false,
5959
received_shutdown?: false,
6060
requests: %{},
61+
requests_ids_by_pid: %{},
6162
# Tracks source files that are currently open in the editor
6263
source_files: %{},
6364
awaiting_contracts: [],
@@ -160,39 +161,48 @@ defmodule ElixirLS.LanguageServer.Server do
160161
def handle_call({:request_finished, id, result}, _from, state = %__MODULE__{}) do
161162
# in case the request has been cancelled we silently ignore
162163
{popped_request, updated_requests} = Map.pop(state.requests, id)
163-
if popped_request do
164-
{{_pid, command, start_time}, requests} = popped_request
165-
case result do
166-
{:error, type, msg, send_telemetry} ->
167-
JsonRpc.respond_with_error(id, type, msg)
168164

169-
if send_telemetry do
165+
requests_ids_by_pid =
166+
if popped_request do
167+
{pid, ref, command, start_time} = popped_request
168+
# we are no longer interested in :DOWN message
169+
Process.demonitor(ref, [:flush])
170+
171+
case result do
172+
{:error, type, msg, send_telemetry} ->
173+
JsonRpc.respond_with_error(id, type, msg)
174+
175+
if send_telemetry do
176+
JsonRpc.telemetry(
177+
"lsp_request_error",
178+
%{
179+
"elixir_ls.lsp_command" => String.replace(command, "/", "_"),
180+
"elixir_ls.lsp_error" => to_string(type),
181+
"elixir_ls.lsp_error_message" => msg || to_string(type)
182+
},
183+
%{}
184+
)
185+
end
186+
187+
{:ok, result} ->
188+
elapsed = System.monotonic_time(:millisecond) - start_time
189+
JsonRpc.respond(id, result)
190+
170191
JsonRpc.telemetry(
171-
"lsp_request_error",
192+
"lsp_request",
193+
%{"elixir_ls.lsp_command" => String.replace(command, "/", "_")},
172194
%{
173-
"elixir_ls.lsp_command" => String.replace(command, "/", "_"),
174-
"elixir_ls.lsp_error" => type,
175-
"elixir_ls.lsp_error_message" => msg
176-
},
177-
%{}
195+
"elixir_ls.lsp_request_time" => elapsed
196+
}
178197
)
179-
end
180-
181-
{:ok, result} ->
182-
elapsed = System.monotonic_time(:millisecond) - start_time
183-
JsonRpc.respond(id, result)
198+
end
184199

185-
JsonRpc.telemetry(
186-
"lsp_request",
187-
%{"elixir_ls.lsp_command" => String.replace(command, "/", "_")},
188-
%{
189-
"elixir_ls.lsp_request_time" => elapsed
190-
}
191-
)
200+
Map.delete(state.requests_ids_by_pid, pid)
201+
else
202+
state.requests_ids_by_pid
192203
end
193-
end
194204

195-
state = %{state | requests: updated_requests}
205+
state = %{state | requests: updated_requests, requests_ids_by_pid: requests_ids_by_pid}
196206
{:reply, :ok, state}
197207
end
198208

@@ -314,30 +324,35 @@ defmodule ElixirLS.LanguageServer.Server do
314324
end
315325

316326
@impl GenServer
317-
def handle_info({:DOWN, _ref, :process, pid, reason}, %__MODULE__{requests: requests} = state) do
318-
state =
319-
case Enum.find(requests, &match?({_, {^pid, _, _}}, &1)) do
320-
{id, {^pid, command, _start_time}} ->
321-
error_msg = Exception.format_exit(reason)
322-
JsonRpc.respond_with_error(id, :server_error, error_msg)
327+
def handle_info(
328+
{:DOWN, ref, :process, pid, reason},
329+
%__MODULE__{requests: requests, requests_ids_by_pid: requests_ids_by_pid} = state
330+
) do
331+
{id, updated_requests_ids_by_pid} = Map.pop(requests_ids_by_pid, pid)
323332

324-
JsonRpc.telemetry(
325-
"lsp_request_error",
326-
%{
327-
"elixir_ls.lsp_command" => String.replace(command, "/", "_"),
328-
"elixir_ls.lsp_error" => :server_error,
329-
"elixir_ls.lsp_error_message" => error_msg
330-
},
331-
%{}
332-
)
333+
updated_requests =
334+
if id do
335+
{{^pid, ^ref, command, _start_time}, updated_requests} = Map.pop!(requests, id)
336+
error_msg = Exception.format_exit(reason)
337+
JsonRpc.respond_with_error(id, :server_error, error_msg)
333338

334-
%{state | requests: Map.delete(requests, id)}
339+
JsonRpc.telemetry(
340+
"lsp_request_error",
341+
%{
342+
"elixir_ls.lsp_command" => String.replace(command, "/", "_"),
343+
"elixir_ls.lsp_error" => :server_error,
344+
"elixir_ls.lsp_error_message" => error_msg
345+
},
346+
%{}
347+
)
335348

336-
nil ->
337-
state
349+
updated_requests
350+
else
351+
requests
338352
end
339353

340-
{:noreply, state}
354+
{:noreply,
355+
%{state | requests: updated_requests, requests_ids_by_pid: updated_requests_ids_by_pid}}
341356
end
342357

343358
@impl GenServer
@@ -426,19 +441,26 @@ defmodule ElixirLS.LanguageServer.Server do
426441
state
427442
end
428443

429-
defp handle_notification(cancel_request(id), %__MODULE__{requests: requests} = state) do
430-
case requests do
431-
%{^id => {pid, _command, _start_time}} ->
444+
defp handle_notification(
445+
cancel_request(id),
446+
%__MODULE__{requests: requests, requests_ids_by_pid: requests_ids_by_pid} = state
447+
) do
448+
{request, updated_requests} = Map.pop(requests, id)
449+
450+
updated_requests_ids_by_pid =
451+
if request do
452+
{pid, ref, _command, _start_time} = request
453+
# we are no longer interested in :DOWN message
454+
Process.demonitor(ref, [:flush])
432455
Process.exit(pid, :cancelled)
433456
JsonRpc.respond_with_error(id, :request_cancelled, "Request cancelled")
457+
Map.delete(requests_ids_by_pid, pid)
458+
else
459+
Logger.debug("Received $/cancelRequest for unknown request id: #{inspect(id)}")
460+
requests_ids_by_pid
461+
end
434462

435-
%{state | requests: Map.delete(requests, id)}
436-
437-
_ ->
438-
Logger.warning("Received $/cancelRequest for unknown request id: #{inspect(id)}")
439-
440-
state
441-
end
463+
%{state | requests: updated_requests, requests_ids_by_pid: updated_requests_ids_by_pid}
442464
end
443465

444466
# We don't start performing builds until we receive settings from the client in case they've set
@@ -729,8 +751,13 @@ defmodule ElixirLS.LanguageServer.Server do
729751
state
730752

731753
{:async, fun, state} ->
732-
{pid, _ref} = handle_request_async(id, fun)
733-
%{state | requests: Map.put(state.requests, id, {pid, command, start_time})}
754+
{pid, ref} = handle_request_async(id, fun)
755+
756+
%{
757+
state
758+
| requests: Map.put(state.requests, id, {pid, ref, command, start_time}),
759+
requests_ids_by_pid: Map.put(state.requests_ids_by_pid, pid, id)
760+
}
734761
end
735762
rescue
736763
e in InvalidParamError ->

apps/language_server/test/server_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1448,7 +1448,7 @@ defmodule ElixirLS.LanguageServer.ServerTest do
14481448
"method" => "window/logMessage",
14491449
"params" => %{
14501450
"message" => "Received $/cancelRequest for unknown request" <> _,
1451-
"type" => 2
1451+
"type" => 4
14521452
}
14531453
},
14541454
1000

0 commit comments

Comments
 (0)