Skip to content

Commit 1c5b750

Browse files
authored
Merge pull request #17 from andig/fix/races
Fix races in accessing connection and builder
2 parents 744e0ab + 054695c commit 1c5b750

File tree

3 files changed

+48
-36
lines changed

3 files changed

+48
-36
lines changed

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: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,29 @@ 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
if conn.conn != nil { // there might be dead connection in the cache, e.g. when connection was disconnected
@@ -30,10 +34,9 @@ func NewConnection(host string, cache time.Duration) (*Connection, error) {
3034
}
3135

3236
conn := &Connection{
33-
host: host,
34-
builder: NewDatagramBuilder(),
35-
parser: NewDatagramParser(),
36-
cache: NewCache(cache),
37+
host: host,
38+
parser: NewDatagramParser(),
39+
cache: NewCache(cache),
3740
}
3841

3942
if err := conn.connect(); err != nil {
@@ -59,7 +62,13 @@ func (c *Connection) Close() {
5962
}
6063

6164
// Sends the given RCT datagram via the connection
62-
func (c *Connection) Send(rdb *DatagramBuilder) (n int, err error) {
65+
func (c *Connection) Send(rdb *DatagramBuilder) (int, error) {
66+
c.mu.Lock()
67+
defer c.mu.Unlock()
68+
return c.send(rdb)
69+
}
70+
71+
func (c *Connection) send(rdb *DatagramBuilder) (int, error) {
6372
// ensure active connection
6473
if c.conn == nil {
6574
if err := c.connect(); err != nil {
@@ -68,8 +77,7 @@ func (c *Connection) Send(rdb *DatagramBuilder) (n int, err error) {
6877
}
6978

7079
// fmt.Printf("Sending %v\n", c.Builder.String())
71-
n, err = c.conn.Write(rdb.Bytes())
72-
80+
n, err := c.conn.Write(rdb.Bytes())
7381
// single retry on error when sending
7482
if err != nil {
7583
// fmt.Printf("Read %d bytes error %v\n", n, err)
@@ -85,7 +93,14 @@ func (c *Connection) Send(rdb *DatagramBuilder) (n int, err error) {
8593
}
8694

8795
// Receives an RCT response via the connection
88-
func (c *Connection) Receive() (dg *Datagram, err error) {
96+
func (c *Connection) Receive() (*Datagram, error) {
97+
c.mu.Lock()
98+
defer c.mu.Unlock()
99+
return c.receive()
100+
}
101+
102+
// Receives an RCT response via the connection
103+
func (c *Connection) receive() (dg *Datagram, err error) {
89104
// ensure active connection
90105
if c.conn == nil {
91106
if err := c.connect(); err != nil {
@@ -108,17 +123,21 @@ func (c *Connection) Receive() (dg *Datagram, err error) {
108123
}
109124

110125
// Queries the given identifier on the RCT device, returning its value as a datagram
111-
func (c *Connection) Query(id Identifier) (dg *Datagram, err error) {
126+
func (c *Connection) Query(id Identifier) (*Datagram, error) {
127+
c.mu.Lock()
128+
defer c.mu.Unlock()
129+
112130
if dg, ok := c.cache.Get(id); ok {
113131
return dg, nil
114132
}
115-
c.builder.Build(&Datagram{Read, id, nil})
116-
_, err = c.Send(c.builder)
117-
if err != nil {
133+
134+
builder := NewDatagramBuilder()
135+
builder.Build(&Datagram{Read, id, nil})
136+
if _, err := c.send(builder); err != nil {
118137
return nil, err
119138
}
120139

121-
dg, err = c.Receive()
140+
dg, err := c.receive()
122141
if err != nil {
123142
return nil, err
124143
}

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)