diff --git a/ts/SignalProtocolStore.ts b/ts/SignalProtocolStore.ts index 768c0c9b8..ec2486a38 100644 --- a/ts/SignalProtocolStore.ts +++ b/ts/SignalProtocolStore.ts @@ -625,10 +625,21 @@ export class SignalProtocolStore extends EventsMixin { window.log.info( `${debugName}: locked by ${this.currentZone.name}, waiting` ); - await new Promise(resolve => this.zoneQueue.push(resolve)); - const duration = Date.now() - start; - window.log.info(`${debugName}: unlocked after ${duration}ms`); + return new Promise((resolve, reject) => { + this.zoneQueue.push(async () => { + const duration = Date.now() - start; + window.log.info(`${debugName}: unlocked after ${duration}ms`); + + // Call `.withZone` synchronously from `this.zoneQueue` to avoid + // extra in-between ticks while we are on microtasks queue. + try { + resolve(await this.withZone(zone, name, body)); + } catch (error) { + reject(error); + } + }); + }); } this.enterZone(zone, name); diff --git a/ts/test-electron/SignalProtocolStore_test.ts b/ts/test-electron/SignalProtocolStore_test.ts index 8530a5da9..d5507ac93 100644 --- a/ts/test-electron/SignalProtocolStore_test.ts +++ b/ts/test-electron/SignalProtocolStore_test.ts @@ -1384,6 +1384,32 @@ describe('SignalProtocolStore', () => { assert.equal(await store.loadSession(id), testRecord); }); + + it('can be re-entered after waiting', async () => { + const a = new Zone('a'); + const b = new Zone('b'); + + const order: Array = []; + const promises: Array> = []; + + // What happens below is briefly following: + // 1. We enter zone "a" + // 2. We wait for zone "a" to be left to enter zone "b" + // 3. Skip few ticks to trigger leave of zone "a" and resolve the waiting + // queue promise for zone "b" + // 4. Enter zone "a" while resolution was the promise above is queued in + // microtasks queue. + + promises.push(store.withZone(a, 'a', async () => order.push(1))); + promises.push(store.withZone(b, 'b', async () => order.push(2))); + await Promise.resolve(); + await Promise.resolve(); + promises.push(store.withZone(a, 'a again', async () => order.push(3))); + + await Promise.all(promises); + + assert.deepEqual(order, [1, 2, 3]); + }); }); describe('Not yet processed messages', () => {