Live video on the AT Protocol

app: prevent concurrent sqlite3 access

Eli Mallon 9839024d 4afc87bf

+76 -5
+9 -1
js/app/app.config.ts
··· 12 12 return config; 13 13 }; 14 14 15 + export const withoutNotificationsIOS: ConfigPlugin = (config) => { 16 + config = withEntitlementsPlist(config, (config) => { 17 + delete config.modResults["aps-environment"]; 18 + return config; 19 + }); 20 + return config; 21 + }; 22 + 15 23 const withConsistentVersionNumber = ( 16 24 config, 17 25 { version }: { version: string }, ··· 170 178 "@react-native-firebase/messaging", 171 179 [withNotificationsIOS, {}], 172 180 ] 173 - : ["expo-dev-launcher"]), 181 + : ["expo-dev-launcher", withoutNotificationsIOS]), 174 182 ], 175 183 experiments: { 176 184 typedRoutes: true,
+38
js/app/storage/lock.tsx
··· 1 + type Cont = () => void; 2 + 3 + export class Lock { 4 + private readonly queue: Cont[] = []; 5 + private acquired = false; 6 + 7 + public async acquire(): Promise<void> { 8 + if (!this.acquired) { 9 + this.acquired = true; 10 + } else { 11 + return new Promise<void>((resolve, _) => { 12 + this.queue.push(resolve); 13 + }); 14 + } 15 + } 16 + 17 + public async release(): Promise<void> { 18 + if (this.queue.length === 0 && this.acquired) { 19 + this.acquired = false; 20 + return; 21 + } 22 + 23 + const continuation = this.queue.shift(); 24 + return new Promise((res: Cont) => { 25 + continuation!(); 26 + res(); 27 + }); 28 + } 29 + 30 + public async critical<T>(task: () => Promise<T>) { 31 + await this.acquire(); 32 + try { 33 + return await task(); 34 + } finally { 35 + await this.release(); 36 + } 37 + } 38 + }
+29 -4
js/app/storage/storage.native.tsx
··· 1 1 import Storage from "expo-sqlite/kv-store"; 2 2 import { AQStorage } from "./storage.shared"; 3 + import { Lock } from "./lock"; 4 + 5 + // Needed because concurrent calls seem to return with a locked database 6 + const lock = new Lock(); 3 7 4 8 export default class NativeStorage implements AQStorage { 5 9 async getItem(key: string): Promise<string | null> { 6 - const value = await Storage.getItem(key); 7 - return value ?? null; 10 + return lock.critical(async () => { 11 + try { 12 + const value = await Storage.getItem(key); 13 + return value ?? null; 14 + } catch (e) { 15 + console.error(`error in NativeStorage.getItem: ${e}`); 16 + throw e; 17 + } 18 + }); 8 19 } 9 20 10 21 async setItem(key: string, value: string): Promise<void> { 11 - await Storage.setItem(key, value); 22 + return lock.critical(async () => { 23 + try { 24 + await Storage.setItem(key, value); 25 + } catch (e) { 26 + console.error(`error in NativeStorage.setItem: ${e}`); 27 + throw e; 28 + } 29 + }); 12 30 } 13 31 14 32 async removeItem(key: string): Promise<void> { 15 - await Storage.removeItem(key); 33 + return lock.critical(async () => { 34 + try { 35 + await Storage.removeItem(key); 36 + } catch (e) { 37 + console.error(`error in NativeStorage.removeItem: ${e}`); 38 + throw e; 39 + } 40 + }); 16 41 } 17 42 }