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:
parent
bb8767730c
commit
cf63949412
2 changed files with 29 additions and 7 deletions
|
@ -2,6 +2,7 @@ package bluetooth
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/JuulLabs-OSS/cbgo"
|
||||
|
@ -18,7 +19,10 @@ type Adapter struct {
|
|||
peripheralFoundHandler func(*Adapter, ScanResult)
|
||||
scanChan chan error
|
||||
poweredChan chan error
|
||||
connectChan chan cbgo.Peripheral
|
||||
|
||||
// connectMap is a mapping of peripheralId -> chan cbgo.Peripheral,
|
||||
// used to allow multiple callers to call Connect concurrently.
|
||||
connectMap sync.Map
|
||||
|
||||
connectHandler func(device Addresser, connected bool)
|
||||
}
|
||||
|
@ -27,9 +31,10 @@ type Adapter struct {
|
|||
//
|
||||
// Make sure to call Enable() before using it to initialize the adapter.
|
||||
var DefaultAdapter = &Adapter{
|
||||
cm: cbgo.NewCentralManager(nil),
|
||||
pm: cbgo.NewPeripheralManager(nil),
|
||||
connectChan: make(chan cbgo.Peripheral),
|
||||
cm: cbgo.NewCentralManager(nil),
|
||||
pm: cbgo.NewPeripheralManager(nil),
|
||||
connectMap: sync.Map{},
|
||||
|
||||
connectHandler: func(device Addresser, connected bool) {
|
||||
return
|
||||
},
|
||||
|
@ -97,8 +102,18 @@ 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
|
||||
id := prph.Identifier().String()
|
||||
|
||||
// Check if we have a chan allocated for this peripheral, and remove it
|
||||
// from the map if so (it's single-use, will be garbage collected after
|
||||
// receiver receives the peripheral).
|
||||
//
|
||||
// If we don't have a chan allocated, the receiving side timed out, so
|
||||
// ignore this connection.
|
||||
if ch, ok := cmd.a.connectMap.LoadAndDelete(id); ok {
|
||||
// Unblock now that we're connected.
|
||||
ch.(chan cbgo.Peripheral) <- prph
|
||||
}
|
||||
}
|
||||
|
||||
// makeScanResult creates a ScanResult when peripheral is found.
|
||||
|
|
|
@ -105,11 +105,18 @@ 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())
|
||||
}
|
||||
|
||||
id := prphs[0].Identifier().String()
|
||||
prphCh := make(chan cbgo.Peripheral)
|
||||
|
||||
a.connectMap.Store(id, prphCh)
|
||||
defer a.connectMap.Delete(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,
|
||||
|
|
Loading…
Reference in a new issue