From 39e929c059b02330b2e8b9238cd5a431fcc45f02 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Tue, 14 Dec 2021 13:29:56 +0000 Subject: [PATCH] Delay closing until the next loop tick Motivation: When the idle handler determines that the channel needs to be closed (becuase the connection is no longer required) it does so on the current event loop tick. Closing immediately means that some events which are already scheduled to run on the current tick may be dropped unexpectedly. Modifications: - Execute the channel close on the next event-loop tick. - Fixup a test which now requires an extra loop `run()`. Result: Shutdown is a little more graceful. --- Sources/GRPC/GRPCIdleHandler.swift | 8 ++++++-- Tests/GRPCTests/ConnectionManagerTests.swift | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Sources/GRPC/GRPCIdleHandler.swift b/Sources/GRPC/GRPCIdleHandler.swift index 26f44de18..35c500435 100644 --- a/Sources/GRPC/GRPCIdleHandler.swift +++ b/Sources/GRPC/GRPCIdleHandler.swift @@ -157,8 +157,12 @@ internal final class GRPCIdleHandler: ChannelInboundHandler { } // Close the channel, if necessary. - if operations.shouldCloseChannel { - self.context?.close(mode: .all, promise: nil) + if operations.shouldCloseChannel, let context = self.context { + // Close on the next event-loop tick so we don't drop any events which are + // currently being processed. + context.eventLoop.execute { + context.close(mode: .all, promise: nil) + } } } diff --git a/Tests/GRPCTests/ConnectionManagerTests.swift b/Tests/GRPCTests/ConnectionManagerTests.swift index e1bd49b24..bcc93b8ff 100644 --- a/Tests/GRPCTests/ConnectionManagerTests.swift +++ b/Tests/GRPCTests/ConnectionManagerTests.swift @@ -800,6 +800,7 @@ extension ConnectionManagerTests { payload: .goAway(lastStreamID: 1, errorCode: .noError, opaqueData: nil) ) XCTAssertNoThrow(try channel.writeInbound(goAway)) + self.loop.run() } self.loop.run()