Reuse global locks, handle empty envelopes
This commit is contained in:
parent
25f271e61c
commit
1f0119a7ac
5 changed files with 29 additions and 11 deletions
|
@ -44,20 +44,22 @@ export type SessionsOptions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Sessions extends SessionStore {
|
export class Sessions extends SessionStore {
|
||||||
private readonly lock: Lock;
|
private readonly lock: Lock | undefined;
|
||||||
|
|
||||||
private inTransaction = false;
|
private inTransaction = false;
|
||||||
|
|
||||||
constructor(private readonly options: SessionsOptions = {}) {
|
constructor(private readonly options: SessionsOptions = {}) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.lock = options.lock || new Lock();
|
this.lock = options.lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async transaction<T>(fn: () => Promise<T>): Promise<T> {
|
public async transaction<T>(fn: () => Promise<T>): Promise<T> {
|
||||||
assert(!this.inTransaction, 'Already in transaction');
|
assert(!this.inTransaction, 'Already in transaction');
|
||||||
this.inTransaction = true;
|
this.inTransaction = true;
|
||||||
|
|
||||||
|
assert(this.lock, "Can't start transaction without lock");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await window.textsecure.storage.protocol.sessionTransaction(
|
return await window.textsecure.storage.protocol.sessionTransaction(
|
||||||
'Sessions.transaction',
|
'Sessions.transaction',
|
||||||
|
@ -117,9 +119,9 @@ export type IdentityKeysOptions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export class IdentityKeys extends IdentityKeyStore {
|
export class IdentityKeys extends IdentityKeyStore {
|
||||||
private readonly lock: Lock;
|
private readonly lock: Lock | undefined;
|
||||||
|
|
||||||
constructor({ lock = new Lock() }: IdentityKeysOptions = {}) {
|
constructor({ lock }: IdentityKeysOptions = {}) {
|
||||||
super();
|
super();
|
||||||
this.lock = lock;
|
this.lock = lock;
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@ export type SessionTransactionOptions = {
|
||||||
readonly lock?: Lock;
|
readonly lock?: Lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GLOBAL_LOCK = new Lock();
|
const GLOBAL_LOCK = new Lock('GLOBAL_LOCK');
|
||||||
|
|
||||||
async function _fillCaches<ID, T extends HasIdType<ID>, HydratedType>(
|
async function _fillCaches<ID, T extends HasIdType<ID>, HydratedType>(
|
||||||
object: SignalProtocolStore,
|
object: SignalProtocolStore,
|
||||||
|
@ -608,16 +608,25 @@ export class SignalProtocolStore extends EventsMixin {
|
||||||
body: () => Promise<T>,
|
body: () => Promise<T>,
|
||||||
lock: Lock = GLOBAL_LOCK
|
lock: Lock = GLOBAL_LOCK
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
|
const debugName = `sessionTransaction(${lock.name}:${name})`;
|
||||||
|
|
||||||
// Allow re-entering from LibSignalStores
|
// Allow re-entering from LibSignalStores
|
||||||
const isNested = this.sessionLock === lock;
|
const isNested = this.sessionLock === lock;
|
||||||
if (this.sessionLock && !isNested) {
|
if (this.sessionLock && !isNested) {
|
||||||
window.log.info(`sessionTransaction(${name}): sessions locked, waiting`);
|
const start = Date.now();
|
||||||
|
|
||||||
|
window.log.info(
|
||||||
|
`${debugName}: locked by ${this.sessionLock.name}, waiting`
|
||||||
|
);
|
||||||
await new Promise<void>(resolve => this.sessionLockQueue.push(resolve));
|
await new Promise<void>(resolve => this.sessionLockQueue.push(resolve));
|
||||||
|
|
||||||
|
const duration = Date.now() - start;
|
||||||
|
window.log.info(`${debugName}: unlocked after ${duration}ms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isNested) {
|
if (!isNested) {
|
||||||
if (lock !== GLOBAL_LOCK) {
|
if (lock !== GLOBAL_LOCK) {
|
||||||
window.log.info(`sessionTransaction(${name}): enter`);
|
window.log.info(`${debugName}: enter`);
|
||||||
}
|
}
|
||||||
this.sessionLock = lock;
|
this.sessionLock = lock;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1346,7 +1346,7 @@ describe('SignalProtocolStore', () => {
|
||||||
const id = `${number}.1`;
|
const id = `${number}.1`;
|
||||||
const testRecord = getSessionRecord();
|
const testRecord = getSessionRecord();
|
||||||
|
|
||||||
const lock = new Lock();
|
const lock = new Lock('lock');
|
||||||
|
|
||||||
await store.sessionTransaction(
|
await store.sessionTransaction(
|
||||||
'test',
|
'test',
|
||||||
|
|
|
@ -49,6 +49,7 @@ import WebSocketResource, {
|
||||||
import Crypto from './Crypto';
|
import Crypto from './Crypto';
|
||||||
import { deriveMasterKeyFromGroupV1, typedArrayToArrayBuffer } from '../Crypto';
|
import { deriveMasterKeyFromGroupV1, typedArrayToArrayBuffer } from '../Crypto';
|
||||||
import { ContactBuffer, GroupBuffer } from './ContactsParser';
|
import { ContactBuffer, GroupBuffer } from './ContactsParser';
|
||||||
|
import { assert } from '../util/assert';
|
||||||
import { isByteBufferEmpty } from '../util/isByteBufferEmpty';
|
import { isByteBufferEmpty } from '../util/isByteBufferEmpty';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -609,7 +610,11 @@ class MessageReceiverInner extends EventTarget {
|
||||||
envelopePlaintext = MessageReceiverInner.stringToArrayBufferBase64(
|
envelopePlaintext = MessageReceiverInner.stringToArrayBufferBase64(
|
||||||
item.envelope
|
item.envelope
|
||||||
);
|
);
|
||||||
} else if (item.envelope && typeof item.envelope === 'string') {
|
} else if (typeof item.envelope === 'string') {
|
||||||
|
assert(
|
||||||
|
item.envelope || item.decrypted,
|
||||||
|
'MessageReceiver.queueCached: empty envelope without decrypted data'
|
||||||
|
);
|
||||||
envelopePlaintext = MessageReceiverInner.stringToArrayBuffer(
|
envelopePlaintext = MessageReceiverInner.stringToArrayBuffer(
|
||||||
item.envelope
|
item.envelope
|
||||||
);
|
);
|
||||||
|
@ -762,7 +767,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
const decrypted: Array<DecryptedEnvelope> = [];
|
const decrypted: Array<DecryptedEnvelope> = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const lock = new Lock();
|
const lock = new Lock('cacheAndQueueBatch');
|
||||||
const sessionStore = new Sessions({
|
const sessionStore = new Sessions({
|
||||||
transactionOnly: true,
|
transactionOnly: true,
|
||||||
lock,
|
lock,
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
export class Lock {}
|
export class Lock {
|
||||||
|
constructor(public readonly name: string) {}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue