Skip to content

Commit

Permalink
Fix possible race condition
Browse files Browse the repository at this point in the history
  • Loading branch information
riccardobl committed Jan 17, 2024
1 parent 104c046 commit 4a20a40
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 36 deletions.
76 changes: 44 additions & 32 deletions src/js/AssetProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ export default class AssetProvider {
try {
this.starting = true;
// restore tracked assets
this.ready = true;

const trackedAssets = await this.store.get("trackedAssets");
if (trackedAssets) {
Expand All @@ -102,6 +101,7 @@ export default class AssetProvider {

this.staticIcons = Icons;
this.specialSymbols = SpecialSymbols;
this.ready = true;
} catch (e) {
console.error(e);
} finally {
Expand Down Expand Up @@ -198,45 +198,57 @@ export default class AssetProvider {
async track(assetHash, noInit = false) {
if (!noInit) await this._init();
if (assetHash === this.baseAssetId) return;
if (this._isFiat(assetHash)) {
if (this.trackedFiatAssets.indexOf(assetHash) < 0) {
this.trackedFiatAssets.push(assetHash);
await this.store.set("trackedFiatAssets", this.trackedFiatAssets);
await this.store.lock("trackedAssets");
try {
console.log("Track", assetHash);

if (this._isFiat(assetHash)) {
if (this.trackedFiatAssets.indexOf(assetHash) < 0) {
this.trackedFiatAssets.push(assetHash);
await this.store.set("trackedFiatAssets", this.trackedFiatAssets);
}
return;
}
return;
}

if (this.trackedAssets.indexOf(assetHash) >= 0) return;
if (this.trackedAssets.indexOf(assetHash) >= 0) return;

this.trackedAssets.push(assetHash);
await this.store.set("trackedAssets", this.trackedAssets);
this.trackedAssets.push(assetHash);
await this.store.set("trackedAssets", this.trackedAssets);

let first = true;
return new Promise((res, rej) => {
const trackerCallback = async (price, baseAssetId) => {
await this.cache.set("p:" + assetHash, price);
if (first) {
res(price);
first = false;
}
};
if (!this.trackerCallbacks) this.trackerCallbacks = {};
this.trackerCallbacks[assetHash] = trackerCallback;
this.sideSwap.subscribeToAssetPriceUpdate(assetHash, trackerCallback);
});
let first = true;
return new Promise((res, rej) => {
const trackerCallback = async (price, baseAssetId) => {
await this.cache.set("p:" + assetHash, price);
if (first) {
res(price);
first = false;
}
};
if (!this.trackerCallbacks) this.trackerCallbacks = {};
this.trackerCallbacks[assetHash] = trackerCallback;
this.sideSwap.subscribeToAssetPriceUpdate(assetHash, trackerCallback);
});
} finally {
await this.store.unlock("trackedAssets");
}
}

async untrack(assetHash) {
if (assetHash === this.baseAssetId) return;
if (this._isFiat(assetHash)) return;
const index = this.trackedAssets.indexOf(assetHash);
if (index < 0) return;
this.trackedAssets.splice(index, 1);
await this.store.set("trackedAssets", this.trackedAssets);
const trackerCallback = this.trackerCallbacks[assetHash];
if (trackerCallback) {
this.sideSwap.unsubscribeFromAssetPriceUpdate(assetHash, trackerCallback);
delete this.trackerCallbacks[assetHash];
await this.store.lock("trackedAssets");
try {
if (this._isFiat(assetHash)) return;
const index = this.trackedAssets.indexOf(assetHash);
if (index < 0) return;
this.trackedAssets.splice(index, 1);
await this.store.set("trackedAssets", this.trackedAssets);
const trackerCallback = this.trackerCallbacks[assetHash];
if (trackerCallback) {
this.sideSwap.unsubscribeFromAssetPriceUpdate(assetHash, trackerCallback);
delete this.trackerCallbacks[assetHash];
}
} finally {
await this.store.unlock("trackedAssets");
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/js/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default {
HARDCODED_FEE: 0.27, // used when the fee api fails
DEBOUNCE_CALLBACK_TIME: 500, // lower this value to make the app more responsive, but more resource intensive
APP_ID: "lq", // The app id
APP_VERSION: 1, // bumping this invalidates all cache and storage
APP_VERSION: 10, // bumping this invalidates all cache and storage
STORAGE_METHODS: ["LocalStore", "IDBStore", "MemStore"], // order matters, first is preferred
STORAGE_METHODS_BY_SPEED: ["LocalStore", "IDBStore", "MemStore"], // order matters, first is fastest

Expand Down
4 changes: 2 additions & 2 deletions src/js/LiquidWallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -1409,7 +1409,7 @@ export default class LiquidWallet {
*/
async pinAsset(assetHash) {
await this.check();
this.assetProvider.track(assetHash);
return this.assetProvider.track(assetHash);
}

/**
Expand All @@ -1418,7 +1418,7 @@ export default class LiquidWallet {
*/
async unpinAsset(assetHash) {
await this.check();
this.assetProvider.untrack(assetHash);
return this.assetProvider.untrack(assetHash);
}

/**
Expand Down
14 changes: 13 additions & 1 deletion src/js/storage/AbstractBrowserStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ export default class AbstractBrowserStore {
constructor(prefix, limit) {
this.limit = limit;
this.prefix = prefix;
this.locks = {};
}

async lock(key) {
while (this.locks[key]) {
await new Promise((resolve) => setTimeout(resolve, 100));
}
this.locks[key] = true;
}

unlock(key) {
delete this.locks[key];
}

async _init() {
Expand Down Expand Up @@ -138,6 +150,7 @@ export default class AbstractBrowserStore {

const expire = this.expirationTable.get(key);
if (!value || (expire && expire < Date.now())) {
console.log("Refreshing " + key);
if (refreshCallback) {
let refreshed = Promise.resolve(refreshCallback());
refreshed = refreshed.then(async (data) => {
Expand Down Expand Up @@ -170,7 +183,6 @@ export default class AbstractBrowserStore {
for (let [key, access] of this.accessTable) {
if (key.startsWith("s:")) continue;
if (access < oldestAccess) {
alert("Deleting " + key);
oldestAccess = access;
oldestKey = key;
}
Expand Down

0 comments on commit 4a20a40

Please sign in to comment.