Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remotedb/grpcdb: fix net.Listener leak in ListenAndServe #269

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion remotedb/grpcdb/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@ import (
// ListenAndServe is a blocking function that sets up a gRPC based
// server at the address supplied, with the gRPC options passed in.
// Normally in usage, invoke it in a goroutine like you would for http.ListenAndServe.
func ListenAndServe(addr, cert, key string, opts ...grpc.ServerOption) error {
func ListenAndServe(addr, cert, key string, opts ...grpc.ServerOption) (rerr error) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain how rerr is ever mutated so that line 25 could ever be true?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go has a feature called "named results" https://go.dev/ref/spec#Function_types whereby if you name a return value, in a defer you can check against that value so what the last return set will now be captured by that return.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return at the end sets it. (Normal returns also set named return values)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
defer func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some reason we shouldn't simply defer the close unconditionally? This function blocks until the server terminates anyway, and we're not checking the result of the Close call anyway.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes:

  1. It is the simplest and most non-invasive fix, as I've pushed up :-)
  2. If at all NewServer returns a value whose .Serve method has goroutines that might do other work on the listener, or also later on invokes .Close on the listener, that could cause problems and the biggest one being an already closed error being reported (something that I've seen in many code bases)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listener is created in this scope, it cannot be closed anywhere else.

if rerr != nil {
_ = ln.Close()
}
}()

srv, err := NewServer(cert, key, opts...)
if err != nil {
return err
Expand Down