Skip to content

Commit 94ee828

Browse files
authored
Fix a race condition in startNotification with the "valuechanged" event notifications (#77)
* see #72: fix a race condition in startNotification with the "valuechanged" event notifications * fix styling issues
1 parent a435111 commit 94ee828

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,16 @@ console.log(buffer)
100100
```javascript
101101
const service2 = await gattServer.getPrimaryService('uuid')
102102
const characteristic2 = await service2.getCharacteristic('uuid')
103-
await characteristic2.startNotifications()
104103
characteristic2.on('valuechanged', buffer => {
105104
console.log(buffer)
106105
})
107-
await characteristic2.stopNotifications()
106+
await characteristic2.startNotifications()
108107
```
109108

110109
## STEP 5: Disconnect
111-
When you have done you can disconnect and destroy the session.
110+
When you have done you can stop notifications, disconnect and destroy the session.
112111
```javascript
112+
await characteristic2.stopNotifications()
113113
await device.disconnect()
114114
destroy()
115115
```

src/GattCharacteristic.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,6 @@ class GattCharacteristic extends EventEmitter {
105105
* It emits valuechanged event when receives a notification.
106106
*/
107107
async startNotifications () {
108-
await this.helper.callMethod('StartNotify')
109-
110108
const cb = (propertiesChanged) => {
111109
if ('Value' in propertiesChanged) {
112110
const { value } = propertiesChanged.Value
@@ -115,6 +113,8 @@ class GattCharacteristic extends EventEmitter {
115113
}
116114

117115
this.helper.on('PropertiesChanged', cb)
116+
117+
await this.helper.callMethod('StartNotify')
118118
}
119119

120120
async stopNotifications () {

test/GattCharacteristic.spec.js

+33
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,36 @@ test('event:valuechanged', async () => {
100100

101101
await characteristic.stopNotifications()
102102
})
103+
104+
test('race condition between event:valuechanged / startNotification', async () => {
105+
const characteristic = new GattCharacteristic(dbus, 'hci0', 'dev_00_00_00_00_00_00', 'characteristic0006', 'char008')
106+
const cb = jest.fn(value => {})
107+
characteristic.on('valuechanged', cb)
108+
109+
// Wrap the call to StartNotify with an early property change to check this event is not lost in a race condition
110+
characteristic.helper.callMethod.mockImplementationOnce(async (method) => {
111+
if (method !== 'StartNotify') {
112+
throw new Error('Unexpected state in unit test')
113+
}
114+
115+
await characteristic.helper.callMethod('StartNotify')
116+
117+
// Send the first event right after StartNotify
118+
characteristic.helper.emit('PropertiesChanged',
119+
{ Value: { signature: 'ay', value: [0x62, 0x61, 0x72] } } // means bar
120+
)
121+
})
122+
123+
// Start notifications, wait 200ms and send a second event
124+
characteristic.startNotifications()
125+
await new Promise((resolve) => setTimeout(resolve, 200))
126+
characteristic.helper.emit('PropertiesChanged',
127+
{ Value: { signature: 'ay', value: [0x62, 0x61, 0x72] } } // means bar
128+
)
129+
130+
// Check the mocked callback function has been called twice
131+
expect(cb.mock.calls).toHaveLength(2)
132+
133+
// Cleanup
134+
characteristic.off('valuechanged', cb)
135+
})

0 commit comments

Comments
 (0)