From b962d7d791bb13830d62d7ae326780b413463971 Mon Sep 17 00:00:00 2001 From: Brady OBrien Date: Thu, 17 May 2018 17:58:54 -0500 Subject: Add FreeBSD support Signed-off-by: Brady OBrien --- rwcancel/fdset_default.go | 24 +++++++++ rwcancel/fdset_freebsd.go | 22 ++++++++ rwcancel/rwcancel.go | 113 ++++++++++++++++++++++++++++++++++++++++ rwcancel/rwcancel_unix.go | 127 --------------------------------------------- rwcancel/select_darwin.go | 12 ----- rwcancel/select_default.go | 14 +++++ 6 files changed, 173 insertions(+), 139 deletions(-) create mode 100644 rwcancel/fdset_default.go create mode 100644 rwcancel/fdset_freebsd.go create mode 100644 rwcancel/rwcancel.go delete mode 100644 rwcancel/rwcancel_unix.go delete mode 100644 rwcancel/select_darwin.go create mode 100644 rwcancel/select_default.go (limited to 'rwcancel') diff --git a/rwcancel/fdset_default.go b/rwcancel/fdset_default.go new file mode 100644 index 0000000..06e2695 --- /dev/null +++ b/rwcancel/fdset_default.go @@ -0,0 +1,24 @@ +// +build !freebsd + +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. + */ + +package rwcancel + +import "golang.org/x/sys/unix" + +type fdSet struct { + fdset unix.FdSet +} + +func (fdset *fdSet) set(i int) { + bits := 32 << (^uint(0) >> 63) + fdset.fdset.Bits[i/bits] |= 1 << uint(i%bits) +} + +func (fdset *fdSet) check(i int) bool { + bits := 32 << (^uint(0) >> 63) + return (fdset.fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0 +} diff --git a/rwcancel/fdset_freebsd.go b/rwcancel/fdset_freebsd.go new file mode 100644 index 0000000..39a7a4e --- /dev/null +++ b/rwcancel/fdset_freebsd.go @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. + */ + +package rwcancel + +import "golang.org/x/sys/unix" + +type fdSet struct { + fdset unix.FdSet +} + +func (fdset *fdSet) set(i int) { + bits := 32 << (^uint(0) >> 63) + fdset.fdset.X__fds_bits[i/bits] |= 1 << uint(i%bits) +} + +func (fdset *fdSet) check(i int) bool { + bits := 32 << (^uint(0) >> 63) + return (fdset.fdset.X__fds_bits[i/bits] & (1 << uint(i%bits))) != 0 +} diff --git a/rwcancel/rwcancel.go b/rwcancel/rwcancel.go new file mode 100644 index 0000000..aac743a --- /dev/null +++ b/rwcancel/rwcancel.go @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. + */ + +package rwcancel + +import ( + "errors" + "golang.org/x/sys/unix" + "os" + "syscall" +) + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +type RWCancel struct { + fd int + closingReader *os.File + closingWriter *os.File +} + +func NewRWCancel(fd int) (*RWCancel, error) { + err := unix.SetNonblock(fd, true) + if err != nil { + return nil, err + } + rwcancel := RWCancel{fd: fd} + + rwcancel.closingReader, rwcancel.closingWriter, err = os.Pipe() + if err != nil { + return nil, err + } + + return &rwcancel, nil +} + +/* https://golang.org/src/crypto/rand/eagain.go */ +func ErrorIsEAGAIN(err error) bool { + if pe, ok := err.(*os.PathError); ok { + if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EAGAIN { + return true + } + } + if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN { + return true + } + return false +} + +func (rw *RWCancel) ReadyRead() bool { + closeFd := int(rw.closingReader.Fd()) + fdset := fdSet{} + fdset.set(rw.fd) + fdset.set(closeFd) + err := unixSelect(max(rw.fd, closeFd)+1, &fdset.fdset, nil, nil, nil) + if err != nil { + return false + } + if fdset.check(closeFd) { + return false + } + return fdset.check(rw.fd) +} + +func (rw *RWCancel) ReadyWrite() bool { + closeFd := int(rw.closingReader.Fd()) + fdset := fdSet{} + fdset.set(rw.fd) + fdset.set(closeFd) + err := unixSelect(max(rw.fd, closeFd)+1, nil, &fdset.fdset, nil, nil) + if err != nil { + return false + } + if fdset.check(closeFd) { + return false + } + return fdset.check(rw.fd) +} + +func (rw *RWCancel) Read(p []byte) (n int, err error) { + for { + n, err := unix.Read(rw.fd, p) + if err == nil || !ErrorIsEAGAIN(err) { + return n, err + } + if !rw.ReadyRead() { + return 0, errors.New("fd closed") + } + } +} + +func (rw *RWCancel) Write(p []byte) (n int, err error) { + for { + n, err := unix.Write(rw.fd, p) + if err == nil || !ErrorIsEAGAIN(err) { + return n, err + } + if !rw.ReadyWrite() { + return 0, errors.New("fd closed") + } + } +} + +func (rw *RWCancel) Cancel() (err error) { + _, err = rw.closingWriter.Write([]byte{0}) + return +} diff --git a/rwcancel/rwcancel_unix.go b/rwcancel/rwcancel_unix.go deleted file mode 100644 index 739a8c3..0000000 --- a/rwcancel/rwcancel_unix.go +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. - */ - -package rwcancel - -import ( - "errors" - "golang.org/x/sys/unix" - "os" - "syscall" -) - -type RWCancel struct { - fd int - closingReader *os.File - closingWriter *os.File -} - -type fdSet struct { - fdset unix.FdSet -} - -func (fdset *fdSet) set(i int) { - bits := 32 << (^uint(0) >> 63) - fdset.fdset.Bits[i/bits] |= 1 << uint(i%bits) -} - -func (fdset *fdSet) check(i int) bool { - bits := 32 << (^uint(0) >> 63) - return (fdset.fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0 -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} - -func NewRWCancel(fd int) (*RWCancel, error) { - err := unix.SetNonblock(fd, true) - if err != nil { - return nil, err - } - rwcancel := RWCancel{fd: fd} - - rwcancel.closingReader, rwcancel.closingWriter, err = os.Pipe() - if err != nil { - return nil, err - } - - return &rwcancel, nil -} - -/* https://golang.org/src/crypto/rand/eagain.go */ -func ErrorIsEAGAIN(err error) bool { - if pe, ok := err.(*os.PathError); ok { - if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EAGAIN { - return true - } - } - if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN { - return true - } - return false -} - -func (rw *RWCancel) ReadyRead() bool { - closeFd := int(rw.closingReader.Fd()) - fdset := fdSet{} - fdset.set(rw.fd) - fdset.set(closeFd) - err := unixSelect(max(rw.fd, closeFd)+1, &fdset.fdset, nil, nil, nil) - if err != nil { - return false - } - if fdset.check(closeFd) { - return false - } - return fdset.check(rw.fd) -} - -func (rw *RWCancel) ReadyWrite() bool { - closeFd := int(rw.closingReader.Fd()) - fdset := fdSet{} - fdset.set(rw.fd) - fdset.set(closeFd) - err := unixSelect(max(rw.fd, closeFd)+1, nil, &fdset.fdset, nil, nil) - if err != nil { - return false - } - if fdset.check(closeFd) { - return false - } - return fdset.check(rw.fd) -} - -func (rw *RWCancel) Read(p []byte) (n int, err error) { - for { - n, err := unix.Read(rw.fd, p) - if err == nil || !ErrorIsEAGAIN(err) { - return n, err - } - if !rw.ReadyRead() { - return 0, errors.New("fd closed") - } - } -} - -func (rw *RWCancel) Write(p []byte) (n int, err error) { - for { - n, err := unix.Write(rw.fd, p) - if err == nil || !ErrorIsEAGAIN(err) { - return n, err - } - if !rw.ReadyWrite() { - return 0, errors.New("fd closed") - } - } -} - -func (rw *RWCancel) Cancel() (err error) { - _, err = rw.closingWriter.Write([]byte{0}) - return -} diff --git a/rwcancel/select_darwin.go b/rwcancel/select_darwin.go deleted file mode 100644 index d14edc8..0000000 --- a/rwcancel/select_darwin.go +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. - */ - -package rwcancel - -import "golang.org/x/sys/unix" - -func unixSelect(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout *unix.Timeval) error { - return unix.Select(nfd, r, w, e, timeout) -} diff --git a/rwcancel/select_default.go b/rwcancel/select_default.go new file mode 100644 index 0000000..302a618 --- /dev/null +++ b/rwcancel/select_default.go @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. + */ + +// +build !linux + +package rwcancel + +import "golang.org/x/sys/unix" + +func unixSelect(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout *unix.Timeval) error { + return unix.Select(nfd, r, w, e, timeout) +} -- cgit v1.2.3