Move triggers (#416)

* move event triggers to connmanager

* use base error type

* avoid deadlocks

* handle eof and closed when identifying incoming

* use `closeWait`
This commit is contained in:
Dmitriy Ryajov 2020-11-02 14:35:26 -06:00 committed by GitHub
parent 43a77e60a1
commit 7b5259dbc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 79 deletions

View File

@ -124,19 +124,20 @@ proc triggerPeerEvents*(c: ConnManager,
peerId: PeerID,
event: PeerEvent) {.async, gcsafe.} =
trace "About to trigger peer events", peer = peerId
if event notin c.peerEvents:
return
try:
let count = c.connCount(peerId)
if event == PeerEvent.Joined and count != 1:
trace "peer already joined", peerId, event
trace "peer already joined", peerId, event = $event
return
elif event == PeerEvent.Left and count != 0:
trace "peer still connected or already left", peerId, event
trace "peer still connected or already left", peerId, event = $event
return
trace "triggering peer events", peerId, event
trace "triggering peer events", peerId, event = $event
var peerEvents: seq[Future[void]]
for h in c.peerEvents[event]:
@ -146,7 +147,7 @@ proc triggerPeerEvents*(c: ConnManager,
except CancelledError as exc:
raise exc
except CatchableError as exc: # handlers should not raise!
warn "exception in triggerPeerEvents", exc = exc.msg, peerId
warn "Exception in triggerPeerEvents", exc = exc.msg, peerId
proc contains*(c: ConnManager, conn: Connection): bool =
## checks if a connection is being tracked by the
@ -224,6 +225,32 @@ proc cleanupConn(c: ConnManager, conn: Connection) {.async.} =
trace "Connection cleaned up", conn
proc peerStartup(c: ConnManager, conn: Connection) {.async.} =
try:
trace "Triggering peer and connection events on connect", conn
let peerId = conn.peerInfo.peerId
await c.triggerPeerEvents(peerId, PeerEvent.Joined)
await c.triggerConnEvent(
peerId, ConnEvent(kind: ConnEventKind.Connected, incoming: conn.dir == Direction.In))
except CatchableError as exc:
# This is top-level procedure which will work as separate task, so it
# do not need to propagate CancelledError and should handle other errors
warn "Unexpected exception in switch peer connection cleanup",
conn, msg = exc.msg
proc peerCleanup(c: ConnManager, conn: Connection) {.async.} =
try:
trace "Triggering peer and connection events on disconnect", conn
let peerId = conn.peerInfo.peerId
await c.triggerConnEvent(
peerId, ConnEvent(kind: ConnEventKind.Disconnected))
await c.triggerPeerEvents(peerId, PeerEvent.Left)
except CatchableError as exc:
# This is top-level procedure which will work as separate task, so it
# do not need to propagate CancelledError and should handle other errors
warn "Unexpected exception peer cleanup handler",
conn, msg = exc.msg
proc onClose(c: ConnManager, conn: Connection) {.async.} =
## connection close even handler
##
@ -235,11 +262,14 @@ proc onClose(c: ConnManager, conn: Connection) {.async.} =
await c.cleanupConn(conn)
except CancelledError:
# This is top-level procedure which will work as separate task, so it
# do not need to propogate CancelledError.
# do not need to propagate CancelledError.
debug "Unexpected cancellation in connection manager's cleanup", conn
except CatchableError as exc:
debug "Unexpected exception in connection manager's cleanup",
errMsg = exc.msg, conn
finally:
trace "Triggering peerCleanup", conn
asyncSpawn c.peerCleanup(conn)
proc selectConn*(c: ConnManager,
peerId: PeerID,
@ -335,6 +365,8 @@ proc storeMuxer*(c: ConnManager,
trace "Stored muxer",
muxer, handle = not handle.isNil, connections = c.conns.len
asyncSpawn c.peerStartup(muxer.connection)
proc getMuxedStream*(c: ConnManager,
peerId: PeerID,
dir: Direction): Future[Connection] {.async, gcsafe.} =

View File

@ -30,8 +30,9 @@ const
#TODO: implement push identify, leaving out for now as it is not essential
type
IdentityNoMatchError* = object of CatchableError
IdentityInvalidMsgError* = object of CatchableError
IdentifyError* = object of CatchableError
IdentityNoMatchError* = object of IdentifyError
IdentityInvalidMsgError* = object of IdentifyError
IdentifyInfo* = object
pubKey*: Option[PublicKey]
@ -138,9 +139,6 @@ proc identify*(p: Identify,
if peer.isErr:
raise newException(IdentityInvalidMsgError, $peer.error)
else:
# do a string comaprison of the ids,
# because that is the only thing we
# have in most cases
if peer.get() != remotePeerInfo.peerId:
trace "Peer ids don't match",
remote = peer,

View File

@ -167,13 +167,9 @@ proc mux(s: Switch, conn: Connection): Future[Muxer] {.async, gcsafe.} =
muxer.streamHandler = s.streamHandler
s.connManager.storeOutgoing(conn)
s.connManager.storeMuxer(muxer)
# start muxer read loop - the future will complete when loop ends
let handlerFut = muxer.handle()
# store it in muxed connections if we have a peer for it
s.connManager.storeMuxer(muxer, handlerFut) # update muxer with handler
s.connManager.storeMuxer(muxer, muxer.handle()) # store muxer and start read loop
return muxer
@ -308,7 +304,7 @@ proc internalConnect(s: Switch,
await s.upgradeOutgoing(dialed)
except CatchableError as exc:
# If we failed to establish the connection through one transport,
# we won't succeeed through another - no use in trying again
# we won't succeeded through another - no use in trying again
await dialed.close()
debug "Upgrade failed", msg = exc.msg, peerId
if exc isnot CancelledError:
@ -327,30 +323,13 @@ proc internalConnect(s: Switch,
if isNil(conn): # None of the addresses connected
raise newException(CatchableError, "Unable to establish outgoing link")
if conn.closed():
# This can happen if one of the peer event handlers deems the peer
# unworthy and disconnects it
if conn.closed() or conn.atEof():
# This can happen when the other ends drops us
# before we get a chance to return the connection
# back to the dialer.
trace "Connection dead on arrival", conn
raise newLPStreamClosedError()
await s.connManager.triggerPeerEvents(peerId, PeerEvent.Joined)
await s.connManager.triggerConnEvent(
peerId, ConnEvent(kind: ConnEventKind.Connected, incoming: false))
proc peerCleanup() {.async.} =
try:
await conn.closeEvent.wait()
await s.connManager.triggerConnEvent(
peerId, ConnEvent(kind: ConnEventKind.Disconnected))
await s.connManager.triggerPeerEvents(peerId, PeerEvent.Left)
except CatchableError as exc:
# This is top-level procedure which will work as separate task, so it
# do not need to propogate CancelledError and should handle other errors
warn "Unexpected exception in switch peer connect cleanup",
conn, msg = exc.msg
# All the errors are handled inside `cleanup()` procedure.
asyncSpawn peerCleanup()
return conn
proc connect*(s: Switch, peerId: PeerID, addrs: seq[MultiAddress]) {.async.} =
@ -486,45 +465,17 @@ proc muxerHandler(s: Switch, muxer: Muxer) {.async, gcsafe.} =
s.connManager.storeMuxer(muxer)
try:
await s.identify(muxer)
except CatchableError as exc:
# Identify is non-essential, though if it fails, it might indicate that
# the connection was closed already - this will be picked up by the read
# loop
debug "Could not identify connection", conn, msg = exc.msg
try:
let peerId = conn.peerInfo.peerId
proc peerCleanup() {.async.} =
try:
await muxer.connection.join()
await s.connManager.triggerConnEvent(
peerId, ConnEvent(kind: ConnEventKind.Disconnected))
await s.connManager.triggerPeerEvents(peerId, PeerEvent.Left)
except CatchableError as exc:
# This is top-level procedure which will work as separate task, so it
# do not need to propogate CancelledError and shouldn't leak others
debug "Unexpected exception in switch muxer cleanup",
conn, msg = exc.msg
proc peerStartup() {.async.} =
try:
await s.connManager.triggerPeerEvents(peerId, PeerEvent.Joined)
await s.connManager.triggerConnEvent(peerId,
ConnEvent(kind: ConnEventKind.Connected, incoming: true))
except CatchableError as exc:
# This is top-level procedure which will work as separate task, so it
# do not need to propogate CancelledError and shouldn't leak others
debug "Unexpected exception in switch muxer startup",
conn, msg = exc.msg
# All the errors are handled inside `peerStartup()` procedure.
asyncSpawn peerStartup()
# All the errors are handled inside `peerCleanup()` procedure.
asyncSpawn peerCleanup()
try:
await s.identify(muxer)
except IdentifyError as exc:
# Identify is non-essential, though if it fails, it might indicate that
# the connection was closed already - this will be picked up by the read
# loop
debug "Could not identify connection", conn, msg = exc.msg
except LPStreamClosedError as exc:
debug "Identify stream closed", conn, msg = exc.msg
except LPStreamEOFError as exc:
debug "Identify stream EOF", conn, msg = exc.msg
except CancelledError as exc:
await muxer.close()
raise exc

View File

@ -72,7 +72,6 @@ proc connHandler*(t: TcpTransport,
else:
Direction.In))
conn.observedAddr = MultiAddress.init(client.remoteAddress).tryGet()
if not initiator:
if not isNil(t.handler):
t.handlers &= t.handler(conn)
@ -94,7 +93,15 @@ proc connHandler*(t: TcpTransport,
t.clients.add(client)
# All the errors are handled inside `cleanup()` procedure.
asyncSpawn cleanup()
result = conn
try:
conn.observedAddr = MultiAddress.init(client.remoteAddress).tryGet()
except CatchableError as exc:
trace "Connection setup failed", exc = exc.msg
if not(isNil(client)):
client.close()
return conn
proc connCb(server: StreamServer,
client: StreamTransport) {.async, gcsafe.} =