Skip to content

Commit 05f0899

Browse files
committed
Fix races in accessing connection and builder
1 parent 03eacb9 commit 05f0899

3 files changed

Lines changed: 39 additions & 30 deletions

File tree

‎build.go‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ type DatagramBuilder struct {
1414
// Returns a new DatagramBuilder
1515
func NewDatagramBuilder() (b *DatagramBuilder) {
1616
return &DatagramBuilder{
17-
buffer: bytes.Buffer{},
18-
crc: NewCRC(),
17+
crc: NewCRC(),
1918
}
2019
}
2120

@@ -69,7 +68,7 @@ func (r *DatagramBuilder) Bytes() []byte {
6968

7069
// Converts the datagram into a string representation for printing
7170
func (r *DatagramBuilder) String() string {
72-
buf := bytes.Buffer{}
71+
var buf bytes.Buffer
7372
buf.WriteByte(byte('['))
7473
for i, b := range r.buffer.Bytes() {
7574
if i != 0 {

‎connection.go‎

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,38 @@ package rct
33
import (
44
"fmt"
55
"net"
6+
"sync"
67
"time"
78
)
89

9-
// DialTimeout is the default cache for connecting to a RCT device
10-
var DialTimeout = time.Second * 5
10+
var (
11+
// DialTimeout is the default cache for connecting to a RCT device
12+
DialTimeout = time.Second * 5
13+
14+
// Map of active connections
15+
connectionCache = make(map[string]*Connection)
16+
)
1117

1218
// Connection to a RCT device
1319
type Connection struct {
14-
host string
15-
conn net.Conn
16-
builder *DatagramBuilder
17-
parser *DatagramParser
18-
cache *Cache
20+
mu sync.Mutex
21+
host string
22+
conn net.Conn
23+
parser *DatagramParser
24+
cache *Cache
1925
}
2026

21-
// Map of active connections
22-
var connectionCache map[string]*Connection = make(map[string]*Connection)
23-
24-
// Creates a new connection to a RCT device at the given address
27+
// Creates a new connection to a RCT device at the given address.
28+
// Must not be called concurrently.
2529
func NewConnection(host string, cache time.Duration) (*Connection, error) {
2630
if conn, ok := connectionCache[host]; ok {
2731
return conn, nil
2832
}
2933

3034
conn := &Connection{
31-
host: host,
32-
builder: NewDatagramBuilder(),
33-
parser: NewDatagramParser(),
34-
cache: NewCache(cache),
35+
host: host,
36+
parser: NewDatagramParser(),
37+
cache: NewCache(cache),
3538
}
3639

3740
if err := conn.connect(); err != nil {
@@ -57,6 +60,12 @@ func (c *Connection) Close() {
5760

5861
// Sends the given RCT datagram via the connection
5962
func (c *Connection) Send(rdb *DatagramBuilder) (n int, err error) {
63+
c.mu.Lock()
64+
defer c.mu.Unlock()
65+
return c.send(rdb)
66+
}
67+
68+
func (c *Connection) send(rdb *DatagramBuilder) (n int, err error) {
6069
// ensure active connection
6170
if c.conn == nil {
6271
if err := c.connect(); err != nil {
@@ -66,7 +75,6 @@ func (c *Connection) Send(rdb *DatagramBuilder) (n int, err error) {
6675

6776
// fmt.Printf("Sending %v\n", c.Builder.String())
6877
n, err = c.conn.Write(rdb.Bytes())
69-
7078
// single retry on error when sending
7179
if err != nil {
7280
// fmt.Printf("Read %d bytes error %v\n", n, err)
@@ -83,6 +91,9 @@ func (c *Connection) Send(rdb *DatagramBuilder) (n int, err error) {
8391

8492
// Receives an RCT response via the connection
8593
func (c *Connection) Receive() (dg *Datagram, err error) {
94+
c.mu.Lock()
95+
defer c.mu.Unlock()
96+
8697
// ensure active connection
8798
if c.conn == nil {
8899
if err := c.connect(); err != nil {
@@ -106,11 +117,16 @@ func (c *Connection) Receive() (dg *Datagram, err error) {
106117

107118
// Queries the given identifier on the RCT device, returning its value as a datagram
108119
func (c *Connection) Query(id Identifier) (dg *Datagram, err error) {
120+
c.mu.Lock()
121+
defer c.mu.Unlock()
122+
109123
if dg, ok := c.cache.Get(id); ok {
110124
return dg, nil
111125
}
112-
c.builder.Build(&Datagram{Read, id, nil})
113-
_, err = c.Send(c.builder)
126+
127+
builder := NewDatagramBuilder()
128+
builder.Build(&Datagram{Read, id, nil})
129+
_, err = c.send(builder)
114130
if err != nil {
115131
return nil, err
116132
}

‎recoverable.go‎

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
1-
package rct;
1+
package rct
22

33
// Errors caused by a malformed or unexpected packet, which can be potentially be recovered by retrying the transmission
44
type RecoverableError struct {
5-
Err string
5+
Err string
66
}
77

88
// Prints error to string
99
func (e RecoverableError) Error() string {
10-
return e.Err
11-
}
12-
13-
// Returns true if the given error is potentially recoverable
14-
func IsRecoverableError(err error) bool {
15-
_, ok:=err.(RecoverableError)
16-
return ok
10+
return e.Err
1711
}

0 commit comments

Comments
 (0)