Skip to content

Commit 737eea0

Browse files
authored
Improves the way ns processes are spawn. Dont need to use threads anymore. (#234)
1 parent caf576c commit 737eea0

File tree

5 files changed

+38
-65
lines changed

5 files changed

+38
-65
lines changed

nimlangserver.nim

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json_rpc/[servers/socketserver, private/jrpc_sys, jsonmarshal, rpcclient, router]
22
import chronicles, chronos
3-
import std/[ syncio, os, json, strutils, strformat, net]
3+
import std/[ syncio, os, json, strutils, strformat]
44
import ls, routes, suggestapi, utils, lstransports, asyncprocmonitor
55
import protocol/types
66
when defined(posix):
@@ -45,13 +45,6 @@ proc registerRoutes(srv: RpcSocketServer, ls: LanguageServer) =
4545
srv.register("textDocument/didChange", wrapRpc(partial(didChange, ls)))
4646
srv.register("$/setTrace", wrapRpc(partial(setTrace, ls)))
4747

48-
proc getNextFreePort*(): Port=
49-
let s = newSocket()
50-
s.bindAddr(Port(0), "localhost")
51-
let (_, port) = s.getLocalAddr
52-
s.close()
53-
port
54-
5548
proc handleParams(): CommandLineParams =
5649
if paramCount() > 0 and paramStr(1) in ["-v", "--version"]:
5750
echo LSPVersion

nimlangserver.nimble

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ requires "nim == 2.0.8",
1414
"with",
1515
"chronicles",
1616
"serialization",
17-
"json_serialization"
17+
"json_serialization",
18+
"stew"
1819

1920

2021
--path:"."

suggestapi.nim

Lines changed: 21 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import osproc,
1212
chronicles,
1313
protocol/types,
1414
std/options,
15-
chronos
15+
chronos,
16+
chronos/asyncproc,
17+
stew/[byteutils]
1618

1719
const REQUEST_TIMEOUT* = 120000
1820
const HighestSupportedNimSuggestProtocolVersion = 4
@@ -78,7 +80,7 @@ type
7880
openFiles*: OrderedSet[string]
7981
successfullCall*: bool
8082
errorCallback: NimsuggestCallback
81-
process*: Process
83+
process*: AsyncProcessRef
8284
port*: int
8385
root: string
8486
requestQueue: Deque[SuggestCall]
@@ -224,30 +226,11 @@ proc markFailed(self: Nimsuggest, errMessage: string) {.raises: [].} =
224226
if self.errorCallback != nil:
225227
self.errorCallback(self)
226228

227-
proc readPort(param: tuple[ns: ptr NimSuggestImpl, process: Process]) {.thread.} =
228-
try:
229-
var line = param.process.outputStream.readLine & "\n"
230-
param.ns.port = line.strip.parseInt
231-
except CatchableError:
232-
error "Failed to read nimsuggest port"
233-
var msg = failedToken & "\n"
234-
param.ns.port = -1
235-
236-
proc logStderr(param: tuple[root: string, process: Process]) {.thread.} =
237-
try:
238-
var line = param.process.errorStream.readLine
239-
while line != "\0":
240-
stderr.writeLine &"******NIM SUGGEST ERRROR***** {param.root} \n"
241-
stderr.writeLine fmt ">> {line}"
242-
line = param.process.errorStream.readLine
243-
except IOError:
244-
discard
245-
246229
proc stop*(self: Nimsuggest) =
247230
debug "Stopping nimsuggest for ", root = self.root
248231
try:
249-
self.process.kill()
250-
self.process.close()
232+
let res = self.process.kill()
233+
debug "Stopped nimsuggest ", res = res
251234
except Exception as ex:
252235
writeStackTrace(ex)
253236

@@ -306,14 +289,10 @@ proc getNimsuggestCapabilities*(nimsuggestPath: string):
306289
if cap.isSome:
307290
result.incl(cap.get)
308291

309-
proc waitUntilPortIsRead(ns: NimSuggest): Future[void] {.async.} =
310-
while ns.port == 0:
311-
await sleepAsync(10)
312-
313-
if ns.port == -1:
314-
ns.markFailed "Failed to start nimsuggest"
315-
else:
316-
debug "Started nimsuggest", port = ns.port, root = ns.root
292+
proc logNsError(ns: NimSuggest) {.async.} =
293+
let err = string.fromBytes(ns.process.stderrStream.read().await)
294+
error "NimSuggest Error (stderr)", err = err
295+
ns.markFailed(err)
317296

318297
proc createNimsuggest*(root: string,
319298
nimsuggestPath: string,
@@ -324,13 +303,6 @@ proc createNimsuggest*(root: string,
324303
workingDir = getCurrentDir(),
325304
enableLog: bool = false,
326305
enableExceptionInlayHints: bool = false): Future[Nimsuggest] {.async, gcsafe.} =
327-
var
328-
thread: Thread[tuple[ns: ptr NimSuggestImpl, process: Process]]
329-
stderrThread: Thread[tuple[root: string, process: Process]]
330-
331-
info "Starting nimsuggest", root = root, timeout = timeout, path = nimsuggestPath,
332-
workingDir = workingDir
333-
334306
result = Nimsuggest()
335307
result.requestQueue = Deque[SuggestCall]()
336308
result.root = root
@@ -340,11 +312,14 @@ proc createNimsuggest*(root: string,
340312
result.nimSuggestPath = nimsuggestPath
341313
result.version = version
342314
315+
info "Starting nimsuggest", root = root, timeout = timeout, path = nimsuggestPath,
316+
workingDir = workingDir
317+
343318
if nimsuggestPath != "":
344319
result.protocolVersion = detectNimsuggestVersion(root, nimsuggestPath, workingDir)
345320
if result.protocolVersion > HighestSupportedNimSuggestProtocolVersion:
346321
result.protocolVersion = HighestSupportedNimSuggestProtocolVersion
347-
var
322+
var
348323
args = @[root, "--v" & $result.protocolVersion, "--autobind"]
349324
if result.protocolVersion >= 4:
350325
args.add("--clientProcessId:" & $getCurrentProcessId())
@@ -357,18 +332,13 @@ proc createNimsuggest*(root: string,
357332
args.add("--exceptionInlayHints:on")
358333
else:
359334
args.add("--exceptionInlayHints:off")
360-
result.process = startProcess(command = nimsuggestPath,
361-
workingDir = workingDir,
362-
args = args,
363-
options = {poUsePath})
364-
#TODO better use startProcess from chronos so we dont need to spawn a thread
365-
# all this is needed to avoid the need to block on the main thread.
366-
createThread(thread, readPort, (ns: cast[ptr NimSuggestImpl](result), process: result.process))
367-
368-
# copy stderr of log
369-
createThread(stderrThread, logStderr, (root: root, process: result.process))
370-
await waitUntilPortIsRead(result)
371-
335+
result.process =
336+
await startProcess(nimsuggestPath, arguments = args, options = { UsePath },
337+
stdoutHandle = AsyncProcess.Pipe,
338+
stderrHandle = AsyncProcess.Pipe)
339+
340+
asyncSpawn logNsError(result)
341+
result.port = (await result.process.stdoutStream.readLine(sep="\n")).parseInt
372342
else:
373343
error "Unable to start nimsuggest. Unable to find binary on the $PATH", nimsuggestPath = nimsuggestPath
374344
result.markFailed fmt "Unable to start nimsuggest. `{nimsuggestPath}` is not present on the PATH"

tests/textensions.nim

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import ../[
22
nimlangserver, ls, lstransports, utils
33
]
44
import ../protocol/[enums, types]
5-
import std/[options, unittest, json, os, jsonutils, sequtils, strutils, sugar, strformat, osproc]
5+
import std/[options, unittest, json, os, jsonutils, sequtils, strutils, sugar, strformat]
66
import json_rpc/[rpcclient]
77
import chronicles
88
import lspsocketclient
9+
import chronos/asyncproc
910

1011

1112
suite "Nimlangserver":
@@ -50,10 +51,10 @@ suite "Nimlangserver":
5051
client.notify("textDocument/didOpen",
5152
%createDidOpenParams("projects/hw/useRoot.nim"))
5253

53-
let prevSuggestPid = ls.projectFiles[hwAbsFile].waitFor.process.processID
54+
let prevSuggestPid = ls.projectFiles[hwAbsFile].waitFor.process.pid
5455
let suggestParams = SuggestParams(action: saRestart, projectFile: hwAbsFile)
5556
let suggestRes = client.call("extension/suggest", %suggestParams).waitFor
56-
let suggestPid = ls.projectFiles[hwAbsFile].waitFor.process.processID
57+
let suggestPid = ls.projectFiles[hwAbsFile].waitFor.process.pid
5758

5859
check prevSuggestPid != suggestPid
5960

utils.nim

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import std/[unicode, uri, strformat, os, strutils, options, json, jsonutils, sugar]
1+
import std/[unicode, uri, strformat, os, strutils, options, json, jsonutils, sugar, net]
22
import chronos, chronicles
33
import "$nim/compiler/pathutils"
44
import json_rpc/private/jrpc_sys
@@ -277,4 +277,12 @@ proc withTimeout*[T](fut: Future[T], timeout: int = 500): Future[Option[T]] {.as
277277
#Returns None when the timeout is reached and cancels the fut. Otherwise returns the Fut
278278
let timeoutFut = sleepAsync(timeout).map(() => none(T))
279279
let optFut = fut.map((r: T) => some r)
280-
await either(optFut, timeoutFut)
280+
await either(optFut, timeoutFut)
281+
282+
proc getNextFreePort*(): Port=
283+
let s = newSocket()
284+
s.bindAddr(Port(0), "localhost")
285+
let (_, port) = s.getLocalAddr
286+
s.close()
287+
port
288+

0 commit comments

Comments
 (0)