Skip to content

Commit f844306

Browse files
sbinetaykevl
authored andcommitted
linux: properly close goroutines started by EnableNotifications
Fixes #168. Signed-off-by: Sebastien Binet <binet@cern.ch>
1 parent 87d2492 commit f844306

File tree

1 file changed

+57
-11
lines changed

1 file changed

+57
-11
lines changed

gattc_linux.go

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ import (
1414
"github.com/muka/go-bluetooth/bluez/profile/gatt"
1515
)
1616

17+
var (
18+
errDupNotif = errors.New("unclosed notifications")
19+
)
20+
1721
// UUIDWrapper is a type alias for UUID so we ensure no conflicts with
1822
// struct method of the same name.
1923
type uuidWrapper = UUID
@@ -133,6 +137,7 @@ type DeviceCharacteristic struct {
133137
uuidWrapper
134138

135139
characteristic *gatt.GattCharacteristic1
140+
property chan *bluez.PropertyChanged // channel where notifications are reported
136141
}
137142

138143
// UUID returns the UUID for this DeviceCharacteristic.
@@ -238,19 +243,60 @@ func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error)
238243
// Configuration Descriptor (CCCD). This means that most peripherals will send a
239244
// notification with a new value every time the value of the characteristic
240245
// changes.
241-
func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error {
242-
ch, err := c.characteristic.WatchProperties()
243-
if err != nil {
244-
return err
245-
}
246-
go func() {
247-
for update := range ch {
248-
if update.Interface == "org.bluez.GattCharacteristic1" && update.Name == "Value" {
249-
callback(update.Value.([]byte))
246+
//
247+
// Users may call EnableNotifications with a nil callback to disable notifications.
248+
func (c *DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error {
249+
switch callback {
250+
default:
251+
if c.property != nil {
252+
return errDupNotif
253+
}
254+
255+
ch, err := c.characteristic.WatchProperties()
256+
if err != nil {
257+
return err
258+
}
259+
260+
err = c.characteristic.StartNotify()
261+
if err != nil {
262+
_ = c.characteristic.UnwatchProperties(ch)
263+
return err
264+
}
265+
c.property = ch
266+
267+
go func() {
268+
for update := range ch {
269+
if update == nil {
270+
continue
271+
}
272+
if update.Interface == "org.bluez.GattCharacteristic1" && update.Name == "Value" {
273+
callback(update.Value.([]byte))
274+
}
250275
}
276+
}()
277+
278+
return nil
279+
280+
case nil:
281+
if c.property == nil {
282+
return nil
283+
}
284+
285+
e1 := c.characteristic.StopNotify()
286+
e2 := c.characteristic.UnwatchProperties(c.property)
287+
c.property = nil
288+
289+
// FIXME(sbinet): use errors.Join(e1, e2)
290+
if e1 != nil {
291+
return e1
292+
}
293+
294+
if e2 != nil {
295+
return e2
251296
}
252-
}()
253-
return c.characteristic.StartNotify()
297+
298+
return nil
299+
}
254300
}
255301

256302
// GetMTU returns the MTU for the characteristic.

0 commit comments

Comments
 (0)