make opening a txn async again

as we'll need to await a bogus request first thing after opening the txn

funny enough, we originally made it sync to accommodate the same bug
in safari, but that didn't prevent any microtask being awaited
before scheduling a request in the calling code closing the txn.

We'll await a bogus request within the transaction class now so it
doesn't depend on the calling code
This commit is contained in:
Bruno Windels 2021-03-04 19:47:02 +01:00
parent 771719857d
commit e0d14207ac
15 changed files with 46 additions and 46 deletions

View File

@ -185,13 +185,13 @@ export class Session {
}
const key = await ssssKeyFromCredential(type, credential, this._storage, this._platform, this._olm);
// and create session backup, which needs to read from accountData
const readTxn = this._storage.readTxn([
const readTxn = await this._storage.readTxn([
this._storage.storeNames.accountData,
]);
await this._createSessionBackup(key, readTxn);
// only after having read a secret, write the key
// as we only find out if it was good if the MAC verification succeeds
const writeTxn = this._storage.readWriteTxn([
const writeTxn = await this._storage.readWriteTxn([
this._storage.storeNames.session,
]);
try {
@ -249,7 +249,7 @@ export class Session {
/** @internal */
async load(log) {
const txn = this._storage.readTxn([
const txn = await this._storage.readTxn([
this._storage.storeNames.session,
this._storage.storeNames.roomSummary,
this._storage.storeNames.roomMembers,
@ -301,7 +301,7 @@ export class Session {
async start(lastVersionResponse, log) {
if (lastVersionResponse) {
// store /versions response
const txn = this._storage.readWriteTxn([
const txn = await this._storage.readWriteTxn([
this._storage.storeNames.session
]);
txn.session.set("serverVersions", lastVersionResponse);
@ -310,7 +310,7 @@ export class Session {
}
// enable session backup, this requests the latest backup version
if (!this._sessionBackup) {
const txn = this._storage.readTxn([
const txn = await this._storage.readTxn([
this._storage.storeNames.session,
this._storage.storeNames.accountData,
]);
@ -323,7 +323,7 @@ export class Session {
this._hasSecretStorageKey.set(!!ssssKey);
}
// restore unfinished operations, like sending out room keys
const opsTxn = this._storage.readWriteTxn([
const opsTxn = await this._storage.readWriteTxn([
this._storage.storeNames.operations
]);
const operations = await opsTxn.operations.getAll();

View File

@ -201,7 +201,7 @@ export class Sync {
return rs.room.afterPrepareSync(rs.preparation, log);
})));
await log.wrap("write", async log => {
const syncTxn = this._openSyncTxn();
const syncTxn = await this._openSyncTxn();
try {
sessionState.changes = await log.wrap("session", log => this._session.writeSync(
response, syncFilterId, sessionState.preparation, syncTxn, log));
@ -253,7 +253,7 @@ export class Sync {
}
async _prepareSessionAndRooms(sessionState, roomStates, response, log) {
const prepareTxn = this._openPrepareSyncTxn();
const prepareTxn = await this._openPrepareSyncTxn();
sessionState.preparation = await log.wrap("session", log => this._session.prepareSync(
response, sessionState.lock, prepareTxn, log));

View File

@ -45,7 +45,7 @@ export class Account {
}
const pickledAccount = account.pickle(pickleKey);
const areDeviceKeysUploaded = false;
const txn = storage.readWriteTxn([
const txn = await storage.readWriteTxn([
storage.storeNames.session
]);
try {
@ -225,7 +225,7 @@ export class Account {
}
async _updateSessionStorage(storage, callback) {
const txn = storage.readWriteTxn([
const txn = await storage.readWriteTxn([
storage.storeNames.session
]);
try {

View File

@ -75,7 +75,7 @@ export class DeviceTracker {
}
const memberList = await room.loadMemberList(log);
try {
const txn = this._storage.readWriteTxn([
const txn = await this._storage.readWriteTxn([
this._storage.storeNames.roomSummary,
this._storage.storeNames.userIdentities,
]);
@ -157,7 +157,7 @@ export class DeviceTracker {
}, {log}).response();
const verifiedKeysPerUser = log.wrap("verify", log => this._filterVerifiedDeviceKeys(deviceKeyResponse["device_keys"], log));
const txn = this._storage.readWriteTxn([
const txn = await this._storage.readWriteTxn([
this._storage.storeNames.userIdentities,
this._storage.storeNames.deviceIdentities,
]);
@ -271,7 +271,7 @@ export class DeviceTracker {
* @return {[type]} [description]
*/
async devicesForTrackedRoom(roomId, hsApi, log) {
const txn = this._storage.readTxn([
const txn = await this._storage.readTxn([
this._storage.storeNames.roomMembers,
this._storage.storeNames.userIdentities,
]);
@ -287,7 +287,7 @@ export class DeviceTracker {
}
async devicesForRoomMembers(roomId, userIds, hsApi, log) {
const txn = this._storage.readTxn([
const txn = await this._storage.readTxn([
this._storage.storeNames.userIdentities,
]);
return await this._devicesForUserIds(roomId, userIds, txn, hsApi, log);
@ -319,7 +319,7 @@ export class DeviceTracker {
queriedDevices = await this._queryKeys(outdatedIdentities.map(i => i.userId), hsApi, log);
}
const deviceTxn = this._storage.readTxn([
const deviceTxn = await this._storage.readTxn([
this._storage.storeNames.deviceIdentities,
]);
const devicesPerUser = await Promise.all(upToDateIdentities.map(identity => {

View File

@ -59,7 +59,7 @@ export class RoomEncryption {
const events = entries.filter(e => e.isEncrypted && !e.isDecrypted && e.event).map(e => e.event);
const eventsBySession = groupEventsBySession(events);
const groups = Array.from(eventsBySession.values());
const txn = this._storage.readTxn([this._storage.storeNames.inboundGroupSessions]);
const txn = await this._storage.readTxn([this._storage.storeNames.inboundGroupSessions]);
const hasSessions = await Promise.all(groups.map(async group => {
return this._megolmDecryption.hasSession(this._room.id, group.senderKey, group.sessionId, txn);
}));
@ -164,7 +164,7 @@ export class RoomEncryption {
return;
}
// now check which sessions have been received already
const txn = this._storage.readTxn([this._storage.storeNames.inboundGroupSessions]);
const txn = await this._storage.readTxn([this._storage.storeNames.inboundGroupSessions]);
await Promise.all(Array.from(eventsBySession).map(async ([key, group]) => {
if (await this._megolmDecryption.hasSession(this._room.id, group.senderKey, group.sessionId, txn)) {
eventsBySession.delete(key);
@ -211,7 +211,7 @@ export class RoomEncryption {
if (roomKey) {
let keyIsBestOne = false;
try {
const txn = this._storage.readWriteTxn([this._storage.storeNames.inboundGroupSessions]);
const txn = await this._storage.readWriteTxn([this._storage.storeNames.inboundGroupSessions]);
try {
keyIsBestOne = await this._megolmDecryption.writeRoomKey(roomKey, txn);
} catch (err) {
@ -281,7 +281,7 @@ export class RoomEncryption {
}
async _shareNewRoomKey(roomKeyMessage, hsApi, log) {
let writeOpTxn = this._storage.readWriteTxn([this._storage.storeNames.operations]);
let writeOpTxn = await this._storage.readWriteTxn([this._storage.storeNames.operations]);
let operation;
try {
operation = this._writeRoomKeyShareOperation(roomKeyMessage, null, writeOpTxn);
@ -319,7 +319,7 @@ export class RoomEncryption {
this._isFlushingRoomKeyShares = true;
try {
if (!operations) {
const txn = this._storage.readTxn([this._storage.storeNames.operations]);
const txn = await this._storage.readTxn([this._storage.storeNames.operations]);
operations = await txn.operations.getAllByTypeAndScope("share_room_key", this._room.id);
}
for (const operation of operations) {
@ -355,7 +355,7 @@ export class RoomEncryption {
devices = await this._deviceTracker.devicesForTrackedRoom(this._room.id, hsApi, log);
const userIds = Array.from(devices.reduce((set, device) => set.add(device.userId), new Set()));
operation.userIds = userIds;
const userIdsTxn = this._storage.readWriteTxn([this._storage.storeNames.operations]);
const userIdsTxn = await this._storage.readWriteTxn([this._storage.storeNames.operations]);
try {
userIdsTxn.operations.update(operation);
} catch (err) {
@ -371,7 +371,7 @@ export class RoomEncryption {
"m.room_key", operation.roomKeyMessage, devices, hsApi, log));
await log.wrap("send", log => this._sendMessagesToDevices(ENCRYPTED_TYPE, messages, hsApi, log));
const removeTxn = this._storage.readWriteTxn([this._storage.storeNames.operations]);
const removeTxn = await this._storage.readWriteTxn([this._storage.storeNames.operations]);
try {
removeTxn.operations.remove(operation.id);
} catch (err) {

View File

@ -46,7 +46,7 @@ export class Encryption {
async ensureOutboundSession(roomId, encryptionParams) {
let session = new this._olm.OutboundGroupSession();
try {
const txn = this._storage.readWriteTxn([
const txn = await this._storage.readWriteTxn([
this._storage.storeNames.inboundGroupSessions,
this._storage.storeNames.outboundGroupSessions,
]);
@ -104,7 +104,7 @@ export class Encryption {
async encrypt(roomId, type, content, encryptionParams) {
let session = new this._olm.OutboundGroupSession();
try {
const txn = this._storage.readWriteTxn([
const txn = await this._storage.readWriteTxn([
this._storage.storeNames.inboundGroupSessions,
this._storage.storeNames.outboundGroupSessions,
]);

View File

@ -101,7 +101,7 @@ export class Encryption {
}
async _findExistingSessions(devices) {
const txn = this._storage.readTxn([this._storage.storeNames.olmSessions]);
const txn = await this._storage.readTxn([this._storage.storeNames.olmSessions]);
const sessionIdsForDevice = await Promise.all(devices.map(async device => {
return await txn.olmSessions.getSessionIds(device.curve25519Key);
}));
@ -215,7 +215,7 @@ export class Encryption {
}
async _loadSessions(encryptionTargets) {
const txn = this._storage.readTxn([this._storage.storeNames.olmSessions]);
const txn = await this._storage.readTxn([this._storage.storeNames.olmSessions]);
// given we run loading in parallel, there might still be some
// storage requests that will finish later once one has failed.
// those should not allocate a session anymore.
@ -241,7 +241,7 @@ export class Encryption {
}
async _storeSessions(encryptionTargets, timestamp) {
const txn = this._storage.readWriteTxn([this._storage.storeNames.olmSessions]);
const txn = await this._storage.readWriteTxn([this._storage.storeNames.olmSessions]);
try {
for (const target of encryptionTargets) {
const sessionEntry = createSessionEntry(

View File

@ -79,7 +79,7 @@ export class Room extends EventEmitter {
if (!this._roomEncryption) {
return;
}
const txn = this._storage.readTxn([
const txn = await this._storage.readTxn([
this._storage.storeNames.timelineEvents,
this._storage.storeNames.inboundGroupSessions,
]);
@ -118,7 +118,7 @@ export class Room extends EventEmitter {
_decryptEntries(source, entries, inboundSessionTxn = null) {
const request = new DecryptionRequest(async r => {
if (!inboundSessionTxn) {
inboundSessionTxn = this._storage.readTxn([this._storage.storeNames.inboundGroupSessions]);
inboundSessionTxn = await this._storage.readTxn([this._storage.storeNames.inboundGroupSessions]);
}
if (r.cancelled) return;
const events = entries.filter(entry => {
@ -135,7 +135,7 @@ export class Room extends EventEmitter {
// read to fetch devices if timeline is open
stores.push(this._storage.storeNames.deviceIdentities);
}
const writeTxn = this._storage.readWriteTxn(stores);
const writeTxn = await this._storage.readWriteTxn(stores);
let decryption;
try {
decryption = await changes.write(writeTxn);
@ -472,7 +472,7 @@ export class Room extends EventEmitter {
}
}, {log}).response();
const txn = this._storage.readWriteTxn([
const txn = await this._storage.readWriteTxn([
this._storage.storeNames.pendingEvents,
this._storage.storeNames.timelineEvents,
this._storage.storeNames.timelineFragments,
@ -584,7 +584,7 @@ export class Room extends EventEmitter {
async _getLastEventId() {
const lastKey = this._syncWriter.lastMessageKey;
if (lastKey) {
const txn = this._storage.readTxn([
const txn = await this._storage.readTxn([
this._storage.storeNames.timelineEvents,
]);
const eventEntry = await txn.timelineEvents.get(this._roomId, lastKey);
@ -607,7 +607,7 @@ export class Room extends EventEmitter {
if (this.isUnread || this.notificationCount) {
return await this._platform.logger.wrapOrRun(log, "clearUnread", async log => {
log.set("id", this.id);
const txn = this._storage.readWriteTxn([
const txn = await this._storage.readWriteTxn([
this._storage.storeNames.roomSummary,
]);
let data;
@ -706,7 +706,7 @@ export class Room extends EventEmitter {
if (this.isEncrypted) {
stores.push(this._storage.storeNames.inboundGroupSessions);
}
const txn = this._storage.readTxn(stores);
const txn = await this._storage.readTxn(stores);
const storageEntry = await txn.timelineEvents.getByEventId(this._roomId, eventId);
if (storageEntry) {
const entry = new EventEntry(storageEntry, this._fragmentIdComparer);

View File

@ -251,7 +251,7 @@ export class RoomSummary {
if (data === this._data) {
return false;
}
const txn = storage.readWriteTxn([
const txn = await storage.readWriteTxn([
storage.storeNames.roomSummary,
]);
try {

View File

@ -18,7 +18,7 @@ limitations under the License.
import {RoomMember} from "./RoomMember.js";
async function loadMembers({roomId, storage}) {
const txn = storage.readTxn([
const txn = await storage.readTxn([
storage.storeNames.roomMembers,
]);
const memberDatas = await txn.roomMembers.getAll(roomId);
@ -33,7 +33,7 @@ async function fetchMembers({summary, syncToken, roomId, hsApi, storage, setChan
const memberResponse = await hsApi.members(roomId, {at: syncToken}, {log}).response();
const txn = storage.readWriteTxn([
const txn = await storage.readWriteTxn([
storage.storeNames.roomSummary,
storage.storeNames.roomMembers,
]);

View File

@ -129,7 +129,7 @@ export class SendQueue {
async _removeEvent(pendingEvent) {
const idx = this._pendingEvents.array.indexOf(pendingEvent);
if (idx !== -1) {
const txn = this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);
const txn = await this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);
try {
txn.pendingEvents.remove(pendingEvent.roomId, pendingEvent.queueIndex);
} catch (err) {
@ -185,7 +185,7 @@ export class SendQueue {
}
async _tryUpdateEvent(pendingEvent) {
const txn = this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);
const txn = await this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);
try {
// pendingEvent might have been removed already here
// by a racing remote echo, so check first so we don't recreate it
@ -200,7 +200,7 @@ export class SendQueue {
}
async _createAndStoreEvent(eventType, content, attachments) {
const txn = this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);
const txn = await this._storage.readWriteTxn([this._storage.storeNames.pendingEvents]);
let pendingEvent;
try {
const pendingEventsStore = txn.pendingEvents;

View File

@ -46,7 +46,7 @@ export class Timeline {
/** @package */
async load(user) {
const txn = this._storage.readTxn(this._timelineReader.readTxnStores.concat(this._storage.storeNames.roomMembers));
const txn = await this._storage.readTxn(this._timelineReader.readTxnStores.concat(this._storage.storeNames.roomMembers));
const memberData = await txn.roomMembers.get(this._roomId, user.id);
this._ownMember = new RoomMember(memberData);
// it should be fine to not update the local entries,

View File

@ -108,14 +108,14 @@ export class TimelineReader {
readFrom(eventKey, direction, amount) {
return new ReaderRequest(async r => {
const txn = this._storage.readTxn(this.readTxnStores);
const txn = await this._storage.readTxn(this.readTxnStores);
return await this._readFrom(eventKey, direction, amount, r, txn);
});
}
readFromEnd(amount, existingTxn = null) {
return new ReaderRequest(async r => {
const txn = existingTxn || this._storage.readTxn(this.readTxnStores);
const txn = existingTxn || await this._storage.readTxn(this.readTxnStores);
const liveFragment = await txn.timelineFragments.liveFragment(this._roomId);
let entries;
// room hasn't been synced yet

View File

@ -19,7 +19,7 @@ import {keyFromPassphrase} from "./passphrase.js";
import {keyFromRecoveryKey} from "./recoveryKey.js";
async function readDefaultKeyDescription(storage) {
const txn = storage.readTxn([
const txn = await storage.readTxn([
storage.storeNames.accountData
]);
const defaultKeyEvent = await txn.accountData.get("m.secret_storage.default_key");

View File

@ -34,7 +34,7 @@ export class Storage {
}
}
readTxn(storeNames) {
async readTxn(storeNames) {
this._validateStoreNames(storeNames);
try {
const txn = this._db.transaction(storeNames, "readonly");
@ -44,7 +44,7 @@ export class Storage {
}
}
readWriteTxn(storeNames) {
async readWriteTxn(storeNames) {
this._validateStoreNames(storeNames);
try {
const txn = this._db.transaction(storeNames, "readwrite");