SetReadTimeout: unix implementation

This commit is contained in:
Cristian Maglie
2020-05-10 23:42:16 +02:00
parent a63b28875f
commit 2cb14f049c
2 changed files with 40 additions and 3 deletions

View File

@@ -6,6 +6,8 @@
package serial package serial
import "time"
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go //go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go
// Port is the interface for a serial Port // Port is the interface for a serial Port
@@ -40,10 +42,17 @@ type Port interface {
// modem status bits for the serial port (CTS, DSR, etc...) // modem status bits for the serial port (CTS, DSR, etc...)
GetModemStatusBits() (*ModemStatusBits, error) GetModemStatusBits() (*ModemStatusBits, error)
// SetReadTimeout sets the timeout for the Read operation or use serial.NoTimeout
// to disable read timeout.
SetReadTimeout(t time.Duration) error
// Close the serial port // Close the serial port
Close() error Close() error
} }
// NoTimeout should be used as a parameter to SetReadTimeout to disable timeout.
var NoTimeout time.Duration = -1
// ModemStatusBits contains all the modem status bits for a serial port (CTS, DSR, etc...). // ModemStatusBits contains all the modem status bits for a serial port (CTS, DSR, etc...).
// It can be retrieved with the Port.GetModemStatusBits() method. // It can be retrieved with the Port.GetModemStatusBits() method.
type ModemStatusBits struct { type ModemStatusBits struct {
@@ -125,6 +134,8 @@ const (
InvalidParity InvalidParity
// InvalidStopBits the selected number of stop bits is not valid or not supported // InvalidStopBits the selected number of stop bits is not valid or not supported
InvalidStopBits InvalidStopBits
// InvalidTimeoutValue the timeout value is not valid or not supported
InvalidTimeoutValue
// ErrorEnumeratingPorts an error occurred while listing serial port // ErrorEnumeratingPorts an error occurred while listing serial port
ErrorEnumeratingPorts ErrorEnumeratingPorts
// PortClosed the port has been closed while the operation is in progress // PortClosed the port has been closed while the operation is in progress
@@ -152,6 +163,8 @@ func (e PortError) EncodedErrorString() string {
return "Port parity invalid or not supported" return "Port parity invalid or not supported"
case InvalidStopBits: case InvalidStopBits:
return "Port stop bits invalid or not supported" return "Port stop bits invalid or not supported"
case InvalidTimeoutValue:
return "Timeout value invalid or not supported"
case ErrorEnumeratingPorts: case ErrorEnumeratingPorts:
return "Could not enumerate serial ports" return "Could not enumerate serial ports"
case PortClosed: case PortClosed:

View File

@@ -14,6 +14,7 @@ import (
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time"
"go.bug.st/serial/unixutils" "go.bug.st/serial/unixutils"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@@ -22,6 +23,7 @@ import (
type unixPort struct { type unixPort struct {
handle int handle int
readTimeout time.Duration
closeLock sync.RWMutex closeLock sync.RWMutex
closeSignal *unixutils.Pipe closeSignal *unixutils.Pipe
opened uint32 opened uint32
@@ -61,9 +63,18 @@ func (port *unixPort) Read(p []byte) (int, error) {
return 0, &PortError{code: PortClosed} return 0, &PortError{code: PortClosed}
} }
var deadline time.Time
if port.readTimeout != NoTimeout {
deadline = time.Now().Add(port.readTimeout)
}
fds := unixutils.NewFDSet(port.handle, port.closeSignal.ReadFD()) fds := unixutils.NewFDSet(port.handle, port.closeSignal.ReadFD())
for { for {
res, err := unixutils.Select(fds, nil, fds, -1) timeout := time.Duration(-1)
if port.readTimeout != NoTimeout {
timeout = deadline.Sub(time.Now())
}
res, err := unixutils.Select(fds, nil, fds, timeout)
if err == unix.EINTR { if err == unix.EINTR {
continue continue
} }
@@ -73,6 +84,10 @@ func (port *unixPort) Read(p []byte) (int, error) {
if res.IsReadable(port.closeSignal.ReadFD()) { if res.IsReadable(port.closeSignal.ReadFD()) {
return 0, &PortError{code: PortClosed} return 0, &PortError{code: PortClosed}
} }
if !res.IsReadable(port.handle) {
// Timeout happened
return 0, nil
}
n, err := unix.Read(port.handle, p) n, err := unix.Read(port.handle, p)
if err == unix.EINTR { if err == unix.EINTR {
continue continue
@@ -152,6 +167,14 @@ func (port *unixPort) SetRTS(rts bool) error {
return port.setModemBitsStatus(status) return port.setModemBitsStatus(status)
} }
func (port *unixPort) SetReadTimeout(timeout time.Duration) error {
if timeout < 0 && timeout != NoTimeout {
return &PortError{code: InvalidTimeoutValue}
}
port.readTimeout = timeout
return nil
}
func (port *unixPort) GetModemStatusBits() (*ModemStatusBits, error) { func (port *unixPort) GetModemStatusBits() (*ModemStatusBits, error) {
status, err := port.getModemBitsStatus() status, err := port.getModemBitsStatus()
if err != nil { if err != nil {
@@ -179,6 +202,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) {
port := &unixPort{ port := &unixPort{
handle: h, handle: h,
opened: 1, opened: 1,
readTimeout: NoTimeout,
} }
// Setup serial port // Setup serial port