Skip to content

Commit c6ee8f6

Browse files
committed
feat: perf tests and fix: pin connections and streams
1 parent 875334a commit c6ee8f6

8 files changed

Lines changed: 190 additions & 30 deletions

File tree

lsquic.nimble

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ task format, "Format nim code using nph":
2121

2222
task test, "Run tests":
2323
when defined(windows):
24-
exec "nim c --mm:refc -d:nimDebugDlOpen -r --threads:on tests/test_connection.nim"
24+
exec "nim c --mm:refc -d:nimDebugDlOpen --threads:on tests/test_connection.nim"
2525
else:
26-
exec "nim c --mm:refc -r --threads:on tests/test_connection.nim"
26+
exec "nim c --mm:refc --threads:on tests/test_connection.nim"
27+
exec "./tests/test_connection --output-level=VERBOSE"

lsquic/connection.nim

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ proc newOutgoingConnection*(
5353
)
5454
conn.ensureClosedFut = conn.ensureClosed()
5555
conn.quicConn.onClose = proc() {.raises: [].} =
56+
echo "OUTGOING CONN FIRE ONCLOSE"
5657
conn.closed.fire()
5758
conn
5859

@@ -69,6 +70,7 @@ proc dial*(
6970
retFut.fail(newException(DialError, "could not dial: " & error))
7071
nil
7172
connection.quicConn.onClose = proc() {.raises: [].} =
73+
echo "OUTGOING CONN FIRE DIAL"
7274
connection.closed.fire()
7375
retFut
7476

@@ -91,6 +93,9 @@ method incomingStream*(
9193
method incomingStream*(
9294
connection: IncomingConnection
9395
): Future[Stream] {.async: (raises: [CancelledError, ConnectionError]).} =
96+
if connection.isClosed:
97+
raise newException(ConnectionError, "connection is closed")
98+
9499
let closedFut = connection.closed.wait()
95100
let incomingFut = connection.quicConn.incomingStream()
96101
let raceFut = await race(closedFut, incomingFut)
@@ -108,6 +113,8 @@ method openStream*(
108113
method openStream*(
109114
connection: OutgoingConnection
110115
): Future[Stream] {.async: (raises: [CancelledError, ConnectionError]).} =
116+
if connection.isClosed:
117+
raise newException(ConnectionError, "connection is closed")
111118
let s = Stream.new()
112119
let created = connection.quicConn.addPendingStream(s)
113120
connection.quicContext.makeStream(connection.quicConn)

lsquic/connectionmanager.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ proc localAddress*(
4646
connman.udp.localAddress()
4747

4848
proc startSending*(connman: ConnectionManager) =
49-
trace "Starting sending loop"
49+
debug "Starting sending loop"
5050

5151
proc send() {.async: (raises: [CancelledError]).} =
5252
try:
5353
let datagram = await connman.outgoing.get()
5454
await connman.udp.sendTo(datagram.taddr, datagram.data)
5555
except TransportError as e:
56-
trace "Failed to send datagram", errorMsg = e.msg
56+
debug "Failed to send datagram", errorMsg = e.msg
5757

5858
connman.loop = asyncLoop(send)
5959

lsquic/context/client.nim

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import ../helpers/sequninit
99
proc onNewConn(
1010
stream_if_ctx: pointer, conn: ptr lsquic_conn_t
1111
): ptr lsquic_conn_ctx_t {.cdecl.} =
12-
trace "New connection established: client"
12+
debug "New connection established: client"
1313
let conn_ctx = lsquic_conn_get_ctx(conn)
1414
cast[ptr lsquic_conn_ctx_t](conn_ctx)
1515

1616
proc onHandshakeDone(
1717
conn: ptr lsquic_conn_t, status: enum_lsquic_hsk_status
1818
) {.cdecl.} =
19-
trace "Handshake done", status
19+
debug "Handshake done", status
2020
let conn_ctx = lsquic_conn_get_ctx(conn)
2121
if conn_ctx.isNil:
2222
debug "conn_ctx is nil in onHandshakeDone"
@@ -34,7 +34,7 @@ proc onHandshakeDone(
3434
quicClientConn.connectedFut.complete()
3535

3636
proc onConnClosed(conn: ptr lsquic_conn_t) {.cdecl.} =
37-
trace "Connection closed: client"
37+
debug "Connection closed: client"
3838
let conn_ctx = lsquic_conn_get_ctx(conn)
3939
if not conn_ctx.isNil:
4040
let quicClientConn = cast[QuicClientConn](conn_ctx)
@@ -52,12 +52,13 @@ proc onConnClosed(conn: ptr lsquic_conn_t) {.cdecl.} =
5252
)
5353
quicClientConn.cancelPending()
5454
quicClientConn.onClose()
55+
GC_unref(quicClientConn)
5556
lsquic_conn_set_ctx(conn, nil)
5657

5758
proc onNewStream(
5859
stream_if_ctx: pointer, stream: ptr lsquic_stream_t
5960
): ptr lsquic_stream_ctx_t {.cdecl.} =
60-
trace "New stream created: client"
61+
debug "New stream created: client"
6162
let conn = lsquic_stream_conn(stream)
6263
let conn_ctx = lsquic_conn_get_ctx(conn)
6364
if conn_ctx.isNil:
@@ -90,7 +91,7 @@ method dial*(
9091

9192
let quicClientConn =
9293
QuicClientConn(connectedFut: connectedFut, local: local, remote: remote)
93-
94+
GC_ref(quicClientConn) # Keep it pinned until on_conn_closed is called
9495
let conn = lsquic_engine_connect(
9596
ctx.engine,
9697
N_LSQVER,
@@ -163,5 +164,5 @@ proc new*(
163164
method makeStream*(
164165
ctx: ClientContext, quicConn: QuicConnection
165166
) {.gcsafe, raises: [].} =
166-
trace "Creating stream: client"
167+
debug "Creating stream: client"
167168
lsquic_conn_make_stream(quicConn.lsquicConn)

lsquic/context/server.nim

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import ../helpers/[sequninit, transportaddr]
99
proc onNewConn(
1010
stream_if_ctx: pointer, conn: ptr lsquic_conn_t
1111
): ptr lsquic_conn_ctx_t {.cdecl.} =
12-
trace "New connection established: server"
12+
debug "New connection established: server"
1313
var local: ptr SockAddr
1414
var remote: ptr SockAddr
1515
discard lsquic_conn_get_sockaddr(conn, addr local, addr remote)
@@ -19,12 +19,13 @@ proc onNewConn(
1919
remote: remote.toTransportAddress(),
2020
lsquicConn: conn,
2121
)
22+
GC_ref(quicServerConn) # Keep it pinned until on_conn_closed is called
2223
let serverCtx = cast[ServerContext](stream_if_ctx)
2324
serverCtx.incoming.putNoWait(quicServerConn)
2425
cast[ptr lsquic_conn_ctx_t](quicServerConn)
2526

2627
proc onConnClosed(conn: ptr lsquic_conn_t) {.cdecl.} =
27-
trace "Connection closed: server"
28+
debug "Connection closed: server"
2829
let conn_ctx = lsquic_conn_get_ctx(conn)
2930
if not conn_ctx.isNil:
3031
let quicConn = cast[QuicConnection](conn_ctx)
@@ -34,7 +35,7 @@ proc onConnClosed(conn: ptr lsquic_conn_t) {.cdecl.} =
3435
proc onNewStream(
3536
stream_if_ctx: pointer, stream: ptr lsquic_stream_t
3637
): ptr lsquic_stream_ctx_t {.cdecl.} =
37-
trace "New stream created: server"
38+
debug "New stream created: server"
3839
let conn = lsquic_stream_conn(stream)
3940
let conn_ctx = lsquic_conn_get_ctx(conn)
4041
if conn_ctx.isNil:

lsquic/context/stream.nim

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@ import ../[lsquic_ffi, stream]
44
import ../helpers/sequninit
55

66
proc onClose*(stream: ptr lsquic_stream_t, ctx: ptr lsquic_stream_ctx_t) {.cdecl.} =
7-
trace "Stream closed"
7+
debug "Stream closed"
88
if ctx.isNil:
99
debug "stream_ctx is nil onClose"
1010
return
1111

12+
echo "CLOSING STREAM!!!!!!", lsquic_stream_id(stream)
1213
let streamCtx = cast[Stream](ctx)
1314
if not streamCtx.closeWrite:
1415
streamCtx.isEof = true
16+
echo "FIRING CLOSE 3"
1517
streamCtx.closed.fire()
16-
streamCtx.abortPendingWrites("stream closed")
18+
streamCtx.abortPendingWrites("stream closed 4")
19+
GC_unref(streamCtx)
1720

1821
type StreamReadContext = object
1922
stream: ptr lsquic_stream_t
@@ -29,6 +32,7 @@ proc readCtxCb(
2932
copyMem(addr s[0], data, len)
3033
streamCtx.incoming.putNoWait(s)
3134
if fin != 0:
35+
echo "FIN"
3236
streamCtx.incoming.putNoWait(@[])
3337
len
3438

@@ -40,12 +44,11 @@ proc onRead*(stream: ptr lsquic_stream_t, ctx: ptr lsquic_stream_ctx_t) {.cdecl.
4044
if nread < 0:
4145
error "could not read from stream", nread, streamId = lsquic_stream_id(stream)
4246
streamCtx.abort()
43-
47+
4448
if lsquic_stream_wantread(stream, 0) == -1:
4549
error "could not set stream wantread", streamId = lsquic_stream_id(stream)
4650
streamCtx.abort()
4751

48-
4952
proc onWrite*(stream: ptr lsquic_stream_t, ctx: ptr lsquic_stream_ctx_t) {.cdecl.} =
5053
trace "onWrite"
5154

@@ -55,6 +58,8 @@ proc onWrite*(stream: ptr lsquic_stream_t, ctx: ptr lsquic_stream_ctx_t) {.cdecl
5558

5659
let streamCtx = cast[Stream](ctx)
5760
if streamCtx.toWrite.len == 0:
61+
if not streamCtx.shouldClose.isNil and not streamCtx.shouldClose.finished:
62+
streamCtx.shouldClose.complete()
5863
if lsquic_stream_wantwrite(stream, 0) == -1:
5964
error "could not set stream wantwrite", streamId = lsquic_stream_id(stream)
6065
streamCtx.abort()
@@ -88,6 +93,9 @@ proc onWrite*(stream: ptr lsquic_stream_t, ctx: ptr lsquic_stream_ctx_t) {.cdecl
8893
return
8994

9095
if streamCtx.toWrite.len == 0:
96+
echo streamCtx.shouldClose.isNil
97+
if not streamCtx.shouldClose.isNil and not streamCtx.shouldClose.finished:
98+
streamCtx.shouldClose.complete()
9199
if lsquic_stream_wantwrite(stream, 0) == -1:
92100
error "could not set stream wantwrite", streamId = lsquic_stream_id(stream)
93101
streamCtx.abort()

lsquic/stream.nim

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@ type Stream* = ref object
1616
closed*: AsyncEvent # This is called when on_close callback is executed
1717
isEof*: bool # Received a FIN from remote
1818
toWrite*: seq[WriteTask]
19+
shouldClose*: Future[void].Raising([CancelledError])
1920

2021
proc new*(T: typedesc[Stream], quicStream: ptr lsquic_stream_t = nil): T =
21-
Stream(
22+
let s = Stream(
2223
quicStream: quicStream,
2324
incoming: newAsyncQueue[seq[byte]](),
2425
closed: newAsyncEvent(),
2526
)
27+
GC_ref(s) # Keep it pinned until stream_if.on_close is executed
28+
s
2629

2730
proc abortPendingWrites*(stream: Stream, reason: string = "") =
2831
for pendingWrite in stream.toWrite.mitems:
@@ -33,27 +36,38 @@ proc abortPendingWrites*(stream: Stream, reason: string = "") =
3336
proc abort*(stream: Stream) =
3437
if stream.closeWrite and stream.isEof:
3538
if not stream.closed.isSet():
39+
echo "FIRING CLOSE 1"
3640
stream.closed.fire()
3741
stream.abortPendingWrites("stream aborted")
3842
return
3943

44+
echo "CLOSING ON ABORT"
4045
let ret = lsquic_stream_close(stream.quicStream)
4146
if ret != 0:
4247
error "could not abort stream", streamId = lsquic_stream_id(stream.quicStream)
4348

4449
stream.closeWrite = true
4550
stream.isEof = true
4651
stream.abortPendingWrites("stream aborted")
52+
echo "FIRING CLOSE 2"
4753
stream.closed.fire()
4854

49-
proc close*(stream: Stream) =
55+
proc close*(stream: Stream) {.async: (raises: [StreamError, CancelledError]).} =
5056
if stream.closeWrite:
5157
return
58+
echo "CALLED CLOSE!!!!!!!!!!!!!!!!!!"
59+
if stream.toWrite.len != 0:
60+
stream.shouldClose = Future[void].Raising([CancelledError]).init()
61+
62+
echo "AWAITING FOR CLOSE"
63+
await stream.shouldClose
5264

5365
# Closing only the write side
66+
echo "SHUTDOWN WRITE"
5467
let ret = lsquic_stream_shutdown(stream.quicStream, 1)
5568
if ret == 0:
5669
if stream.isEof:
70+
echo "CLOSE DUE TO EOF"
5771
if lsquic_stream_close(stream.quicStream) != 0:
5872
stream.abort()
5973
raise newException(StreamError, "could not close the stream")
@@ -68,22 +82,26 @@ proc read*(
6882
return @[]
6983

7084
if lsquic_stream_wantread(stream.quicStream, 1) == -1:
85+
echo "ABORTING"
7186
stream.abort()
7287
raise newException(StreamError, "could not set wantread")
7388

7489
let incomingFut = stream.incoming.get()
7590
let closedFut = stream.closed.wait()
7691
let raceFut = await race(closedFut, incomingFut)
7792
if raceFut == closedFut:
93+
echo "STREAM GOT CLOSED"
7894
await incomingFut.cancelAndWait()
7995
stream.isEof = true
8096
stream.closeWrite = true
81-
raise newException(StreamError, "stream closed")
97+
raise newException(StreamError, "stream closed 1")
8298

8399
let incoming = await incomingFut
84100
if incoming.len == 0:
101+
echo "INCOMING LEN IS 0"
85102
if stream.closeWrite:
86103
# We were already closed for write. Close the stream completely
104+
echo "CLOSING AFTER READ IS EOF, and WRITE WAS CALLED BEFORE"
87105
if lsquic_stream_close(stream.quicStream) != 0:
88106
stream.abort()
89107
raise newException(StreamError, "could not close the stream")
@@ -95,15 +113,16 @@ proc write*(
95113
stream: Stream, data: seq[byte]
96114
) {.async: (raises: [CancelledError, StreamError]).} =
97115
if stream.closeWrite:
98-
raise newException(StreamError, "stream is closed")
116+
raise newException(StreamError, "stream closed 3")
99117

100118
let closedFut = stream.closed.wait()
101119
let doneFut = Future[void].Raising([CancelledError, StreamError]).init()
102120
stream.toWrite.add(WriteTask(data: data, doneFut: doneFut))
103121
discard lsquic_stream_wantwrite(stream.quicStream, 1)
104122
let raceFut = await race(closedFut, doneFut)
105123
if raceFut == closedFut:
106-
doneFut.fail(newException(StreamError, "stream closed"))
124+
if not doneFut.finished:
125+
doneFut.fail(newException(StreamError, "stream closed 2"))
107126
stream.closeWrite = true
108127

109128
await doneFut

0 commit comments

Comments
 (0)