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:
angri
2017-09-15 19:27:16 +03:00
committed by Cristian Maglie
parent 1131ba52fd
commit 1b760a82af

View File

@@ -13,6 +13,7 @@ import (
"regexp"
"strings"
"sync"
"sync/atomic"
"unsafe"
"go.bug.st/serial/unixutils"
@@ -24,16 +25,18 @@ type unixPort struct {
closeLock sync.RWMutex
closeSignal *unixutils.Pipe
opened bool
opened uint32
}
func (port *unixPort) Close() error {
if !atomic.CompareAndSwapUint32(&port.opened, 1, 0) {
return &PortError{code: PortClosed}
}
// Close port
port.releaseExclusiveAccess()
if err := unix.Close(port.handle); err != nil {
return err
}
port.opened = false
if port.closeSignal != nil {
// 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) {
port.closeLock.RLock()
defer port.closeLock.RUnlock()
if !port.opened {
if atomic.LoadUint32(&port.opened) != 1 {
return 0, &PortError{code: PortClosed}
}
@@ -169,7 +172,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) {
}
port := &unixPort{
handle: h,
opened: true,
opened: 1,
}
// Setup serial port