Merge pull request #18 from status-im/fix-android

Use x/sys/unix instead of syscall to fix EpollWait on Android
This commit is contained in:
Tevin 2019-11-12 16:03:14 +08:00 committed by GitHub
commit 5aa79aeb78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 42 additions and 32 deletions

View File

@ -7,5 +7,6 @@ go:
- "1.11.x"
install:
- go get "github.com/pkg/errors"
- go get "golang.org/x/sys/unix"
script: go test -v -bench=. -benchmem ./...

View File

@ -86,5 +86,6 @@ conn.Close()
## Special thanks to contributors
- @lujjjh
- @jakubgs Fixed compatibility on Android
[tcp-handshake]: https://en.wikipedia.org/wiki/Handshaking#TCP_three-way_handshake

View File

@ -4,10 +4,10 @@ import (
"context"
"sync"
"sync/atomic"
"syscall"
"time"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
// Checker contains an epoll instance for TCP handshake checking
@ -74,7 +74,7 @@ func (c *Checker) closePoller() error {
defer c.pollerLock.Unlock()
var err error
if c.pollerFD() > 0 {
err = syscall.Close(c.pollerFD())
err = unix.Close(c.pollerFD())
}
c.setPollerFD(-1)
return err
@ -151,7 +151,7 @@ func (c *Checker) CheckAddrZeroLinger(addr string, timeout time.Duration, zeroLi
return err
}
// Socket should be closed anyway
defer syscall.Close(fd)
defer unix.Close(fd)
// Connect to the address
if success, cErr := connect(fd, rAddr); cErr != nil {

5
err.go
View File

@ -2,7 +2,8 @@ package tcp
import (
"errors"
"syscall"
"golang.org/x/sys/unix"
)
// ErrTimeout indicates I/O timeout
@ -22,7 +23,7 @@ type ErrConnect struct {
// newErrConnect returns a ErrConnect with given error code
func newErrConnect(errCode int) *ErrConnect {
return &ErrConnect{syscall.Errno(errCode)}
return &ErrConnect{unix.Errno(errCode)}
}
// ErrCheckerAlreadyStarted indicates there is another instance of CheckingLoop running.

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module github.com/tevino/tcp-shaker
go 1.13
require golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 // indirect

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM=
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -3,11 +3,12 @@ package tcp
import (
"net"
"runtime"
"syscall"
"golang.org/x/sys/unix"
)
// parseSockAddr resolves given addr to syscall.Sockaddr
func parseSockAddr(addr string) (syscall.Sockaddr, error) {
// parseSockAddr resolves given addr to unix.Sockaddr
func parseSockAddr(addr string) (unix.Sockaddr, error) {
tAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return nil, err
@ -16,21 +17,21 @@ func parseSockAddr(addr string) (syscall.Sockaddr, error) {
if tAddr.IP != nil {
copy(addr4[:], tAddr.IP.To4()) // copy last 4 bytes of slice to array
}
return &syscall.SockaddrInet4{Port: tAddr.Port, Addr: addr4}, nil
return &unix.SockaddrInet4{Port: tAddr.Port, Addr: addr4}, nil
}
// connect calls the connect syscall with error handled.
func connect(fd int, addr syscall.Sockaddr) (success bool, err error) {
switch serr := syscall.Connect(fd, addr); serr {
case syscall.EALREADY, syscall.EINPROGRESS, syscall.EINTR:
func connect(fd int, addr unix.Sockaddr) (success bool, err error) {
switch serr := unix.Connect(fd, addr); serr {
case unix.EALREADY, unix.EINPROGRESS, unix.EINTR:
// Connection could not be made immediately but asynchronously.
success = false
err = nil
case nil, syscall.EISCONN:
case nil, unix.EISCONN:
// The specified socket is already connected.
success = true
err = nil
case syscall.EINVAL:
case unix.EINVAL:
// On Solaris we can see EINVAL if the socket has
// already been accepted and closed by the server.
// Treat this as a successful connection--writes to

View File

@ -3,8 +3,9 @@ package tcp
import (
"fmt"
"os"
"syscall"
"time"
"golang.org/x/sys/unix"
)
const maxEpollEvents = 32
@ -31,50 +32,48 @@ func _createNonBlockingSocket() (int, error) {
// Set necessary options
err = _setSockOpts(fd)
if err != nil {
syscall.Close(fd)
unix.Close(fd)
}
return fd, err
}
// createSocket creates a socket with CloseOnExec set
func _createSocket() (int, error) {
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
syscall.CloseOnExec(fd)
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
unix.CloseOnExec(fd)
return fd, err
}
// setSockOpts sets SOCK_NONBLOCK and TCP_QUICKACK for given fd
func _setSockOpts(fd int) error {
err := syscall.SetNonblock(fd, true)
err := unix.SetNonblock(fd, true)
if err != nil {
return err
}
return syscall.SetsockoptInt(fd, syscall.SOL_TCP, syscall.TCP_QUICKACK, 0)
return unix.SetsockoptInt(fd, unix.IPPROTO_TCP, unix.TCP_QUICKACK, 0)
}
var zeroLinger = syscall.Linger{Onoff: 1, Linger: 0}
var zeroLinger = unix.Linger{Onoff: 1, Linger: 0}
// setLinger sets SO_Linger with 0 timeout to given fd
func _setZeroLinger(fd int) error {
return syscall.SetsockoptLinger(fd, syscall.SOL_SOCKET, syscall.SO_LINGER, &zeroLinger)
return unix.SetsockoptLinger(fd, unix.SOL_SOCKET, unix.SO_LINGER, &zeroLinger)
}
func createPoller() (fd int, err error) {
fd, err = syscall.EpollCreate1(syscall.EPOLL_CLOEXEC)
fd, err = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
if err != nil {
err = os.NewSyscallError("epoll_create1", err)
}
return fd, err
}
const epollET = 1 << 31
// registerEvents registers given fd with read and write events.
func registerEvents(pollerFd int, fd int) error {
var event syscall.EpollEvent
event.Events = syscall.EPOLLOUT | syscall.EPOLLIN | epollET
var event unix.EpollEvent
event.Events = unix.EPOLLOUT | unix.EPOLLIN | unix.EPOLLET
event.Fd = int32(fd)
if err := syscall.EpollCtl(pollerFd, syscall.EPOLL_CTL_ADD, fd, &event); err != nil {
if err := unix.EpollCtl(pollerFd, unix.EPOLL_CTL_ADD, fd, &event); err != nil {
return os.NewSyscallError(fmt.Sprintf("epoll_ctl(%d, ADD, %d, ...)", pollerFd, fd), err)
}
return nil
@ -82,10 +81,10 @@ func registerEvents(pollerFd int, fd int) error {
func pollEvents(pollerFd int, timeout time.Duration) ([]event, error) {
var timeoutMS = int(timeout.Nanoseconds() / 1000000)
var epollEvents [maxEpollEvents]syscall.EpollEvent
nEvents, err := syscall.EpollWait(pollerFd, epollEvents[:], timeoutMS)
var epollEvents [maxEpollEvents]unix.EpollEvent
nEvents, err := unix.EpollWait(pollerFd, epollEvents[:], timeoutMS)
if err != nil {
if err == syscall.EINTR {
if err == unix.EINTR {
return nil, nil
}
return nil, os.NewSyscallError("epoll_wait", err)
@ -97,7 +96,7 @@ func pollEvents(pollerFd int, timeout time.Duration) ([]event, error) {
var fd = int(epollEvents[i].Fd)
var evt = event{Fd: fd, Err: nil}
errCode, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_ERROR)
errCode, err := unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_ERROR)
if err != nil {
evt.Err = os.NewSyscallError("getsockopt", err)
}