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, peerId: PeerID,
event: PeerEvent) {.async, gcsafe.} = event: PeerEvent) {.async, gcsafe.} =
trace "About to trigger peer events", peer = peerId
if event notin c.peerEvents: if event notin c.peerEvents:
return return
try: try:
let count = c.connCount(peerId) let count = c.connCount(peerId)
if event == PeerEvent.Joined and count != 1: if event == PeerEvent.Joined and count != 1:
trace "peer already joined", peerId, event trace "peer already joined", peerId, event = $event
return return
elif event == PeerEvent.Left and count != 0: 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 return
trace "triggering peer events", peerId, event trace "triggering peer events", peerId, event = $event
var peerEvents: seq[Future[void]] var peerEvents: seq[Future[void]]
for h in c.peerEvents[event]: for h in c.peerEvents[event]:
@ -146,7 +147,7 @@ proc triggerPeerEvents*(c: ConnManager,
except CancelledError as exc: except CancelledError as exc:
raise exc raise exc
except CatchableError as exc: # handlers should not raise! 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 = proc contains*(c: ConnManager, conn: Connection): bool =
## checks if a connection is being tracked by the ## 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 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.} = proc onClose(c: ConnManager, conn: Connection) {.async.} =
## connection close even handler ## connection close even handler
## ##
@ -235,11 +262,14 @@ proc onClose(c: ConnManager, conn: Connection) {.async.} =
await c.cleanupConn(conn) await c.cleanupConn(conn)
except CancelledError: except CancelledError:
# This is top-level procedure which will work as separate task, so it # 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 debug "Unexpected cancellation in connection manager's cleanup", conn
except CatchableError as exc: except CatchableError as exc:
debug "Unexpected exception in connection manager's cleanup", debug "Unexpected exception in connection manager's cleanup",
errMsg = exc.msg, conn errMsg = exc.msg, conn
finally:
trace "Triggering peerCleanup", conn
asyncSpawn c.peerCleanup(conn)
proc selectConn*(c: ConnManager, proc selectConn*(c: ConnManager,
peerId: PeerID, peerId: PeerID,
@ -335,6 +365,8 @@ proc storeMuxer*(c: ConnManager,
trace "Stored muxer", trace "Stored muxer",
muxer, handle = not handle.isNil, connections = c.conns.len muxer, handle = not handle.isNil, connections = c.conns.len
asyncSpawn c.peerStartup(muxer.connection)
proc getMuxedStream*(c: ConnManager, proc getMuxedStream*(c: ConnManager,
peerId: PeerID, peerId: PeerID,
dir: Direction): Future[Connection] {.async, gcsafe.} = 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 #TODO: implement push identify, leaving out for now as it is not essential
type type
IdentityNoMatchError* = object of CatchableError IdentifyError* = object of CatchableError
IdentityInvalidMsgError* = object of CatchableError IdentityNoMatchError* = object of IdentifyError
IdentityInvalidMsgError* = object of IdentifyError
IdentifyInfo* = object IdentifyInfo* = object
pubKey*: Option[PublicKey] pubKey*: Option[PublicKey]
@ -138,9 +139,6 @@ proc identify*(p: Identify,
if peer.isErr: if peer.isErr:
raise newException(IdentityInvalidMsgError, $peer.error) raise newException(IdentityInvalidMsgError, $peer.error)
else: else:
# do a string comaprison of the ids,
# because that is the only thing we
# have in most cases
if peer.get() != remotePeerInfo.peerId: if peer.get() != remotePeerInfo.peerId:
trace "Peer ids don't match", trace "Peer ids don't match",
remote = peer, remote = peer,

View File

@ -167,13 +167,9 @@ proc mux(s: Switch, conn: Connection): Future[Muxer] {.async, gcsafe.} =
muxer.streamHandler = s.streamHandler muxer.streamHandler = s.streamHandler
s.connManager.storeOutgoing(conn) 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 # 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 return muxer
@ -308,7 +304,7 @@ proc internalConnect(s: Switch,
await s.upgradeOutgoing(dialed) await s.upgradeOutgoing(dialed)
except CatchableError as exc: except CatchableError as exc:
# If we failed to establish the connection through one transport, # 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() await dialed.close()
debug "Upgrade failed", msg = exc.msg, peerId debug "Upgrade failed", msg = exc.msg, peerId
if exc isnot CancelledError: if exc isnot CancelledError:
@ -327,30 +323,13 @@ proc internalConnect(s: Switch,
if isNil(conn): # None of the addresses connected if isNil(conn): # None of the addresses connected
raise newException(CatchableError, "Unable to establish outgoing link") raise newException(CatchableError, "Unable to establish outgoing link")
if conn.closed(): if conn.closed() or conn.atEof():
# This can happen if one of the peer event handlers deems the peer # This can happen when the other ends drops us
# unworthy and disconnects it # before we get a chance to return the connection
# back to the dialer.
trace "Connection dead on arrival", conn
raise newLPStreamClosedError() 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 return conn
proc connect*(s: Switch, peerId: PeerID, addrs: seq[MultiAddress]) {.async.} = proc connect*(s: Switch, peerId: PeerID, addrs: seq[MultiAddress]) {.async.} =
@ -485,46 +464,18 @@ proc muxerHandler(s: Switch, muxer: Muxer) {.async, gcsafe.} =
# store muxer and muxed connection # store muxer and muxed connection
s.connManager.storeMuxer(muxer) s.connManager.storeMuxer(muxer)
try:
try: try:
await s.identify(muxer) await s.identify(muxer)
except CatchableError as exc: except IdentifyError as exc:
# Identify is non-essential, though if it fails, it might indicate that # 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 # the connection was closed already - this will be picked up by the read
# loop # loop
debug "Could not identify connection", conn, msg = exc.msg debug "Could not identify connection", conn, msg = exc.msg
except LPStreamClosedError as exc:
try: debug "Identify stream closed", conn, msg = exc.msg
let peerId = conn.peerInfo.peerId except LPStreamEOFError as exc:
debug "Identify stream EOF", conn, msg = exc.msg
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()
except CancelledError as exc: except CancelledError as exc:
await muxer.close() await muxer.close()
raise exc raise exc

View File

@ -72,7 +72,6 @@ proc connHandler*(t: TcpTransport,
else: else:
Direction.In)) Direction.In))
conn.observedAddr = MultiAddress.init(client.remoteAddress).tryGet()
if not initiator: if not initiator:
if not isNil(t.handler): if not isNil(t.handler):
t.handlers &= t.handler(conn) t.handlers &= t.handler(conn)
@ -94,7 +93,15 @@ proc connHandler*(t: TcpTransport,
t.clients.add(client) t.clients.add(client)
# All the errors are handled inside `cleanup()` procedure. # All the errors are handled inside `cleanup()` procedure.
asyncSpawn cleanup() 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, proc connCb(server: StreamServer,
client: StreamTransport) {.async, gcsafe.} = client: StreamTransport) {.async, gcsafe.} =