From 7428080e2d398845e3c4f5a9690ef56be9920470 Mon Sep 17 00:00:00 2001 From: nitely Date: Wed, 31 Jul 2024 15:02:00 -0300 Subject: [PATCH 1/5] Stream cancel fixes --- .gitignore | 1 + src/hyperx/clientserver.nim | 2 + src/hyperx/stream.nim | 4 +- tests/functional/tcancel.nim | 112 ++++++++++++++++++++++++++++++ tests/functional/tflowcontrol.nim | 1 - 5 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 tests/functional/tcancel.nim diff --git a/.gitignore b/.gitignore index 05af81d..8c6d43e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ tests/functional/tserver tests/functional/tconcurrent tests/functional/tconcurrentdata tests/functional/tflowcontrol +tests/functional/tcancel src/hyperx/client src/hyperx/server src/hyperx/clientserver diff --git a/src/hyperx/clientserver.nim b/src/hyperx/clientserver.nim index f12b52b..7bae7ce 100644 --- a/src/hyperx/clientserver.nim +++ b/src/hyperx/clientserver.nim @@ -1225,6 +1225,8 @@ proc cancel*(strm: ClientStream, code: ErrorCode) {.async.} = await failSilently strm.writeRst(code) await failSilently strm.ping() finally: + if strm.stream.error == nil: + strm.stream.error = newStrmError(errStreamClosed) strm.close() when defined(hyperxTest): diff --git a/src/hyperx/stream.nim b/src/hyperx/stream.nim index 923821d..891cfb2 100644 --- a/src/hyperx/stream.nim +++ b/src/hyperx/stream.nim @@ -160,7 +160,7 @@ func toNextStateSend*(s: StreamState, e: StreamEvent): StreamState {.raises: []. case e of seHeadersEndStream, seDataEndStream: strmHalfClosedLocal - of seRstStream: strmClosed + of seRstStream: strmHalfClosedLocal # strmClosed else: strmOpen of strmClosed: case e @@ -180,7 +180,7 @@ func toNextStateSend*(s: StreamState, e: StreamEvent): StreamState {.raises: []. else: strmHalfClosedRemote of strmHalfClosedLocal, strmReservedRemote: case e - of seRstStream: strmClosed + of seRstStream: s # strmClosed of seWindowUpdate, sePriority: s else: strmInvalid of strmInvalid: diff --git a/tests/functional/tcancel.nim b/tests/functional/tcancel.nim new file mode 100644 index 0000000..e4cd71a --- /dev/null +++ b/tests/functional/tcancel.nim @@ -0,0 +1,112 @@ +{.define: ssl.} +{.define: hyperxSanityCheck.} + +import std/asyncdispatch +import ../../src/hyperx/client +import ../../src/hyperx/signal +import ../../src/hyperx/errors +import ./tutils.nim +from ../../src/hyperx/clientserver import stgWindowSize + +const strmsPerClient = 1123 +const clientsCount = 25 +const strmsInFlight = 100 +const dataPayloadLen = stgWindowSize.int * 2 + 123 + +proc send(strm: ClientStream) {.async.} = + await strm.sendHeaders( + newSeqRef(@[ + (":method", "POST"), + (":scheme", "https"), + (":path", "/file/"), + (":authority", "foo.bar"), + ("user-agent", "HyperX/0.1"), + ("content-type", "text/plain") + ]), + finish = false + ) + var sentBytes = 0 + var data = newStringRef newString(16 * 1024 + 123) + while sentBytes < dataPayloadLen: + await strm.sendBody(data, finish = false) + sentBytes += data[].len + await strm.sendBody(data, finish = true) + +proc recv(strm: ClientStream) {.async.} = + var data = newStringref() + await strm.recvHeaders(data) + doAssert data[] == ":status: 200\r\n" + data[].setLen 0 + while not strm.recvEnded: + await strm.recvBody(data) + await strm.cancel(errCancel) # CANCEL + +proc spawnStream( + client: ClientContext, + checked: ref int +) {.async.} = + let strm = client.newClientStream() + with strm: + let recvFut = strm.recv() + let sendFut = strm.send() + try: + await recvFut + except StrmError as err: + doAssert err.typ == hxLocalErr + doAssert err.code == errStreamClosed + try: + await sendFut + except StrmError as err: + doAssert err.typ == hxLocalErr + doAssert err.code == errStreamClosed + inc checked[] + return + +proc spawnStream( + client: ClientContext, + checked: ref int, + sig: SignalAsync, + inFlight: ref int +) {.async.} = + try: + await spawnStream(client, checked) + finally: + inFlight[] -= 1 + sig.trigger() + +proc spawnClient( + checked: ref int +) {.async.} = + var client = newClient(localHost, localPort) + with client: + var stmsCount = 0 + var inFlight = new(int) + inFlight[] = 0 + var sig = newSignal() + while stmsCount < strmsPerClient: + inFlight[] = inFlight[] + 1 + asyncCheck spawnStream(client, checked, sig, inFlight) + inc stmsCount + if stmsCount >= strmsPerClient: + break + if inFlight[] == strmsInFlight: + await sig.waitFor() + while inFlight[] > 0: + await sig.waitFor() + +proc main() {.async.} = + let checked = new(int) + checked[] = 0 + var clients = newSeq[Future[void]]() + for _ in 0 .. clientsCount-1: + clients.add spawnClient(checked) + for clientFut in clients: + await clientFut + doAssert checked[] == clientsCount * strmsPerClient + echo "checked ", $checked[] + +(proc = + waitFor main() + doAssert not hasPendingOperations() + echo "ok" +)() diff --git a/tests/functional/tflowcontrol.nim b/tests/functional/tflowcontrol.nim index 455a19b..92afc21 100644 --- a/tests/functional/tflowcontrol.nim +++ b/tests/functional/tflowcontrol.nim @@ -43,7 +43,6 @@ func newReqsCtx(): ReqsCtx = ) proc send(strm: ClientStream, req: Req) {.async.} = - # XXX send multiple data frames await strm.sendHeaders(req.headers.s, finish = false) await strm.sendBody(req.data.s, finish = true) From cd491c8af0ce8be0614d53c4b8c4cfb183ac832d Mon Sep 17 00:00:00 2001 From: nitely Date: Wed, 31 Jul 2024 19:31:42 -0300 Subject: [PATCH 2/5] progress --- .gitignore | 1 + src/hyperx/clientserver.nim | 19 ++++--- src/hyperx/signal.nim | 7 +-- src/hyperx/stream.nim | 9 ++- src/hyperx/value.nim | 83 ++++++++++++++++++++++++++++ tests/functional/tcancel.nim | 12 ++-- tests/functional/tconcurrentdata.nim | 2 + tests/functional/tflowcontrol.nim | 2 + 8 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 src/hyperx/value.nim diff --git a/.gitignore b/.gitignore index 8c6d43e..ee5c11f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ src/hyperx/clientserver src/hyperx/queue src/hyperx/lock src/hyperx/signal +src/hyperx/value src/hyperx/untestable src/hyperx/frame src/hyperx/stream diff --git a/src/hyperx/clientserver.nim b/src/hyperx/clientserver.nim index 7bae7ce..bf363c0 100644 --- a/src/hyperx/clientserver.nim +++ b/src/hyperx/clientserver.nim @@ -13,6 +13,7 @@ import pkg/hpack import ./frame import ./stream import ./queue +import ./value import ./signal import ./errors import ./utils @@ -668,6 +669,8 @@ proc recvDispatcherNaked(client: ClientContext) {.async.} = # Process headers even if the stream # does not exist if frm.sid.StreamId notin client.streams: + if frm.typ == frmtData: + client.windowPending -= frm.payloadLen.int check frm.typ in {frmtRstStream, frmtWindowUpdate}, newConnError errStreamClosed debugInfo "stream not found " & $frm.sid.int @@ -892,14 +895,16 @@ proc write(strm: ClientStream, frm: Frame): Future[void] = proc read(stream: Stream): Future[Frame] {.async.} = var frm: Frame while true: - frm = await stream.msgs.pop() + #frm = await stream.msgs.pop() + frm = await stream.msgs.get() + stream.msgs.getDone() doAssert stream.id == frm.sid.StreamId doAssert frm.typ in frmStreamAllowed # this can raise stream/conn error stream.doTransitionRecv frm if frm.typ == frmtRstStream: - for frm2 in stream.msgs: - stream.doTransitionRecv frm2 + #for frm2 in stream.msgs: + # stream.doTransitionRecv frm2 stream.error = newStrmError(frm.errorCode, hxRemoteErr) stream.close() raise newStrmError(frm.errorCode, hxRemoteErr) @@ -1058,10 +1063,10 @@ proc recvBodyNaked(strm: ClientStream, data: ref string) {.async.} = let bodyL = strm.bodyRecv.len data[].add strm.bodyRecv strm.bodyRecv.setLen 0 - if not client.isConnected: - # this avoids raising when sending a window update - # if the conn is closed. Unsure if it's useful - return + #if not client.isConnected: + # # this avoids raising when sending a window update + # # if the conn is closed. Unsure if it's useful + # return client.windowProcessed += bodyL stream.windowProcessed += bodyL doAssert stream.windowPending >= stream.windowProcessed diff --git a/src/hyperx/signal.nim b/src/hyperx/signal.nim index a8ef02a..6192067 100644 --- a/src/hyperx/signal.nim +++ b/src/hyperx/signal.nim @@ -24,12 +24,11 @@ proc newSignal*(): SignalAsync {.raises: [].} = isClosed: false ) -proc waitFor*(sig: SignalAsync) {.async.} = +proc waitFor*(sig: SignalAsync): Future[void] {.raises: [SignalClosedError].} = if sig.isClosed: raise newSignalClosedError() - let fut = newFuture[void]() - sig.waiters.addFirst fut - await fut + result = newFuture[void]() + sig.waiters.addFirst result proc wakeupSoon(f: Future[void]) = proc wakeup = diff --git a/src/hyperx/stream.nim b/src/hyperx/stream.nim index 891cfb2..d98133b 100644 --- a/src/hyperx/stream.nim +++ b/src/hyperx/stream.nim @@ -2,6 +2,7 @@ import std/tables import ./frame import ./queue +import ./value import ./signal import ./errors @@ -198,7 +199,8 @@ type Stream* = ref object id*: StreamId state*: StreamState - msgs*: QueueAsync[Frame] + #msgs*: QueueAsync[Frame] + msgs*: ValueAsync[Frame] peerWindow*: int32 peerWindowUpdateSig*: SignalAsync windowPending*: int @@ -211,7 +213,8 @@ proc newStream(id: StreamId, peerWindow: int32): Stream {.raises: [].} = Stream( id: id, state: strmIdle, - msgs: newQueue[Frame](1), + #msgs: newQueue[Frame](1), + msgs: newValueAsync[Frame](), peerWindow: peerWindow, peerWindowUpdateSig: newSignal(), windowPending: 0, @@ -259,7 +262,7 @@ func open*( ): Stream {.raises: [StreamsClosedError].} = doAssert sid notin s.t, $sid.int if s.isClosed: - raise newException(StreamsClosedError, "Streams is closed") + raise newException(StreamsClosedError, "Cannot open stream") result = newStream(sid, peerWindow) s.t[sid] = result diff --git a/src/hyperx/value.nim b/src/hyperx/value.nim new file mode 100644 index 0000000..bc041a5 --- /dev/null +++ b/src/hyperx/value.nim @@ -0,0 +1,83 @@ +import std/asyncdispatch + +import ./signal +import ./errors + +type + ValueAsyncClosedError* = QueueClosedError + +func newValueAsyncClosedError(): ref ValueAsyncClosedError {.raises: [].} = + result = (ref ValueAsyncClosedError)(msg: "ValueAsync is closed") + +type + ValueAsync*[T] = ref object + sigPut, sigGet: SignalAsync + val: T + isClosed: bool + +func newValueAsync*[T](): ValueAsync[T] {.raises: [].} = + ValueAsync[T]( + sigPut: newSignal(), + sigGet: newSignal(), + val: nil, + isClosed: false + ) + +proc put*[T](vala: ValueAsync[T], val: T) {.async.} = + if vala.isClosed: + raise newValueAsyncClosedError() + try: + while vala.val != nil: + await vala.sigPut.waitFor() + vala.val = val + vala.sigGet.trigger() + while vala.val != nil: + await vala.sigPut.waitFor() + except SignalClosedError: + raise newValueAsyncClosedError() + +proc get*[T](vala: ValueAsync[T]): Future[T] {.async.} = + if vala.isClosed: + raise newValueAsyncClosedError() + try: + while vala.val == nil: + await vala.sigGet.waitFor() + except SignalClosedError: + raise newValueAsyncClosedError() + result = vala.val + +proc getDone*[T](vala: ValueAsync[T]) = + vala.val = nil + try: + vala.sigPut.trigger() + except SignalClosedError: + raise newValueAsyncClosedError() + +proc close*[T](vala: ValueAsync[T]) {.raises: [].} = + if vala.isClosed: + return + vala.isClosed = true + vala.sigPut.close() + vala.sigGet.close() + +when isMainModule: + func newIntRef(n: int): ref int = + new result + result[] = n + block: + proc test() {.async.} = + var q = newValueAsync[ref int]() + proc puts {.async.} = + await q.put newIntRef(1) + await q.put newIntRef(2) + await q.put newIntRef(3) + await q.put newIntRef(4) + let puts1 = puts() + doAssert (await q.get())[] == 1 + doAssert (await q.get())[] == 2 + doAssert (await q.get())[] == 3 + doAssert (await q.get())[] == 4 + await puts1 + waitFor test() + doAssert not hasPendingOperations() + echo "ok" diff --git a/tests/functional/tcancel.nim b/tests/functional/tcancel.nim index e4cd71a..dafd217 100644 --- a/tests/functional/tcancel.nim +++ b/tests/functional/tcancel.nim @@ -11,7 +11,8 @@ from ../../src/hyperx/clientserver import stgWindowSize const strmsPerClient = 1123 const clientsCount = 25 const strmsInFlight = 100 -const dataPayloadLen = stgWindowSize.int * 2 + 123 +const dataFrameLen = 1 +#const dataFrameLen = stgWindowSize.int * 2 + 123 proc send(strm: ClientStream) {.async.} = await strm.sendHeaders( @@ -25,12 +26,9 @@ proc send(strm: ClientStream) {.async.} = ]), finish = false ) - var sentBytes = 0 - var data = newStringRef newString(16 * 1024 + 123) - while sentBytes < dataPayloadLen: + var data = newStringRef newString(dataFrameLen) + while true: await strm.sendBody(data, finish = false) - sentBytes += data[].len - await strm.sendBody(data, finish = true) proc recv(strm: ClientStream) {.async.} = var data = newStringref() @@ -84,6 +82,8 @@ proc spawnClient( inFlight[] = 0 var sig = newSignal() while stmsCount < strmsPerClient: + if not client.isConnected: + return inFlight[] = inFlight[] + 1 asyncCheck spawnStream(client, checked, sig, inFlight) inc stmsCount diff --git a/tests/functional/tconcurrentdata.nim b/tests/functional/tconcurrentdata.nim index a1b1b76..afdbaba 100644 --- a/tests/functional/tconcurrentdata.nim +++ b/tests/functional/tconcurrentdata.nim @@ -98,6 +98,8 @@ proc spawnClient( var sig = newSignal() while stmsCount < strmsPerClient: for req in reqsCtx.s: + if not client.isConnected: + return inFlight[] = inFlight[] + 1 asyncCheck spawnStream(client, req, checked, sig, inFlight) inc stmsCount diff --git a/tests/functional/tflowcontrol.nim b/tests/functional/tflowcontrol.nim index 92afc21..289f776 100644 --- a/tests/functional/tflowcontrol.nim +++ b/tests/functional/tflowcontrol.nim @@ -98,6 +98,8 @@ proc spawnClient( var sig = newSignal() while stmsCount < strmsPerClient: for req in reqsCtx.s: + if not client.isConnected: + return inFlight[] = inFlight[] + 1 asyncCheck spawnStream(client, req, checked, sig, inFlight) inc stmsCount From 856565fc72e3cfd43d01ce5d8c9101943296813a Mon Sep 17 00:00:00 2001 From: nitely Date: Thu, 1 Aug 2024 07:27:01 -0300 Subject: [PATCH 3/5] progress --- hyperx.nimble | 1 + src/hyperx/clientserver.nim | 2 +- src/hyperx/stream.nim | 41 ++++++++++++++++++++++++++++-------- src/hyperx/value.nim | 16 ++++++++------ tests/functional/tcancel.nim | 6 +++--- 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/hyperx.nimble b/hyperx.nimble index 9cf4c33..9dacfd2 100644 --- a/hyperx.nimble +++ b/hyperx.nimble @@ -14,6 +14,7 @@ task test, "Test": exec "nim c -r src/hyperx/utils.nim" exec "nim c -r src/hyperx/queue.nim" exec "nim c -r src/hyperx/signal.nim" + exec "nim c -r src/hyperx/value.nim" exec "nim c -r src/hyperx/stream.nim" exec "nim c -r src/hyperx/frame.nim" exec "nim c -r -f -d:hyperxTest -d:ssl src/hyperx/testutils.nim" diff --git a/src/hyperx/clientserver.nim b/src/hyperx/clientserver.nim index bf363c0..bf4b216 100644 --- a/src/hyperx/clientserver.nim +++ b/src/hyperx/clientserver.nim @@ -897,7 +897,7 @@ proc read(stream: Stream): Future[Frame] {.async.} = while true: #frm = await stream.msgs.pop() frm = await stream.msgs.get() - stream.msgs.getDone() + #stream.msgs.getDone() doAssert stream.id == frm.sid.StreamId doAssert frm.typ in frmStreamAllowed # this can raise stream/conn error diff --git a/src/hyperx/stream.nim b/src/hyperx/stream.nim index d98133b..8a7171a 100644 --- a/src/hyperx/stream.nim +++ b/src/hyperx/stream.nim @@ -1,7 +1,7 @@ import std/tables import ./frame -import ./queue +#import ./queue import ./value import ./signal import ./errors @@ -16,6 +16,7 @@ type strmReservedRemote strmHalfClosedLocal strmHalfClosedRemote + strmClosedRst strmInvalid StreamEvent* = enum seHeaders @@ -131,12 +132,12 @@ func toNextStateRecv*(s: StreamState, e: StreamEvent): StreamState {.raises: []. of seHeadersEndStream, seRstStream: strmClosed of sePriority: strmReservedRemote else: strmInvalid - of strmHalfClosedLocal: + of strmHalfClosedLocal, strmClosedRst: case e of seHeadersEndStream, seDataEndStream, seRstStream: strmClosed - else: strmHalfClosedLocal + else: s of strmHalfClosedRemote, strmReservedLocal: case e of seRstStream: strmClosed @@ -161,11 +162,11 @@ func toNextStateSend*(s: StreamState, e: StreamEvent): StreamState {.raises: []. case e of seHeadersEndStream, seDataEndStream: strmHalfClosedLocal - of seRstStream: strmHalfClosedLocal # strmClosed + of seRstStream: strmClosedRst else: strmOpen - of strmClosed: + of strmClosed, strmClosedRst: case e - of sePriority: strmClosed + of sePriority: s else: strmInvalid of strmReservedLocal: case e @@ -179,9 +180,14 @@ func toNextStateSend*(s: StreamState, e: StreamEvent): StreamState {.raises: []. seDataEndStream, seRstStream: strmClosed else: strmHalfClosedRemote - of strmHalfClosedLocal, strmReservedRemote: + of strmHalfClosedLocal: case e - of seRstStream: s # strmClosed + of seRstStream: strmClosedRst + of seWindowUpdate, sePriority: s + else: strmInvalid + of strmReservedRemote: + case e + of seRstStream: strmClosed of seWindowUpdate, sePriority: s else: strmInvalid of strmInvalid: @@ -320,7 +326,8 @@ when isMainModule: strmReservedLocal, strmReservedRemote, strmHalfClosedLocal, - strmHalfClosedRemote + strmHalfClosedRemote, + strmClosedRst #strmInvalid } block: @@ -422,5 +429,21 @@ when isMainModule: let isValid = toNextStateRecv(state, seData) != strmInvalid let isValid2 = toNextStateRecv(state, seDataEndStream) != strmInvalid doAssert isValid == isValid2, $state + block: + for ev in allEvents-{seUnknown,sePriority}: + doAssert toNextStateSend(strmClosedRst, ev) == strmInvalid + doAssert toNextStateSend(strmClosedRst, sePriority) == strmClosedRst + block: + for state in {strmOpen,strmHalfClosedLocal}: + doAssert toNextStateSend(state, seRstStream) == strmClosedRst + for state in allStates-{strmOpen,strmHalfClosedLocal}: + doAssert toNextStateSend(state, seRstStream) in {strmInvalid, strmClosed} + block: + for ev in allEvents-{seUnknown,seRstStream,seHeadersEndStream,seDataEndStream}: + doAssert toNextStateRecv(strmClosedRst, ev) == strmClosedRst + doAssert toNextStateRecv(strmHalfClosedLocal, ev) == strmHalfClosedLocal + for ev in {seRstStream,seHeadersEndStream,seDataEndStream}: + doAssert toNextStateRecv(strmClosedRst, ev) == strmClosed + doAssert toNextStateRecv(strmHalfClosedLocal, ev) == strmClosed echo "ok" \ No newline at end of file diff --git a/src/hyperx/value.nim b/src/hyperx/value.nim index bc041a5..a179a16 100644 --- a/src/hyperx/value.nim +++ b/src/hyperx/value.nim @@ -42,17 +42,19 @@ proc get*[T](vala: ValueAsync[T]): Future[T] {.async.} = try: while vala.val == nil: await vala.sigGet.waitFor() - except SignalClosedError: - raise newValueAsyncClosedError() - result = vala.val - -proc getDone*[T](vala: ValueAsync[T]) = - vala.val = nil - try: + result = vala.val + vala.val = nil vala.sigPut.trigger() except SignalClosedError: raise newValueAsyncClosedError() +#proc getDone*[T](vala: ValueAsync[T]) = +# vala.val = nil +# try: +# vala.sigPut.trigger() +# except SignalClosedError: +# raise newValueAsyncClosedError() + proc close*[T](vala: ValueAsync[T]) {.raises: [].} = if vala.isClosed: return diff --git a/tests/functional/tcancel.nim b/tests/functional/tcancel.nim index dafd217..a9baaa0 100644 --- a/tests/functional/tcancel.nim +++ b/tests/functional/tcancel.nim @@ -9,7 +9,7 @@ import ./tutils.nim from ../../src/hyperx/clientserver import stgWindowSize const strmsPerClient = 1123 -const clientsCount = 25 +const clientsCount = 13 const strmsInFlight = 100 const dataFrameLen = 1 #const dataFrameLen = stgWindowSize.int * 2 + 123 @@ -26,7 +26,7 @@ proc send(strm: ClientStream) {.async.} = ]), finish = false ) - var data = newStringRef newString(dataFrameLen) + let data = newStringRef newString(dataFrameLen) while true: await strm.sendBody(data, finish = false) @@ -45,8 +45,8 @@ proc spawnStream( ) {.async.} = let strm = client.newClientStream() with strm: - let recvFut = strm.recv() let sendFut = strm.send() + let recvFut = strm.recv() try: await recvFut except StrmError as err: From b39c2fd6693554b94bd93d0225b5f3f6f2f7fc21 Mon Sep 17 00:00:00 2001 From: nitely Date: Thu, 1 Aug 2024 07:28:13 -0300 Subject: [PATCH 4/5] nitely --- hyperx.nimble | 1 + 1 file changed, 1 insertion(+) diff --git a/hyperx.nimble b/hyperx.nimble index 9dacfd2..d22bf8c 100644 --- a/hyperx.nimble +++ b/hyperx.nimble @@ -60,6 +60,7 @@ task functest, "Func test": exec "nim c -r -d:release tests/functional/tconcurrent.nim" exec "nim c -r -d:release tests/functional/tconcurrentdata.nim" exec "nim c -r -d:release tests/functional/tflowcontrol.nim" + exec "nim c -r -d:release tests/functional/tcancel.nim" task h2spec, "h2spec test": exec "./h2spec --tls --port 8783 --strict" From f61d06e0e9192e17610f10343b8b4793352bd86d Mon Sep 17 00:00:00 2001 From: nitely Date: Thu, 1 Aug 2024 08:15:41 -0300 Subject: [PATCH 5/5] progress --- tests/functional/tserver.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/tserver.nim b/tests/functional/tserver.nim index a8d69d8..0899a51 100644 --- a/tests/functional/tserver.nim +++ b/tests/functional/tserver.nim @@ -50,6 +50,7 @@ proc processClientHandler(client: ClientContext) {.async.} = debugEcho err.msg when defined(hyperxStats): echoStats client + #GC_fullCollect() proc serve(server: ServerContext) {.async.} = with server: