From 489dd89a45d8c354c8b69b9e6f5ba379250c472a Mon Sep 17 00:00:00 2001 From: Nikolay Nikolov Date: Sat, 25 Nov 2023 12:50:27 +0200 Subject: [PATCH] backport to v 2.0: nimsuggest: Added optional command line option '--clientProcessId:XXX' (#22969) (#22981) When it is specified, the nimsuggest instance monitors whether this process is still alive. In case it's found to be dead, nimsuggest shuts itself down. Currently only implemented on POSIX and Windows platforms. The switch is silently ignored on other platforms. Note that the Nim language server should still try to shut down its child nimsuggest processes. This switch just adds extra protection against crashing Nim language server and gets rid of the remaining nimsuggest processes, which consume memory and system resources. (cherry picked from commit 502a4486aeb8d0a5dcdf86540522d3dc16960536) --- compiler/options.nim | 2 ++ nimsuggest/nimsuggest.nim | 7 +++++++ nimsuggest/procmonitor.nim | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 nimsuggest/procmonitor.nim diff --git a/compiler/options.nim b/compiler/options.nim index d3a96301ac..ea3b71a568 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -432,6 +432,8 @@ type expandNodeResult*: string expandPosition*: TLineInfo + clientProcessId*: int + proc parseNimVersion*(a: string): NimVer = # could be moved somewhere reusable diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 36fa9c7287..2ffe06c8f6 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -12,6 +12,7 @@ import strformat import algorithm import tables import times +import procmonitor template tryImport(module) = import module @@ -56,6 +57,7 @@ Options: --address:HOST binds to that address, by default "" --stdin read commands from stdin and write results to stdout instead of using sockets + --clientProcessId:PID shutdown nimsuggest in case this process dies --epc use emacs epc mode --debug enable debug output --log enable verbose logging to nimsuggest.log file @@ -605,6 +607,9 @@ proc mainCommand(graph: ModuleGraph) = open(requests) open(results) + if graph.config.clientProcessId != 0: + hookProcMonitor(graph.config.clientProcessId) + case gMode of mstdin: createThread(inputThread, replStdin, (gPort, gAddress)) of mtcp: createThread(inputThread, replTcp, (gPort, gAddress)) @@ -680,6 +685,8 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = conf.suggestMaxResults = parseInt(p.val) of "find": findProject = true + of "clientprocessid": + conf.clientProcessId = parseInt(p.val) else: processSwitch(pass, p, conf) of cmdArgument: let a = unixToNativePath(p.key) diff --git a/nimsuggest/procmonitor.nim b/nimsuggest/procmonitor.nim new file mode 100644 index 0000000000..0f1ba1e0d3 --- /dev/null +++ b/nimsuggest/procmonitor.nim @@ -0,0 +1,34 @@ +# Monitor a client process and shutdown the current process, if the client +# process is found to be dead + +import os + +when defined(posix): + import posix_utils + import posix + +when defined(windows): + import winlean + +when defined(posix): + proc monitorClientProcessIdThreadProc(pid: int) {.thread.} = + while true: + sleep(1000) + try: + sendSignal(Pid(pid), 0) + except: + discard kill(Pid(getCurrentProcessId()), cint(SIGTERM)) + +when defined(windows): + proc monitorClientProcessIdThreadProc(pid: int) {.thread.} = + var process = openProcess(SYNCHRONIZE, 0, DWORD(pid)) + if process != 0: + discard waitForSingleObject(process, INFINITE) + discard closeHandle(process) + quit(0) + +var tid: Thread[int] + +proc hookProcMonitor*(pid: int) = + when defined(posix) or defined(windows): + createThread(tid, monitorClientProcessIdThreadProc, pid)