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

x/mobile/bind: ios app crashes with SIGPIPE in background #17393

Closed
steeve opened this issue Oct 10, 2016 · 10 comments
Closed

x/mobile/bind: ios app crashes with SIGPIPE in background #17393

steeve opened this issue Oct 10, 2016 · 10 comments
Milestone

Comments

@steeve
Copy link
Contributor

steeve commented Oct 10, 2016

What version of Go are you using (go version)?

go version devel +56d35d4 Sun Oct 9 00:22:59 2016 +0000 darwin/amd64
gomobile version +6ea0bb5 Wed Oct 5 13:16:13 2016 +0000 (android,ios); androidSDK=

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/steeve/projects/go"
GORACE=""
GOROOT="/usr/local/Cellar/go/HEAD-56d35d4/libexec"
GOTOOLDIR="/usr/local/Cellar/go/HEAD-56d35d4/libexec/pkg/tool/darwin_amd64"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/90/cc7sdl690pd6j0lsppxrgk_m0000gn/T/go-build270609370=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"

What did you do?

App is attached to XCode's lldb.
Try to wake an app with a gomobile bind binary makes it crash when the device was locked for too long. Usually after ~2 minutes.
I can reproduce it if I make a HTTP call to Internet.
Strangely enough, this seems not to occur when trying to get the raw IP address.

What did you expect to see?

Program resume normally.

What did you see instead?

Program crashes with the following stack traces:

* thread #1: tid = 0x16cece, 0x0000000180fac16c libsystem_kernel.dylib`mach_msg_trap + 8, queue = 'com.apple.main-thread', stop reason = signal SIGPIPE
    frame #0: 0x0000000180fac16c libsystem_kernel.dylib`mach_msg_trap + 8
    frame #1: 0x0000000180fabfdc libsystem_kernel.dylib`mach_msg + 72
    frame #2: 0x0000000181fa9cec CoreFoundation`__CFRunLoopServiceMachPort + 192
    frame #3: 0x0000000181fa7908 CoreFoundation`__CFRunLoopRun + 1132
    frame #4: 0x0000000181ed6048 CoreFoundation`CFRunLoopRunSpecific + 444
    frame #5: 0x0000000183959198 GraphicsServices`GSEventRunModal + 180
    frame #6: 0x0000000187eaf818 UIKit`-[UIApplication _run] + 684
  * frame #7: 0x0000000187eaa550 UIKit`UIApplicationMain + 208
    frame #8: 0x000000010002b6e4 my.go.app`main + 140 at AppDelegate.swift:14
    frame #9: 0x0000000180eb85b8 libdyld.dylib`start + 4
@steeve
Copy link
Contributor Author

steeve commented Oct 10, 2016

Actually, it may be that lldb is intercepting the SIGPIPE itself.

One workaround would be to create the socket with SO_NOSIGPIPE.

@steeve
Copy link
Contributor Author

steeve commented Oct 10, 2016

So I have implemented a NoSIGPIPEDialer/Transport that leverages SO_NOSIGPIPE, and the problem goes away.
Since threads are suspended while sleeping, it may be a good idea to force all connections to have this flag.

For future somebody who runs into this problem, here is some code:

// SilenceSIGPIPE configures the net.Conn in a way that silences SIGPIPEs with
// the SO_NOSIGPIPE socket option.
func SilenceSIGPIPE(c net.Conn) error {
    // use reflection until https://github.com/golang/go/issues/9661 is fixed
    fd := int(reflect.ValueOf(c).Elem().FieldByName("fd").Elem().FieldByName("sysfd").Int())
    return syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_NOSIGPIPE, 1)
}

// NoSIGPIPEDialer returns a dialer that won't SIGPIPE should a connection
// actually SIGPIPE. This prevents the debugger from intercepting the signal
// even though this is normal behaviour.
type NoSIGPIPEDialer net.Dialer

func (d *NoSIGPIPEDialer) handle(c net.Conn, err error) (net.Conn, error) {
    if err != nil {
        return nil, err
    }
    if err := SilenceSIGPIPE(c); err != nil {
        c.Close()
        return nil, err
    }
    return c, err
}

func (d *NoSIGPIPEDialer) Dial(network, address string) (net.Conn, error) {
    c, err := (*net.Dialer)(d).Dial(network, address)
    return d.handle(c, err)
}

func (d *NoSIGPIPEDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
    c, err := (*net.Dialer)(d).DialContext(ctx, network, address)
    return d.handle(c, err)
}

And http.Transport:

// NoSIGPIPETransport is a default HTTP transport (configured in the same manner)
var NoSIGPIPETransport http.RoundTripper = &http.Transport{
    Proxy: http.ProxyFromEnvironment,
    DialContext: (&NoSIGPIPEDialer{
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
        DualStack: true,
    }).DialContext,
    MaxIdleConns:          100,
    IdleConnTimeout:       90 * time.Second,
    TLSHandshakeTimeout:   10 * time.Second,
    ExpectContinueTimeout: 1 * time.Second,
}

func init() {
    // Install NoSIGPIPETransport as the default HTTP transport
    http.DefaultTransport = NoSIGPIPETransport
}

@steeve
Copy link
Contributor Author

steeve commented Oct 10, 2016

Also, this codes needs reflection until #9661 is fixed.

@gopherbot
Copy link
Contributor

CL https://golang.org/cl/32796 mentions this issue.

@steeve
Copy link
Contributor Author

steeve commented Nov 6, 2016

Thanks @eliasnaur !

Another workaround in the mean time is:

func init() {
    // Install NoSIGPIPEDialer as the default HTTP dialer
    defaultTransport := http.DefaultTransport.(*http.Transport)
    defaultTransport.DialContext = (&NoSIGPIPEDialer{
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
        DualStack: true,
    }).DialContext
}

@eliasnaur
Copy link
Contributor

CL 32796 was reverted (see #18100 for details) so the SIGPIPE fix will not be in Go 1.8. An easy workaround is to call signal.Notify(c, SIGPIPE) to block SIGPIPEs from crashing the program.

@steeve
Copy link
Contributor Author

steeve commented Dec 1, 2016

Thank you the information. We are still running our NoSIGPIPEDialer anyhow.

@eliasnaur
Copy link
Contributor

I've added a workaround in Gomobile so you shouldn't need your NoSIGPIPEDialer anymore: https://go-review.googlesource.com/c/33771/

@bradfitz bradfitz reopened this Dec 1, 2016
@bradfitz bradfitz modified the milestones: Go1.9, Unreleased Dec 1, 2016
@bradfitz
Copy link
Contributor

bradfitz commented Dec 1, 2016

Reopening per #18100 (comment)

@gopherbot
Copy link
Contributor

CL https://golang.org/cl/35960 mentions this issue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants