Skip to content

Commit cf63949

Browse files
erikdeadprogram
authored andcommitted
darwin: make Adapter.Connect thread-safe
This change allows multiple concurrent goroutines to call `Adapter.Connect` without racing. Fixes #57
1 parent bb87677 commit cf63949

File tree

2 files changed

+29
-7
lines changed

2 files changed

+29
-7
lines changed

adapter_darwin.go

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

33
import (
44
"errors"
5+
"sync"
56
"time"
67

78
"github.com/JuulLabs-OSS/cbgo"
@@ -18,7 +19,10 @@ type Adapter struct {
1819
peripheralFoundHandler func(*Adapter, ScanResult)
1920
scanChan chan error
2021
poweredChan chan error
21-
connectChan chan cbgo.Peripheral
22+
23+
// connectMap is a mapping of peripheralId -> chan cbgo.Peripheral,
24+
// used to allow multiple callers to call Connect concurrently.
25+
connectMap sync.Map
2226

2327
connectHandler func(device Addresser, connected bool)
2428
}
@@ -27,9 +31,10 @@ type Adapter struct {
2731
//
2832
// Make sure to call Enable() before using it to initialize the adapter.
2933
var DefaultAdapter = &Adapter{
30-
cm: cbgo.NewCentralManager(nil),
31-
pm: cbgo.NewPeripheralManager(nil),
32-
connectChan: make(chan cbgo.Peripheral),
34+
cm: cbgo.NewCentralManager(nil),
35+
pm: cbgo.NewPeripheralManager(nil),
36+
connectMap: sync.Map{},
37+
3338
connectHandler: func(device Addresser, connected bool) {
3439
return
3540
},
@@ -97,8 +102,18 @@ func (cmd *centralManagerDelegate) DidDiscoverPeripheral(cmgr cbgo.CentralManage
97102

98103
// DidConnectPeripheral when peripheral is connected.
99104
func (cmd *centralManagerDelegate) DidConnectPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral) {
100-
// Unblock now that we're connected.
101-
cmd.a.connectChan <- prph
105+
id := prph.Identifier().String()
106+
107+
// Check if we have a chan allocated for this peripheral, and remove it
108+
// from the map if so (it's single-use, will be garbage collected after
109+
// receiver receives the peripheral).
110+
//
111+
// If we don't have a chan allocated, the receiving side timed out, so
112+
// ignore this connection.
113+
if ch, ok := cmd.a.connectMap.LoadAndDelete(id); ok {
114+
// Unblock now that we're connected.
115+
ch.(chan cbgo.Peripheral) <- prph
116+
}
102117
}
103118

104119
// makeScanResult creates a ScanResult when peripheral is found.

gap_darwin.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,18 @@ func (a *Adapter) Connect(address Addresser, params ConnectionParams) (*Device,
105105
if len(prphs) == 0 {
106106
return nil, fmt.Errorf("Connect failed: no peer with address: %s", adr.UUID.String())
107107
}
108+
109+
id := prphs[0].Identifier().String()
110+
prphCh := make(chan cbgo.Peripheral)
111+
112+
a.connectMap.Store(id, prphCh)
113+
defer a.connectMap.Delete(id)
114+
108115
a.cm.Connect(prphs[0], nil)
109116

110117
// wait on channel for connect
111118
select {
112-
case p := <-a.connectChan:
119+
case p := <-prphCh:
113120
d := &Device{
114121
cm: a.cm,
115122
prph: p,

0 commit comments

Comments
 (0)