Skip to content

Commit 5d01164

Browse files
committed
Add timeout handling for messages / commands on Linux to avoid deadlocks
1 parent c4e2179 commit 5d01164

File tree

5 files changed

+56
-21
lines changed

5 files changed

+56
-21
lines changed

device_linux.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package gatt
33
import (
44
"encoding/binary"
55
"net"
6+
"time"
67

78
"github.com/fako1024/gatt/linux"
89
"github.com/fako1024/gatt/linux/cmd"
@@ -18,9 +19,10 @@ type device struct {
1819
svcs []*Service
1920
attrs *attrRange
2021

21-
devID int
22-
chkLE bool
23-
maxConn int
22+
devID int
23+
chkLE bool
24+
maxConn int
25+
msgTimeout time.Duration
2426

2527
advData *cmd.LESetAdvertisingData
2628
scanResp *cmd.LESetScanResponseData
@@ -30,9 +32,10 @@ type device struct {
3032

3133
func NewDevice(opts ...Option) (Device, error) {
3234
d := &device{
33-
maxConn: 1, // Support 1 connection at a time.
34-
devID: -1, // Find an available HCI device.
35-
chkLE: true, // Check if the device supports LE.
35+
maxConn: 1, // Support 1 connection at a time.
36+
devID: -1, // Find an available HCI device.
37+
chkLE: true, // Check if the device supports LE.
38+
msgTimeout: 999999 * time.Hour, // Set a very high (practically infinite) default timeout
3639

3740
advParam: &cmd.LESetAdvertisingParameters{
3841
AdvertisingIntervalMin: 0x800, // [0x0800]: 0.625 ms * 0x0800 = 1280.0 ms

option_linux.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package gatt
33
import (
44
"errors"
55
"io"
6+
"time"
67

78
"github.com/fako1024/gatt/linux/cmd"
89
)
@@ -85,3 +86,13 @@ func LnxSendHCIRawCommand(c cmd.CmdParam, rsp io.Writer) Option {
8586
return err
8687
}
8788
}
89+
90+
// LnxMsgTimeout is an optional parameter.
91+
// If set, it overrides the default (basically infinite) timeout for command / message handling
92+
// This option can be used with NewDevice or Option on Linux implementation.
93+
func LnxMsgTimeout(timeout time.Duration) Option {
94+
return func(d Device) error {
95+
d.(*device).msgTimeout = timeout
96+
return nil
97+
}
98+
}

option_linux_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package gatt
22

33
import (
44
"bytes"
5+
"time"
56

67
"github.com/fako1024/gatt/linux/cmd"
78
)
@@ -14,6 +15,10 @@ func ExampleLnxMaxConnections() {
1415
NewDevice(LnxMaxConnections(1)) // Can only be used with NewDevice.
1516
}
1617

18+
func ExampleLnxMsgTimeout() {
19+
NewDevice(LnxMsgTimeout(time.Second)) // Can only be used with NewDevice.
20+
}
21+
1722
func ExampleLnxSetAdvertisingEnable() {
1823
d, _ := NewDevice()
1924
d.Option(LnxSetAdvertisingEnable(true)) // Can only be used with Option.

peripheral.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,5 @@ func (s *subscriber) fn(h uint16) subscribefn {
100100
var (
101101
ErrInvalidLength = errors.New("invalid length")
102102
ErrDisconnected = errors.New("disconnected mid request")
103+
ErrTimeout = errors.New("message timeout")
103104
)

peripheral_linux.go

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io"
99
"net"
1010
"strings"
11+
"time"
1112

1213
"github.com/fako1024/gatt/linux"
1314
)
@@ -61,7 +62,7 @@ func (p *peripheral) DiscoverServices(s []UUID) ([]*Service, error) {
6162
binary.LittleEndian.PutUint16(b[3:5], 0xFFFF)
6263
binary.LittleEndian.PutUint16(b[5:7], 0x2800)
6364

64-
b = p.sendReq(op, b)
65+
b, _ = p.sendReq(op, b)
6566
if b == nil {
6667
return nil, ErrDisconnected
6768
}
@@ -112,7 +113,7 @@ func (p *peripheral) DiscoverCharacteristics(cs []UUID, s *Service) ([]*Characte
112113
binary.LittleEndian.PutUint16(b[3:5], s.endh)
113114
binary.LittleEndian.PutUint16(b[5:7], 0x2803)
114115

115-
b = p.sendReq(op, b)
116+
b, _ = p.sendReq(op, b)
116117
if b == nil {
117118
return nil, ErrDisconnected
118119
}
@@ -175,7 +176,7 @@ func (p *peripheral) DiscoverDescriptors(ds []UUID, c *Characteristic) ([]*Descr
175176
binary.LittleEndian.PutUint16(b[1:3], start)
176177
binary.LittleEndian.PutUint16(b[3:5], c.endh)
177178

178-
b = p.sendReq(op, b)
179+
b, _ = p.sendReq(op, b)
179180
if b == nil {
180181
return nil, ErrDisconnected
181182
}
@@ -217,7 +218,7 @@ func (p *peripheral) ReadCharacteristic(c *Characteristic) ([]byte, error) {
217218
b[0] = op
218219
binary.LittleEndian.PutUint16(b[1:3], c.vh)
219220

220-
b = p.sendReq(op, b)
221+
b, _ = p.sendReq(op, b)
221222
if b == nil {
222223
return nil, ErrDisconnected
223224
}
@@ -248,7 +249,7 @@ func (p *peripheral) ReadLongCharacteristic(c *Characteristic) ([]byte, error) {
248249
binary.LittleEndian.PutUint16(b[1:3], c.vh)
249250
binary.LittleEndian.PutUint16(b[3:5], off)
250251

251-
b = p.sendReq(op, b)
252+
b, _ = p.sendReq(op, b)
252253
if b == nil {
253254
return nil, ErrDisconnected
254255
}
@@ -279,7 +280,7 @@ func (p *peripheral) WriteCharacteristic(c *Characteristic, value []byte, noRsp
279280
p.sendCmd(op, b)
280281
return nil
281282
}
282-
b = p.sendReq(op, b)
283+
b, _ = p.sendReq(op, b)
283284
if b == nil {
284285
return ErrDisconnected
285286
}
@@ -294,7 +295,7 @@ func (p *peripheral) ReadDescriptor(d *Descriptor) ([]byte, error) {
294295
b[0] = op
295296
binary.LittleEndian.PutUint16(b[1:3], d.h)
296297

297-
b = p.sendReq(op, b)
298+
b, _ = p.sendReq(op, b)
298299
if b == nil {
299300
return nil, ErrDisconnected
300301
}
@@ -310,7 +311,7 @@ func (p *peripheral) WriteDescriptor(d *Descriptor, value []byte) error {
310311
binary.LittleEndian.PutUint16(b[1:3], d.h)
311312
copy(b[3:], value)
312313

313-
b = p.sendReq(op, b)
314+
b, _ = p.sendReq(op, b)
314315
if b == nil {
315316
return ErrDisconnected
316317
}
@@ -335,7 +336,7 @@ func (p *peripheral) setNotifyValue(c *Characteristic, flag uint16,
335336
binary.LittleEndian.PutUint16(b[1:3], c.cccd.h)
336337
binary.LittleEndian.PutUint16(b[3:5], ccc)
337338

338-
b = p.sendReq(op, b)
339+
b, _ = p.sendReq(op, b)
339340
if b == nil {
340341
return ErrDisconnected
341342
}
@@ -378,14 +379,28 @@ type message struct {
378379
rspc chan []byte
379380
}
380381

381-
func (p *peripheral) sendCmd(op byte, b []byte) {
382-
p.reqc <- message{op: op, b: b}
382+
func (p *peripheral) sendCmd(op byte, b []byte) error {
383+
select {
384+
case p.reqc <- message{op: op, b: b}:
385+
return nil
386+
case <-time.After(p.d.msgTimeout):
387+
return ErrTimeout
388+
}
383389
}
384390

385-
func (p *peripheral) sendReq(op byte, b []byte) []byte {
391+
func (p *peripheral) sendReq(op byte, b []byte) ([]byte, error) {
386392
m := message{op: op, b: b, rspc: make(chan []byte)}
387-
p.reqc <- m
388-
return <-m.rspc
393+
select {
394+
case p.reqc <- m:
395+
select {
396+
case res := <-m.rspc:
397+
return res, nil
398+
case <-time.After(p.d.msgTimeout):
399+
return nil, ErrTimeout
400+
}
401+
case <-time.After(p.d.msgTimeout):
402+
return nil, ErrTimeout
403+
}
389404
}
390405

391406
func (p *peripheral) loop() {
@@ -463,7 +478,7 @@ func (p *peripheral) SetMTU(mtu uint16) error {
463478
b[0] = op
464479
binary.LittleEndian.PutUint16(b[1:3], uint16(mtu))
465480

466-
b = p.sendReq(op, b)
481+
b, _ = p.sendReq(op, b)
467482
if b == nil {
468483
return ErrDisconnected
469484
}

0 commit comments

Comments
 (0)