From bd294eddfba8775d74bed1c44b1e698a2eb4dba4 Mon Sep 17 00:00:00 2001 From: Rafael Esposito Date: Tue, 17 Sep 2024 10:53:46 +0200 Subject: [PATCH 1/3] Fix memory leak in waitDevice method --- src/Adapter.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Adapter.js b/src/Adapter.js index 15b13db..3cc8fe8 100644 --- a/src/Adapter.js +++ b/src/Adapter.js @@ -161,12 +161,14 @@ class Adapter { cancellable.push(() => clearTimeout(handler)) }) - const device = await Promise.race([discoveryHandler, timeoutHandler]) - - for (const cancel of cancellable) { - cancel() + try { + const device = await Promise.race([discoveryHandler, timeoutHandler]) + return device + } finally { + for (const cancel of cancellable) { + cancel() + } } - return device } /** From a2f4c920dc19004724150d01463bfec8af6fba94 Mon Sep 17 00:00:00 2001 From: Rafael Esposito Date: Wed, 16 Oct 2024 11:49:24 +0200 Subject: [PATCH 2/3] add unit test for waitDevice method to cover memory leak scenario --- test/Adapter.spec.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/Adapter.spec.js b/test/Adapter.spec.js index 752c6ae..7fef9b2 100644 --- a/test/Adapter.spec.js +++ b/test/Adapter.spec.js @@ -142,4 +142,31 @@ describe('waitDevice', () => { return res }) + + test('clear intervals and timeouts after fail', async () => { + jest.useFakeTimers('legacy') + + const adapter = new Adapter(dbus, 'hci0') + + adapter.helper.children.mockResolvedValue([ + 'dev_11_11_11_11_11_11', + 'dev_22_22_22_22_22_22', + 'dev_33_33_33_33_33_33' + ]) + + const timeout = 500 + const discoveryInterval = 100 + + const spyClearInterval = jest.spyOn(global, 'clearInterval') + const spyClearTimeout = jest.spyOn(global, 'clearInterval') + + const waitDevicePromise = adapter.waitDevice('44:44:44:44:44:44', timeout, discoveryInterval) + + jest.advanceTimersByTime(500) + + await expect(waitDevicePromise).rejects.toThrow('operation timed out') + + expect(spyClearInterval).toHaveBeenCalled() + expect(spyClearTimeout).toHaveBeenCalled() + }) }) From a49a807e6f8f48c21e4d32c2e08434377bf070cd Mon Sep 17 00:00:00 2001 From: Rafael Esposito Date: Wed, 30 Oct 2024 14:23:06 +0100 Subject: [PATCH 3/3] apply requested changes to adapter spec --- test/Adapter.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Adapter.spec.js b/test/Adapter.spec.js index 7fef9b2..3c79cb9 100644 --- a/test/Adapter.spec.js +++ b/test/Adapter.spec.js @@ -144,7 +144,7 @@ describe('waitDevice', () => { }) test('clear intervals and timeouts after fail', async () => { - jest.useFakeTimers('legacy') + jest.useFakeTimers() const adapter = new Adapter(dbus, 'hci0') @@ -158,11 +158,11 @@ describe('waitDevice', () => { const discoveryInterval = 100 const spyClearInterval = jest.spyOn(global, 'clearInterval') - const spyClearTimeout = jest.spyOn(global, 'clearInterval') + const spyClearTimeout = jest.spyOn(global, 'clearTimeout') const waitDevicePromise = adapter.waitDevice('44:44:44:44:44:44', timeout, discoveryInterval) - jest.advanceTimersByTime(500) + jest.advanceTimersByTime(timeout) await expect(waitDevicePromise).rejects.toThrow('operation timed out')