From 1b760a82af99fbfe5a084c31231d4fdb0ef8df9f Mon Sep 17 00:00:00 2001 From: angri Date: Fri, 15 Sep 2017 19:27:16 +0300 Subject: [PATCH] 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 --- serial_unix.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index c796c30..c33bca8 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -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