Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix a race condition in startNotification with the "valuechanged" event notifications #77

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,16 @@ console.log(buffer)
```javascript
const service2 = await gattServer.getPrimaryService('uuid')
const characteristic2 = await service2.getCharacteristic('uuid')
await characteristic2.startNotifications()
characteristic2.on('valuechanged', buffer => {
console.log(buffer)
})
await characteristic2.stopNotifications()
await characteristic2.startNotifications()
```

## STEP 5: Disconnect
When you have done you can disconnect and destroy the session.
When you have done you can stop notifications, disconnect and destroy the session.
```javascript
await characteristic2.stopNotifications()
await device.disconnect()
destroy()
```
Expand Down
4 changes: 2 additions & 2 deletions src/GattCharacteristic.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ class GattCharacteristic extends EventEmitter {
* It emits valuechanged event when receives a notification.
*/
async startNotifications () {
await this.helper.callMethod('StartNotify')

const cb = (propertiesChanged) => {
if ('Value' in propertiesChanged) {
const { value } = propertiesChanged.Value
Expand All @@ -115,6 +113,8 @@ class GattCharacteristic extends EventEmitter {
}

this.helper.on('PropertiesChanged', cb)

await this.helper.callMethod('StartNotify')
}

async stopNotifications () {
Expand Down
33 changes: 33 additions & 0 deletions test/GattCharacteristic.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,36 @@ test('event:valuechanged', async () => {

await characteristic.stopNotifications()
})

test('race condition between event:valuechanged / startNotification', async () => {
const characteristic = new GattCharacteristic(dbus, 'hci0', 'dev_00_00_00_00_00_00', 'characteristic0006', 'char008')
const cb = jest.fn(value => {})
characteristic.on('valuechanged', cb)

// Wrap the call to StartNotify with an early property change to check this event is not lost in a race condition
characteristic.helper.callMethod.mockImplementationOnce(async (method) => {
if (method !== 'StartNotify') {
throw new Error('Unexpected state in unit test')
}

await characteristic.helper.callMethod('StartNotify')

// Send the first event right after StartNotify
characteristic.helper.emit('PropertiesChanged',
{ Value: { signature: 'ay', value: [0x62, 0x61, 0x72] } } // means bar
)
})

// Start notifications, wait 200ms and send a second event
characteristic.startNotifications()
await new Promise((resolve) => setTimeout(resolve, 200))
characteristic.helper.emit('PropertiesChanged',
{ Value: { signature: 'ay', value: [0x62, 0x61, 0x72] } } // means bar
)

// Check the mocked callback function has been called twice
expect(cb.mock.calls).toHaveLength(2)

// Cleanup
characteristic.off('valuechanged', cb)
})