Do not wait on clients of terminated event loops in shutdownNow() - #256
Open
codeconsole wants to merge 1 commit into
Open
codeconsole wants to merge 1 commit into
codeconsole wants to merge 1 commit into
Conversation
shutdownNow() never returned when it was called again on a server that had been shut down, if a client connected just as the first shutdown began. shutdown() stops the event loop groups with no quiet period. A client accepted just before that can be registered with its worker event loop after the loop has closed its channels, so it is added to the channel group while the loop terminates. closeClients() in a later shutdownNow() then waits on channelGroup.close() forever: closing that client has to go through the terminated event loop, which rejects the work, so its close future never completes. Drop the channel group once the event loops have terminated. bind() creates a new one, so a server that is shut down and bound again still works.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
shutdownNow()never returns when it is called again on a server that has already been shut down, if a client connected just as the first shutdown began. Seen with 1.47.0 and onmain, with Netty 4.2.shutdown()stops the event loop groups with no quiet period. A client accepted just before that can have its registration run on its worker event loop after the loop has closed its channels, so it becomes active and is added tochannelGroupwhile the loop terminates. A latershutdownNow()then blocks incloseClients():Closing that client has to go through its terminated event loop, which rejects the work, so neither the client's close future nor the group's ever completes.
We hit this in Grails, where the application context stops the embedded server and a JVM shutdown hook stops it again as the JVM exits. That second
shutdownNow()kept the JVM from exiting.Fix
Drop the channel group once the event loops have terminated, so a later
shutdownNow()has no clients left to wait for:bind()creates a new group, so a server that is shut down and bound again still works.This does not close such a client's socket, which would also need its event loop; it only stops
shutdownNow()from waiting on it.Tests
MongoServerShutdownTestreproduces the hang deterministically. The server is bound with a single worker thread, and a mock backend'shandleCloseholds that thread until a second client's registration is queued behind it and the server has started to shut down. Without the fix the secondshutdownNow()times out every time; with it, it returns at once.