mirror of https://github.com/vacp2p/nim-libp2p.git
`{.async: (raises).}` for `MultistreamSelect` (#1066)
This commit is contained in:
parent
49a92e5641
commit
48a3ac06ff
|
@ -1,5 +1,5 @@
|
||||||
# Nim-LibP2P
|
# Nim-LibP2P
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
@ -48,12 +48,15 @@ template validateSuffix(str: string): untyped =
|
||||||
if str.endsWith("\n"):
|
if str.endsWith("\n"):
|
||||||
str.removeSuffix("\n")
|
str.removeSuffix("\n")
|
||||||
else:
|
else:
|
||||||
raise newException(MultiStreamError, "MultistreamSelect failed, malformed message")
|
raise (ref MultiStreamError)(msg:
|
||||||
|
"MultistreamSelect failed, malformed message")
|
||||||
|
|
||||||
proc select*(_: MultistreamSelect | type MultistreamSelect,
|
proc select*(
|
||||||
|
_: MultistreamSelect | type MultistreamSelect,
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
proto: seq[string]):
|
proto: seq[string]
|
||||||
Future[string] {.async.} =
|
): Future[string] {.async: (raises: [
|
||||||
|
CancelledError, LPStreamError, MultiStreamError]).} =
|
||||||
trace "initiating handshake", conn, codec = Codec
|
trace "initiating handshake", conn, codec = Codec
|
||||||
## select a remote protocol
|
## select a remote protocol
|
||||||
await conn.writeLp(Codec & "\n") # write handshake
|
await conn.writeLp(Codec & "\n") # write handshake
|
||||||
|
@ -66,7 +69,7 @@ proc select*(_: MultistreamSelect | type MultistreamSelect,
|
||||||
|
|
||||||
if s != Codec:
|
if s != Codec:
|
||||||
notice "handshake failed", conn, codec = s
|
notice "handshake failed", conn, codec = s
|
||||||
raise newException(MultiStreamError, "MultistreamSelect handshake failed")
|
raise (ref MultiStreamError)(msg: "MultistreamSelect handshake failed")
|
||||||
else:
|
else:
|
||||||
trace "multistream handshake success", conn
|
trace "multistream handshake success", conn
|
||||||
|
|
||||||
|
@ -98,19 +101,29 @@ proc select*(_: MultistreamSelect | type MultistreamSelect,
|
||||||
# No alternatives, fail
|
# No alternatives, fail
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
proc select*(_: MultistreamSelect | type MultistreamSelect,
|
proc select*(
|
||||||
|
_: MultistreamSelect | type MultistreamSelect,
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
proto: string): Future[bool] {.async.} =
|
proto: string
|
||||||
|
): Future[bool] {.async: (raises: [
|
||||||
|
CancelledError, LPStreamError, MultiStreamError]).} =
|
||||||
if proto.len > 0:
|
if proto.len > 0:
|
||||||
return (await MultistreamSelect.select(conn, @[proto])) == proto
|
(await MultistreamSelect.select(conn, @[proto])) == proto
|
||||||
else:
|
else:
|
||||||
return (await MultistreamSelect.select(conn, @[])) == Codec
|
(await MultistreamSelect.select(conn, @[])) == Codec
|
||||||
|
|
||||||
proc select*(m: MultistreamSelect, conn: Connection): Future[bool] =
|
proc select*(
|
||||||
|
m: MultistreamSelect,
|
||||||
|
conn: Connection
|
||||||
|
): Future[bool] {.async: (raises: [
|
||||||
|
CancelledError, LPStreamError, MultiStreamError], raw: true).} =
|
||||||
m.select(conn, "")
|
m.select(conn, "")
|
||||||
|
|
||||||
proc list*(m: MultistreamSelect,
|
proc list*(
|
||||||
conn: Connection): Future[seq[string]] {.async.} =
|
m: MultistreamSelect,
|
||||||
|
conn: Connection
|
||||||
|
): Future[seq[string]] {.async: (raises: [
|
||||||
|
CancelledError, LPStreamError, MultiStreamError]).} =
|
||||||
## list remote protos requests on connection
|
## list remote protos requests on connection
|
||||||
if not await m.select(conn):
|
if not await m.select(conn):
|
||||||
return
|
return
|
||||||
|
@ -130,8 +143,9 @@ proc handle*(
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
protos: seq[string],
|
protos: seq[string],
|
||||||
matchers = newSeq[Matcher](),
|
matchers = newSeq[Matcher](),
|
||||||
active: bool = false,
|
active: bool = false
|
||||||
): Future[string] {.async.} =
|
): Future[string] {.async: (raises: [
|
||||||
|
CancelledError, LPStreamError, MultiStreamError]).} =
|
||||||
trace "Starting multistream negotiation", conn, handshaked = active
|
trace "Starting multistream negotiation", conn, handshaked = active
|
||||||
var handshaked = active
|
var handshaked = active
|
||||||
while not conn.atEof:
|
while not conn.atEof:
|
||||||
|
@ -140,7 +154,7 @@ proc handle*(
|
||||||
|
|
||||||
if not handshaked and ms != Codec:
|
if not handshaked and ms != Codec:
|
||||||
debug "expected handshake message", conn, instead=ms
|
debug "expected handshake message", conn, instead=ms
|
||||||
raise newException(CatchableError,
|
raise (ref MultiStreamError)(msg:
|
||||||
"MultistreamSelect handling failed, invalid first message")
|
"MultistreamSelect handling failed, invalid first message")
|
||||||
|
|
||||||
trace "handle: got request", conn, ms
|
trace "handle: got request", conn, ms
|
||||||
|
@ -172,13 +186,16 @@ proc handle*(
|
||||||
trace "no handlers", conn, protocol = ms
|
trace "no handlers", conn, protocol = ms
|
||||||
await conn.writeLp(Na)
|
await conn.writeLp(Na)
|
||||||
|
|
||||||
proc handle*(m: MultistreamSelect, conn: Connection, active: bool = false) {.async.} =
|
proc handle*(
|
||||||
|
m: MultistreamSelect,
|
||||||
|
conn: Connection,
|
||||||
|
active: bool = false) {.async: (raises: [CancelledError]).} =
|
||||||
trace "Starting multistream handler", conn, handshaked = active
|
trace "Starting multistream handler", conn, handshaked = active
|
||||||
var
|
var
|
||||||
protos: seq[string]
|
protos: seq[string]
|
||||||
matchers: seq[Matcher]
|
matchers: seq[Matcher]
|
||||||
for h in m.handlers:
|
for h in m.handlers:
|
||||||
if not isNil(h.match):
|
if h.match != nil:
|
||||||
matchers.add(h.match)
|
matchers.add(h.match)
|
||||||
for proto in h.protos:
|
for proto in h.protos:
|
||||||
protos.add(proto)
|
protos.add(proto)
|
||||||
|
@ -186,12 +203,13 @@ proc handle*(m: MultistreamSelect, conn: Connection, active: bool = false) {.asy
|
||||||
try:
|
try:
|
||||||
let ms = await MultistreamSelect.handle(conn, protos, matchers, active)
|
let ms = await MultistreamSelect.handle(conn, protos, matchers, active)
|
||||||
for h in m.handlers:
|
for h in m.handlers:
|
||||||
if (not isNil(h.match) and h.match(ms)) or h.protos.contains(ms):
|
if (h.match != nil and h.match(ms)) or h.protos.contains(ms):
|
||||||
trace "found handler", conn, protocol = ms
|
trace "found handler", conn, protocol = ms
|
||||||
|
|
||||||
var protocolHolder = h
|
var protocolHolder = h
|
||||||
let maxIncomingStreams = protocolHolder.protocol.maxIncomingStreams
|
let maxIncomingStreams = protocolHolder.protocol.maxIncomingStreams
|
||||||
if protocolHolder.openedStreams.getOrDefault(conn.peerId) >= maxIncomingStreams:
|
if protocolHolder.openedStreams.getOrDefault(conn.peerId) >=
|
||||||
|
maxIncomingStreams:
|
||||||
debug "Max streams for protocol reached, blocking new stream",
|
debug "Max streams for protocol reached, blocking new stream",
|
||||||
conn, protocol = ms, maxIncomingStreams
|
conn, protocol = ms, maxIncomingStreams
|
||||||
return
|
return
|
||||||
|
@ -242,8 +260,32 @@ proc addHandler*(m: MultistreamSelect,
|
||||||
protocol: protocol,
|
protocol: protocol,
|
||||||
match: matcher))
|
match: matcher))
|
||||||
|
|
||||||
proc start*(m: MultistreamSelect) {.async.} =
|
proc start*(m: MultistreamSelect) {.async: (raises: [CancelledError]).} =
|
||||||
await allFutures(m.handlers.mapIt(it.protocol.start()))
|
let
|
||||||
|
handlers = m.handlers
|
||||||
|
futs = handlers.mapIt(it.protocol.start())
|
||||||
|
try:
|
||||||
|
await allFutures(futs)
|
||||||
|
for fut in futs:
|
||||||
|
await fut
|
||||||
|
except CancelledError as exc:
|
||||||
|
var pending: seq[Future[void].Raising([])]
|
||||||
|
for i, fut in futs:
|
||||||
|
if not fut.finished:
|
||||||
|
pending.add noCancel fut.cancelAndWait()
|
||||||
|
elif fut.completed:
|
||||||
|
pending.add handlers[i].protocol.stop()
|
||||||
|
else:
|
||||||
|
static: doAssert typeof(fut).E is (CancelledError,)
|
||||||
|
await noCancel allFutures(pending)
|
||||||
|
raise exc
|
||||||
|
|
||||||
proc stop*(m: MultistreamSelect) {.async.} =
|
|
||||||
await allFutures(m.handlers.mapIt(it.protocol.stop()))
|
proc stop*(m: MultistreamSelect) {.async: (raises: []).} =
|
||||||
|
# Nim 1.6.18: Using `mapIt` results in a seq of `.Raising([CancelledError])`
|
||||||
|
var futs = newSeqOfCap[Future[void].Raising([])](m.handlers.len)
|
||||||
|
for it in m.handlers:
|
||||||
|
futs.add it.protocol.stop()
|
||||||
|
await noCancel allFutures(futs)
|
||||||
|
for fut in futs:
|
||||||
|
await fut
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nim-LibP2P
|
# Nim-LibP2P
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
@ -361,17 +361,25 @@ proc deletesReservation(r: Relay) {.async.} =
|
||||||
if n > r.rsvp[k]:
|
if n > r.rsvp[k]:
|
||||||
r.rsvp.del(k)
|
r.rsvp.del(k)
|
||||||
|
|
||||||
method start*(r: Relay) {.async.} =
|
method start*(
|
||||||
|
r: Relay
|
||||||
|
): Future[void] {.async: (raises: [CancelledError], raw: true).} =
|
||||||
|
let fut = newFuture[void]()
|
||||||
|
fut.complete()
|
||||||
if not r.reservationLoop.isNil:
|
if not r.reservationLoop.isNil:
|
||||||
warn "Starting relay twice"
|
warn "Starting relay twice"
|
||||||
return
|
return fut
|
||||||
r.reservationLoop = r.deletesReservation()
|
r.reservationLoop = r.deletesReservation()
|
||||||
r.started = true
|
r.started = true
|
||||||
|
fut
|
||||||
|
|
||||||
method stop*(r: Relay) {.async.} =
|
method stop*(r: Relay): Future[void] {.async: (raises: [], raw: true).} =
|
||||||
|
let fut = newFuture[void]()
|
||||||
|
fut.complete()
|
||||||
if r.reservationLoop.isNil:
|
if r.reservationLoop.isNil:
|
||||||
warn "Stopping relay without starting it"
|
warn "Stopping relay without starting it"
|
||||||
return
|
return fut
|
||||||
r.started = false
|
r.started = false
|
||||||
r.reservationLoop.cancel()
|
r.reservationLoop.cancel()
|
||||||
r.reservationLoop = nil
|
r.reservationLoop = nil
|
||||||
|
fut
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nim-LibP2P
|
# Nim-LibP2P
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
@ -31,8 +31,19 @@ type
|
||||||
maxIncomingStreams: Opt[int]
|
maxIncomingStreams: Opt[int]
|
||||||
|
|
||||||
method init*(p: LPProtocol) {.base, gcsafe.} = discard
|
method init*(p: LPProtocol) {.base, gcsafe.} = discard
|
||||||
method start*(p: LPProtocol) {.async, base.} = p.started = true
|
|
||||||
method stop*(p: LPProtocol) {.async, base.} = p.started = false
|
method start*(
|
||||||
|
p: LPProtocol) {.async: (raises: [CancelledError], raw: true), base.} =
|
||||||
|
let fut = newFuture[void]()
|
||||||
|
fut.complete()
|
||||||
|
p.started = true
|
||||||
|
fut
|
||||||
|
|
||||||
|
method stop*(p: LPProtocol) {.async: (raises: [], raw: true), base.} =
|
||||||
|
let fut = newFuture[void]()
|
||||||
|
fut.complete()
|
||||||
|
p.started = false
|
||||||
|
fut
|
||||||
|
|
||||||
proc maxIncomingStreams*(p: LPProtocol): int =
|
proc maxIncomingStreams*(p: LPProtocol): int =
|
||||||
p.maxIncomingStreams.get(DefaultMaxIncomingStreams)
|
p.maxIncomingStreams.get(DefaultMaxIncomingStreams)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nim-LibP2P
|
# Nim-LibP2P
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
@ -701,30 +701,40 @@ proc maintainDirectPeers(g: GossipSub) {.async.} =
|
||||||
for id, addrs in g.parameters.directPeers:
|
for id, addrs in g.parameters.directPeers:
|
||||||
await g.addDirectPeer(id, addrs)
|
await g.addDirectPeer(id, addrs)
|
||||||
|
|
||||||
method start*(g: GossipSub) {.async.} =
|
method start*(
|
||||||
|
g: GossipSub
|
||||||
|
): Future[void] {.async: (raises: [CancelledError], raw: true).} =
|
||||||
|
let fut = newFuture[void]()
|
||||||
|
fut.complete()
|
||||||
|
|
||||||
trace "gossipsub start"
|
trace "gossipsub start"
|
||||||
|
|
||||||
if not g.heartbeatFut.isNil:
|
if not g.heartbeatFut.isNil:
|
||||||
warn "Starting gossipsub twice"
|
warn "Starting gossipsub twice"
|
||||||
return
|
return fut
|
||||||
|
|
||||||
g.heartbeatFut = g.heartbeat()
|
g.heartbeatFut = g.heartbeat()
|
||||||
g.scoringHeartbeatFut = g.scoringHeartbeat()
|
g.scoringHeartbeatFut = g.scoringHeartbeat()
|
||||||
g.directPeersLoop = g.maintainDirectPeers()
|
g.directPeersLoop = g.maintainDirectPeers()
|
||||||
g.started = true
|
g.started = true
|
||||||
|
fut
|
||||||
|
|
||||||
|
method stop*(g: GossipSub): Future[void] {.async: (raises: [], raw: true).} =
|
||||||
|
let fut = newFuture[void]()
|
||||||
|
fut.complete()
|
||||||
|
|
||||||
method stop*(g: GossipSub) {.async.} =
|
|
||||||
trace "gossipsub stop"
|
trace "gossipsub stop"
|
||||||
g.started = false
|
g.started = false
|
||||||
if g.heartbeatFut.isNil:
|
if g.heartbeatFut.isNil:
|
||||||
warn "Stopping gossipsub without starting it"
|
warn "Stopping gossipsub without starting it"
|
||||||
return
|
return fut
|
||||||
|
|
||||||
# stop heartbeat interval
|
# stop heartbeat interval
|
||||||
g.directPeersLoop.cancel()
|
g.directPeersLoop.cancel()
|
||||||
g.scoringHeartbeatFut.cancel()
|
g.scoringHeartbeatFut.cancel()
|
||||||
g.heartbeatFut.cancel()
|
g.heartbeatFut.cancel()
|
||||||
g.heartbeatFut = nil
|
g.heartbeatFut = nil
|
||||||
|
fut
|
||||||
|
|
||||||
method initPubSub*(g: GossipSub)
|
method initPubSub*(g: GossipSub)
|
||||||
{.raises: [InitializationError].} =
|
{.raises: [InitializationError].} =
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nim-LibP2P
|
# Nim-LibP2P
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
@ -678,17 +678,25 @@ proc deletesRegister(rdv: RendezVous) {.async.} =
|
||||||
libp2p_rendezvous_registered.set(int64(total))
|
libp2p_rendezvous_registered.set(int64(total))
|
||||||
libp2p_rendezvous_namespaces.set(int64(rdv.namespaces.len))
|
libp2p_rendezvous_namespaces.set(int64(rdv.namespaces.len))
|
||||||
|
|
||||||
method start*(rdv: RendezVous) {.async.} =
|
method start*(
|
||||||
|
rdv: RendezVous
|
||||||
|
): Future[void] {.async: (raises: [CancelledError], raw: true).} =
|
||||||
|
let fut = newFuture[void]()
|
||||||
|
fut.complete()
|
||||||
if not rdv.registerDeletionLoop.isNil:
|
if not rdv.registerDeletionLoop.isNil:
|
||||||
warn "Starting rendezvous twice"
|
warn "Starting rendezvous twice"
|
||||||
return
|
return fut
|
||||||
rdv.registerDeletionLoop = rdv.deletesRegister()
|
rdv.registerDeletionLoop = rdv.deletesRegister()
|
||||||
rdv.started = true
|
rdv.started = true
|
||||||
|
fut
|
||||||
|
|
||||||
method stop*(rdv: RendezVous) {.async.} =
|
method stop*(rdv: RendezVous): Future[void] {.async: (raises: [], raw: true).} =
|
||||||
|
let fut = newFuture[void]()
|
||||||
|
fut.complete()
|
||||||
if rdv.registerDeletionLoop.isNil:
|
if rdv.registerDeletionLoop.isNil:
|
||||||
warn "Stopping rendezvous without starting it"
|
warn "Stopping rendezvous without starting it"
|
||||||
return
|
return fut
|
||||||
rdv.started = false
|
rdv.started = false
|
||||||
rdv.registerDeletionLoop.cancel()
|
rdv.registerDeletionLoop.cancel()
|
||||||
rdv.registerDeletionLoop = nil
|
rdv.registerDeletionLoop = nil
|
||||||
|
fut
|
||||||
|
|
Loading…
Reference in New Issue