darwin: make Adapter.Connect thread-safe

This change allows multiple concurrent goroutines to call
`Adapter.Connect` without racing.

Fixes #57
This commit is contained in:
Erik Price 2021-03-16 16:49:19 -07:00
parent 28f9f4e69e
commit 9471cedb5b
2 changed files with 29 additions and 5 deletions

@ -2,6 +2,7 @@ package bluetooth
import (
"errors"
"sync"
"time"
"github.com/JuulLabs-OSS/cbgo"
@ -9,6 +10,8 @@ import (
// Adapter is a connection to BLE devices.
type Adapter struct {
sync.Mutex
cmd *centralManagerDelegate
pmd *peripheralManagerDelegate
@ -18,7 +21,7 @@ type Adapter struct {
peripheralFoundHandler func(*Adapter, ScanResult)
scanChan chan error
poweredChan chan error
connectChan chan cbgo.Peripheral
connectChan map[string](chan cbgo.Peripheral)
connectHandler func(device Addresser, connected bool)
}
@ -29,7 +32,8 @@ type Adapter struct {
var DefaultAdapter = &Adapter{
cm: cbgo.NewCentralManager(nil),
pm: cbgo.NewPeripheralManager(nil),
connectChan: make(chan cbgo.Peripheral),
connectChan: make(map[string]chan cbgo.Peripheral),
connectHandler: func(device Addresser, connected bool) {
return
},
@ -97,8 +101,14 @@ func (cmd *centralManagerDelegate) DidDiscoverPeripheral(cmgr cbgo.CentralManage
// DidConnectPeripheral when peripheral is connected.
func (cmd *centralManagerDelegate) DidConnectPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral) {
// Unblock now that we're connected.
cmd.a.connectChan <- prph
cmd.a.Lock()
defer cmd.a.Unlock()
// Ignore this peripheral if we don't have a chan set up for it.
if ch, ok := cmd.a.connectChan[prph.Identifier().String()]; ok {
// Unblock now that we're connected.
ch <- prph
}
}
// makeScanResult creates a ScanResult when peripheral is found.

@ -105,11 +105,25 @@ func (a *Adapter) Connect(address Addresser, params ConnectionParams) (*Device,
if len(prphs) == 0 {
return nil, fmt.Errorf("Connect failed: no peer with address: %s", adr.UUID.String())
}
prphCh := make(chan cbgo.Peripheral)
id := prphs[0].Identifier().String()
a.Lock()
a.connectChan[id] = prphCh
a.Unlock()
defer func() {
a.Lock()
defer a.Unlock()
delete(a.connectChan, id)
}()
a.cm.Connect(prphs[0], nil)
// wait on channel for connect
select {
case p := <-a.connectChan:
case p := <-prphCh:
d := &Device{
cm: a.cm,
prph: p,