Skip to content

Commit

Permalink
Fix race condition in session transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
indutny-signal committed May 24, 2021
1 parent 7ca581a commit 095fbce
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 3 deletions.
17 changes: 14 additions & 3 deletions ts/SignalProtocolStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,10 +625,21 @@ export class SignalProtocolStore extends EventsMixin {
window.log.info(
`${debugName}: locked by ${this.currentZone.name}, waiting`
);
await new Promise<void>(resolve => this.zoneQueue.push(resolve));

const duration = Date.now() - start;
window.log.info(`${debugName}: unlocked after ${duration}ms`);
return new Promise<T>((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);
Expand Down
26 changes: 26 additions & 0 deletions ts/test-electron/SignalProtocolStore_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<number> = [];
const promises: Array<Promise<unknown>> = [];

// 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', () => {
Expand Down

0 comments on commit 095fbce

Please sign in to comment.