allow Close() to be called from other goroutine than Read
The only way to interrupt running Read() method of io.Reader is to call Close from another goroutine. What happens is that go runtime builtin race detector complains about unsynchronised access to "opened" member, which is set in Close and checked in Read. It doesn't seem to be dangerous, but 1) you never know, and 2) it makes your life more difficult when you need to debug some other data races in a complex project that uses serial. Proposed fix changes simple access to "opened" to atomic operations. It's also supposed to fix #38
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"go.bug.st/serial/unixutils"
|
"go.bug.st/serial/unixutils"
|
||||||
@@ -24,16 +25,18 @@ type unixPort struct {
|
|||||||
|
|
||||||
closeLock sync.RWMutex
|
closeLock sync.RWMutex
|
||||||
closeSignal *unixutils.Pipe
|
closeSignal *unixutils.Pipe
|
||||||
opened bool
|
opened uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (port *unixPort) Close() error {
|
func (port *unixPort) Close() error {
|
||||||
|
if !atomic.CompareAndSwapUint32(&port.opened, 1, 0) {
|
||||||
|
return &PortError{code: PortClosed}
|
||||||
|
}
|
||||||
// Close port
|
// Close port
|
||||||
port.releaseExclusiveAccess()
|
port.releaseExclusiveAccess()
|
||||||
if err := unix.Close(port.handle); err != nil {
|
if err := unix.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)
|
||||||
@@ -54,7 +57,7 @@ func (port *unixPort) Close() error {
|
|||||||
func (port *unixPort) Read(p []byte) (int, error) {
|
func (port *unixPort) Read(p []byte) (int, error) {
|
||||||
port.closeLock.RLock()
|
port.closeLock.RLock()
|
||||||
defer port.closeLock.RUnlock()
|
defer port.closeLock.RUnlock()
|
||||||
if !port.opened {
|
if atomic.LoadUint32(&port.opened) != 1 {
|
||||||
return 0, &PortError{code: PortClosed}
|
return 0, &PortError{code: PortClosed}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +172,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) {
|
|||||||
}
|
}
|
||||||
port := &unixPort{
|
port := &unixPort{
|
||||||
handle: h,
|
handle: h,
|
||||||
opened: true,
|
opened: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup serial port
|
// Setup serial port
|
||||||
|
|||||||
Reference in New Issue
Block a user