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

lib/nat: Make service termination faster #6777

Merged
merged 2 commits into from
Jun 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 13 additions & 4 deletions lib/pmp/pmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/jackpal/gateway"
"github.com/jackpal/go-nat-pmp"
"github.com/pkg/errors"

"github.com/syncthing/syncthing/lib/nat"
"github.com/syncthing/syncthing/lib/util"
Expand Down Expand Up @@ -44,10 +45,18 @@ func Discover(ctx context.Context, renewal, timeout time.Duration) []nat.Device
c := natpmp.NewClientWithTimeout(ip, timeout)
// Try contacting the gateway, if it does not respond, assume it does not
// speak NAT-PMP.
_, err = c.GetExternalAddress()
if err != nil && strings.Contains(err.Error(), "Timed out") {
l.Debugln("Timeout trying to get external address, assume no NAT-PMP available")
return nil
err = util.CallWithContext(ctx, func() error {
_, ierr := c.GetExternalAddress()
return ierr
})
if err != nil {
if errors.Cause(err) == context.Canceled {
Copy link
Member

Choose a reason for hiding this comment

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

Does util specifically use github.com/pkg/errors for wrapping? If so I guess this is fine (but should be flagged for future refactor), otherwise errors.Is seems more appropriate here now

Copy link
Member

Choose a reason for hiding this comment

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

FYI: pkg/errors is compatible with go1.13's errors.Is/As methods.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, compatibility was withdrawn in pkg/errors 0.9.1.

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

func main() {
	err := fmt.Errorf("wrapped: %w", fmt.Errorf("inner"))
	fmt.Println(errors.Cause(err)) // prints wrapped: inner
}

Copy link
Member

Choose a reason for hiding this comment

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

Ok, not so nice - thanks for the hint. The other way around should still work as far as I see (i.e. you can use errors.Is on an error wrapped with errors.Wrap). So as we are using fmt.Errorf in places, we definitely shouldn't use errors.Cause anymore.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's a wider fix that needs to happen through the codebase, hence I am still merging this.

Also, I am not sure this actually affects context.Cancelled, as it's created with errors.New so does not have Is, so the only way this could go wrong if something wrapped the error which then implemented Is and explicitly returned false.

return nil
}
if strings.Contains(err.Error(), "Timed out") {
l.Debugln("Timeout trying to get external address, assume no NAT-PMP available")
return nil
}
}

var localIP net.IP
Expand Down
33 changes: 22 additions & 11 deletions lib/upnp/upnp.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,19 +119,26 @@ func Discover(ctx context.Context, renewal, timeout time.Duration) []nat.Device
}()

seenResults := make(map[string]bool)
for result := range resultChan {
if seenResults[result.ID()] {
l.Debugf("Skipping duplicate result %s", result.ID())
continue
}

results = append(results, result)
seenResults[result.ID()] = true
for {
select {
case result, ok := <-resultChan:
if !ok {
return results
}
if seenResults[result.ID()] {
l.Debugf("Skipping duplicate result %s", result.ID())
continue
}

l.Debugf("UPnP discovery result %s", result.ID())
}
results = append(results, result)
seenResults[result.ID()] = true

return results
l.Debugf("UPnP discovery result %s", result.ID())
case <-ctx.Done():
return nil
}
}
}

// Search for UPnP InternetGatewayDevices for <timeout> seconds.
Expand Down Expand Up @@ -213,7 +220,11 @@ loop:
}
for _, igd := range igds {
igd := igd // Copy before sending pointer to the channel.
results <- &igd
select {
case results <- &igd:
case <-ctx.Done():
return
}
}
}
l.Debugln("Discovery for device type", deviceType, "on", intf.Name, "finished.")
Expand Down