Skip to content

Commit d724cd9

Browse files
Seth TerashimaLenart12
Seth Terashima
authored andcommitted
Fix MacOS deadlock during BLE scan
The MacOS BLE Scan() method blocks until all queued instances of the callback function finish. This created a deadlock, since the callback function would try to write a matching advertisement to an unbuffered channel, and the unbuffered channel would not be read until Scan() completed. To fix the problem, this commit makes the channel buffered, allowing the callback function to succeed on the first match. The callback handles subsequent matches by detecting that the scan context has been canceled and returning. This commit also uprevs the BLE package. This was previously impossible because we relied on the device.Connect method, which is broken upstream on MacOS.
1 parent 3c91d3d commit d724cd9

File tree

3 files changed

+22
-9
lines changed

3 files changed

+22
-9
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.23
55
require (
66
github.com/99designs/keyring v1.2.2
77
github.com/cronokirby/saferith v0.33.0
8-
github.com/go-ble/ble v0.0.0-20220207185428-60d1eecf2633
8+
github.com/go-ble/ble v0.0.0-20240122180141-8c5522f54333
99
github.com/golang-jwt/jwt/v5 v5.2.1
1010
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
1111
golang.org/x/term v0.5.0

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
1313
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1414
github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY=
1515
github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
16-
github.com/go-ble/ble v0.0.0-20220207185428-60d1eecf2633 h1:ZrzoZQz1CF33SPHLkjRpnVuZwr9cO1lTEc4Js7SgBos=
17-
github.com/go-ble/ble v0.0.0-20220207185428-60d1eecf2633/go.mod h1:fFJl/jD/uyILGBeD5iQ8tYHrPlJafyqCJzAyTHNJ1Uk=
16+
github.com/go-ble/ble v0.0.0-20240122180141-8c5522f54333 h1:bQK6D51cNzMSTyAf0HtM30V2IbljHTDam7jru9JNlJA=
17+
github.com/go-ble/ble v0.0.0-20240122180141-8c5522f54333/go.mod h1:fFJl/jD/uyILGBeD5iQ8tYHrPlJafyqCJzAyTHNJ1Uk=
1818
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
1919
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
2020
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=

pkg/connector/ble/ble.go

+19-6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package ble
55
import (
66
"context"
77
"crypto/sha1"
8+
"errors"
89
"fmt"
910
"strings"
1011
"sync"
@@ -169,16 +170,28 @@ func ScanVehicleBeacon(ctx context.Context, vin string) (Advertisement, error) {
169170
func scanVehicleBeacon(ctx context.Context, localName string) (Advertisement, error) {
170171
var err error
171172
ctx2, cancel := context.WithCancel(ctx)
173+
defer cancel()
172174

173-
ch := make(chan Advertisement)
175+
ch := make(chan Advertisement, 1)
174176
fn := func(a Advertisement) {
175177
if a.LocalName() != localName {
176178
return
177179
}
178-
cancel()
179-
ch <- a
180+
select {
181+
case ch <- a:
182+
cancel() // Notify device.Scan() that we found a match
183+
case <-ctx2.Done():
184+
// Another goroutine already found a matching advertisement. We need to return so that
185+
// the MacOS implementation of device.Scan(...) unblocks.
186+
}
187+
}
188+
189+
if err = device.Scan(ctx2, false, fn); !errors.Is(err, context.Canceled) {
190+
// If ctx rather than ctx2 was canceled, we'll pick that error up below. This is a bit
191+
// hacky, but unfortunately device.Scan() _always_ returns an error on MacOS because it does
192+
// not terminate until the provided context is canceled.
193+
return nil, err
180194
}
181-
err = device.Scan(ctx2, false, fn)
182195

183196
select {
184197
case a, ok := <-ch:
@@ -187,8 +200,8 @@ func scanVehicleBeacon(ctx context.Context, localName string) (Advertisement, er
187200
return nil, fmt.Errorf("scan channel closed")
188201
}
189202
return a, nil
190-
default:
191-
return nil, err
203+
case <-ctx.Done():
204+
return nil, ctx.Err()
192205
}
193206
}
194207

0 commit comments

Comments
 (0)