133 lines
2.9 KiB
Go
133 lines
2.9 KiB
Go
|
//go:build android && amd64
|
||
|
// +build android,amd64
|
||
|
|
||
|
package tcp
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"time"
|
||
|
|
||
|
"golang.org/x/sys/unix"
|
||
|
)
|
||
|
|
||
|
const maxEpollEvents = 32
|
||
|
|
||
|
// createSocket creates a socket with necessary options set.
|
||
|
func createSocketZeroLinger(zeroLinger bool) (fd int, err error) {
|
||
|
// Create socket
|
||
|
fd, err = _createNonBlockingSocket()
|
||
|
if err == nil && zeroLinger {
|
||
|
err = _setZeroLinger(fd)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// createNonBlockingSocket creates a non-blocking socket with necessary options all set.
|
||
|
func _createNonBlockingSocket() (int, error) {
|
||
|
// Create socket
|
||
|
fd, err := _createSocket()
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
// Set necessary options
|
||
|
err = _setSockOpts(fd)
|
||
|
if err != nil {
|
||
|
unix.Close(fd)
|
||
|
}
|
||
|
return fd, err
|
||
|
}
|
||
|
|
||
|
// createSocket creates a socket with CloseOnExec set
|
||
|
func _createSocket() (int, error) {
|
||
|
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
|
||
|
if err != nil {
|
||
|
return 0, os.NewSyscallError("socket", err)
|
||
|
}
|
||
|
|
||
|
unix.CloseOnExec(fd)
|
||
|
return fd, err
|
||
|
}
|
||
|
|
||
|
// setSockOpts sets SOCK_NONBLOCK and TCP_QUICKACK for given fd
|
||
|
func _setSockOpts(fd int) error {
|
||
|
err := unix.SetNonblock(fd, true)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return unix.SetsockoptInt(fd, unix.IPPROTO_TCP, unix.TCP_QUICKACK, 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 unix.SetsockoptLinger(fd, unix.SOL_SOCKET, unix.SO_LINGER, &zeroLinger)
|
||
|
}
|
||
|
|
||
|
func createPoller() (fd int, err error) {
|
||
|
fd, err = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
|
||
|
if err != nil {
|
||
|
err = os.NewSyscallError("epoll_create1", err)
|
||
|
}
|
||
|
return fd, err
|
||
|
}
|
||
|
|
||
|
// registerEvents registers given fd with read and write events.
|
||
|
func registerEvents(pollerFd int, fd int) error {
|
||
|
var event unix.EpollEvent
|
||
|
event.Events = unix.EPOLLOUT | unix.EPOLLIN | unix.EPOLLET
|
||
|
event.Fd = int32(fd)
|
||
|
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
|
||
|
}
|
||
|
|
||
|
func pollEvents(pollerFd int, timeout time.Duration) ([]event, error) {
|
||
|
eventCh := make(chan event)
|
||
|
errorCh := make(chan error)
|
||
|
doneCh := make(chan bool)
|
||
|
|
||
|
go func(fd int) {
|
||
|
for {
|
||
|
select {
|
||
|
case <-doneCh:
|
||
|
return
|
||
|
default:
|
||
|
n, _, err := unix.Recvfrom(fd, nil, unix.MSG_DONTWAIT|unix.MSG_PEEK)
|
||
|
if err != nil && err != unix.EAGAIN && err != unix.EWOULDBLOCK {
|
||
|
errorCh <- os.NewSyscallError("recvfrom", err)
|
||
|
return
|
||
|
}
|
||
|
if n > 0 {
|
||
|
eventCh <- event{Fd: fd, Err: nil}
|
||
|
}
|
||
|
time.Sleep(10 * time.Millisecond)
|
||
|
}
|
||
|
}
|
||
|
}(pollerFd)
|
||
|
|
||
|
var events []event
|
||
|
|
||
|
timer := time.NewTimer(timeout)
|
||
|
defer timer.Stop()
|
||
|
|
||
|
Loop:
|
||
|
for {
|
||
|
select {
|
||
|
case evt := <-eventCh:
|
||
|
events = append(events, evt)
|
||
|
case err := <-errorCh:
|
||
|
return nil, err
|
||
|
case <-timer.C:
|
||
|
break Loop
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Signal the goroutine to stop.
|
||
|
close(doneCh)
|
||
|
|
||
|
return events, nil
|
||
|
}
|