mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-15 12:36:58 +02:00
51 lines
1.5 KiB
Go
51 lines
1.5 KiB
Go
//go:build linux && !android
|
|
// +build linux,!android
|
|
|
|
package tio
|
|
|
|
import (
|
|
"os"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// blockOn parks the calling goroutine until fd is ready (events is POLLIN for
|
|
// reads, POLLOUT for writes) or shutdownFd signals teardown. It builds the
|
|
// pollfd array on the stack every call, so concurrent callers on the same
|
|
// Queue never share Revents storage: the previous shared-array implementation
|
|
// was a genuine Go data race when two writers parked in poll(2) at once (the
|
|
// kernel writing Revents while another goroutine zeroed it). Level-triggered
|
|
// events kept it from deadlocking, but it was still a race.
|
|
//
|
|
// Poll(2) is looped over EINTR. err is checked before the Revents bits are
|
|
// trusted, since a failed poll may leave them bogus. Returns os.ErrClosed when
|
|
// shutdown was signaled (POLLIN on shutdownFd) or either fd reported a problem
|
|
// condition (POLLHUP|POLLNVAL|POLLERR).
|
|
func blockOn(fd, shutdownFd int32, events int16) error {
|
|
const problemFlags = unix.POLLHUP | unix.POLLNVAL | unix.POLLERR
|
|
pfds := [2]unix.PollFd{
|
|
{Fd: fd, Events: events},
|
|
{Fd: shutdownFd, Events: unix.POLLIN},
|
|
}
|
|
var err error
|
|
for {
|
|
_, err = unix.Poll(pfds[:], -1)
|
|
if err != unix.EINTR {
|
|
break
|
|
}
|
|
}
|
|
tunEvents := pfds[0].Revents
|
|
shutdownEvents := pfds[1].Revents
|
|
// Check err before trusting the potentially bogus bits we just got.
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if shutdownEvents&(unix.POLLIN|problemFlags) != 0 {
|
|
return os.ErrClosed
|
|
}
|
|
if tunEvents&problemFlags != 0 {
|
|
return os.ErrClosed
|
|
}
|
|
return nil
|
|
}
|