forked from tevino/tcp-shaker
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add amd64 implementation to fix crash on x86_64
- Loading branch information
1 parent
2158931
commit 571aa46
Showing
2 changed files
with
136 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
// +build linux | ||
//go:build linux && !android && !amd64 | ||
// +build linux,!android,!amd64 | ||
|
||
package tcp | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
//go:build linux && android && amd64 | ||
// +build linux,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 { | ||
if 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 | ||
} |