unix: Fixed race condition while closing serial port. Added PortClosed error

This commit is contained in:
Cristian Maglie
2016-11-14 00:31:01 +01:00
parent 6eedab17b4
commit d50de5cbaf
2 changed files with 14 additions and 1 deletions

View File

@@ -100,6 +100,8 @@ const (
InvalidStopBits InvalidStopBits
// 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
) )
// EncodedErrorString returns a string explaining the error code // EncodedErrorString returns a string explaining the error code
@@ -123,6 +125,8 @@ func (e PortError) EncodedErrorString() string {
return "Port stop bits invalid or not supported" return "Port stop bits invalid or not supported"
case ErrorEnumeratingPorts: case ErrorEnumeratingPorts:
return "Could not enumerate serial ports" return "Could not enumerate serial ports"
case PortClosed:
return "Port has been closed"
default: default:
return "Other error" return "Other error"
} }

View File

@@ -24,6 +24,7 @@ type unixPort struct {
closeLock sync.RWMutex closeLock sync.RWMutex
closeSignal *unixutils.Pipe closeSignal *unixutils.Pipe
opened bool
} }
func (port *unixPort) Close() error { func (port *unixPort) Close() error {
@@ -32,6 +33,7 @@ func (port *unixPort) Close() error {
if err := syscall.Close(port.handle); err != nil { if err := syscall.Close(port.handle); err != nil {
return err return err
} }
port.opened = false
if port.closeSignal != nil { if port.closeSignal != nil {
// Send close signal to all pending reads (if any) // Send close signal to all pending reads (if any)
@@ -52,12 +54,18 @@ func (port *unixPort) Close() error {
func (port *unixPort) Read(p []byte) (n int, err error) { func (port *unixPort) Read(p []byte) (n int, err error) {
port.closeLock.RLock() port.closeLock.RLock()
defer port.closeLock.RUnlock() defer port.closeLock.RUnlock()
if !port.opened {
return 0, &PortError{code: PortClosed}
}
fds := unixutils.NewFDSet(port.handle, port.closeSignal.ReadFD()) fds := unixutils.NewFDSet(port.handle, port.closeSignal.ReadFD())
res, err := unixutils.Select(fds, nil, fds, -1) res, err := unixutils.Select(fds, nil, fds, -1)
if err != nil { if err != nil {
return 0, err return 0, err
} }
if res.IsReadable(port.closeSignal.ReadFD()) {
return 0, &PortError{code: PortClosed}
}
return syscall.Read(port.handle, p) return syscall.Read(port.handle, p)
} }
@@ -98,6 +106,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) {
} }
port := &unixPort{ port := &unixPort{
handle: h, handle: h,
opened: true,
} }
// Setup serial port // Setup serial port
@@ -127,7 +136,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) {
port.acquireExclusiveAccess() port.acquireExclusiveAccess()
// This pipe is used as a signal to cancel blocking Read or Write // This pipe is used as a signal to cancel blocking Read
pipe := &unixutils.Pipe{} pipe := &unixutils.Pipe{}
if err := pipe.Open(); err != nil { if err := pipe.Open(); err != nil {
port.Close() port.Close()