Skip to content

Commit d4eff15

Browse files
committed
Automatically name forked processes based on callers
1 parent 0c895fa commit d4eff15

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

lib/ruby_lsp/ruby_lsp_rails/server.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,23 @@ def database_supports_indexing?(model)
529529
end
530530
end
531531

532+
# Patch fork to name processes based on the caller's file path. This is useful for figuring out what is creating more
533+
# child processes from the runtime server, so that we can optimize and more easily debug orphaned processes
534+
# @requires_ancestor: Kernel
535+
module ForkPatch
536+
#: () { () -> void } -> Integer?
537+
def fork(&block)
538+
super do
539+
fork_caller = caller_locations(3, 1)&.first
540+
$0 = "ruby-lsp-rails: #{fork_caller&.path}"
541+
block.call
542+
end
543+
end
544+
545+
Kernel.prepend(self)
546+
Process.singleton_class.prepend(self)
547+
end
548+
532549
if ARGV.first == "start"
533550
RubyLsp::Rails::Server.new(capabilities: JSON.parse(ARGV[1], symbolize_names: true)).start
534551
end

test/ruby_lsp_rails/server_test.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,38 @@ def print_it!
258258
$> = original_stdout
259259
end
260260

261+
test "forked processes are named based on caller" do
262+
skip("Fork is not supported on Windows") if Gem.win_platform?
263+
264+
File.write("my_addon.rb", <<~RUBY)
265+
class MyServerAddon < RubyLsp::Rails::ServerAddon
266+
def name
267+
"MyAddon"
268+
end
269+
270+
def execute(request, params)
271+
pid = fork do
272+
# We can't directly send a message in theses tests because we're using a StringIO as stdout instead of the
273+
# actual pipe, which means that the child process doesn't have access to the same object
274+
File.write("process_name.txt", $0)
275+
end
276+
277+
Process.wait(pid)
278+
send_message({ process_name: File.read("process_name.txt") })
279+
File.delete("process_name.txt")
280+
end
281+
end
282+
RUBY
283+
284+
addon_path = File.expand_path("my_addon.rb")
285+
@server.execute("server_addon/register", server_addon_path: addon_path)
286+
@server.execute("server_addon/delegate", server_addon_name: "MyAddon", request_name: "dsl")
287+
288+
assert_equal(response, { process_name: "ruby-lsp-rails: #{addon_path}" })
289+
ensure
290+
FileUtils.rm("my_addon.rb")
291+
end
292+
261293
private
262294

263295
def response

0 commit comments

Comments
 (0)