gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-wallet-webex] branch master updated: properly implem


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated: properly implement db resetting
Date: Mon, 05 Jun 2017 02:00:06 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-webex.

The following commit(s) were added to refs/heads/master by this push:
     new e95027f3 properly implement db resetting
e95027f3 is described below

commit e95027f37712c37be243403a78ff6a327a83177e
Author: Florian Dold <address@hidden>
AuthorDate: Mon Jun 5 02:00:03 2017 +0200

    properly implement db resetting
---
 src/timer.ts              | 66 +++++++++++++++++++++++++++++++++++++++++++
 src/wallet.ts             | 40 +++++++++++++-------------
 src/webex/pages/popup.tsx |  2 +-
 src/webex/wxBackend.ts    | 72 ++++++++++++++++++++++++++++++++++-------------
 4 files changed, 139 insertions(+), 41 deletions(-)

diff --git a/src/timer.ts b/src/timer.ts
index dd67dbd2..f19e975a 100644
--- a/src/timer.ts
+++ b/src/timer.ts
@@ -75,3 +75,69 @@ export function every(delayMs: number, callback: () => 
void): TimerHandle {
 export function after(delayMs: number, callback: () => void): TimerHandle {
   return new TimeoutHandle(setInterval(callback, delayMs));
 }
+
+
+const nullTimerHandle = {
+  clear() {
+  }
+};
+
+/**
+ * Group of timers that can be destroyed at once.
+ */
+export class TimerGroup {
+  private stopped: boolean = false;
+
+  private timerMap: { [index: number]: TimerHandle } = {};
+
+  private idGen = 1;
+
+  stopCurrentAndFutureTimers() {
+    this.stopped = true;
+    for (const x in this.timerMap) {
+      if (!this.timerMap.hasOwnProperty(x)) {
+        continue;
+      }
+      this.timerMap[x].clear();
+      delete this.timerMap[x];
+    }
+  }
+
+  after(delayMs: number, callback: () => void): TimerHandle {
+    if (this.stopped) {
+      console.warn("dropping timer since timer group is stopped");
+      return nullTimerHandle;
+    }
+    const h = after(delayMs, callback);
+    let myId = this.idGen++;
+    this.timerMap[myId] = h;
+
+    const tm = this.timerMap;
+
+    return {
+      clear() {
+        h.clear();
+        delete tm[myId];
+      },
+    };
+  }
+
+  every(delayMs: number, callback: () => void): TimerHandle {
+    if (this.stopped) {
+      console.warn("dropping timer since timer group is stopped");
+      return nullTimerHandle;
+    }
+    const h = every(delayMs, callback);
+    let myId = this.idGen++;
+    this.timerMap[myId] = h;
+
+    const tm = this.timerMap;
+
+    return {
+      clear() {
+        h.clear();
+        delete tm[myId];
+      },
+    };
+  }
+}
diff --git a/src/wallet.ts b/src/wallet.ts
index 0d4a8e0d..a4e1d46f 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -43,6 +43,7 @@ import {
   QueryRoot,
   Store,
 } from "./query";
+import {TimerGroup} from "./timer";
 import {
   AmountJson,
   Amounts,
@@ -346,18 +347,6 @@ const builtinCurrencies: CurrencyRecord[] = [
 ];
 
 
-// FIXME: these functions should be dependency-injected
-// into the wallet, as this is chrome specific => bad
-
-function setTimeout(f: any, t: number) {
-  return chrome.extension.getBackgroundPage().setTimeout(f, t);
-}
-
-function setInterval(f: any, t: number) {
-  return chrome.extension.getBackgroundPage().setInterval(f, t);
-}
-
-
 function isWithdrawableDenom(d: DenominationRecord) {
   const nowSec = (new Date()).getTime() / 1000;
   const stampWithdrawSec = getTalerStampSec(d.stampExpireWithdraw);
@@ -583,13 +572,17 @@ interface CoinsForPaymentArgs {
  * The platform-independent wallet implementation.
  */
 export class Wallet {
-  private db: IDBDatabase;
+  /**
+   * IndexedDB database used by the wallet.
+   */
+  db: IDBDatabase;
   private http: HttpRequestLibrary;
   private badge: Badge;
   private notifier: Notifier;
   private cryptoApi: CryptoApi;
   private processPreCoinConcurrent = 0;
   private processPreCoinThrottle: {[url: string]: number} = {};
+  private timerGroup: TimerGroup;
 
   /**
    * Set of identifiers for running operations.
@@ -613,7 +606,9 @@ export class Wallet {
     this.fillDefaults();
     this.resumePendingFromDb();
 
-    setInterval(() => this.updateExchanges(), 1000 * 60 * 15);
+    this.timerGroup = new TimerGroup();
+
+    this.timerGroup.every(1000 * 60 * 15, () => this.updateExchanges());
   }
 
   private async fillDefaults() {
@@ -1027,8 +1022,7 @@ export class Wallet {
       // random, exponential backoff truncated at 3 minutes
       const nextDelay = Math.min(2 * retryDelayMs + retryDelayMs * 
Math.random(), 3000 * 60);
       console.warn(`Failed to deplete reserve, trying again in ${retryDelayMs} 
ms`);
-      setTimeout(() => this.processReserve(reserveRecord, nextDelay),
-                 retryDelayMs);
+      this.timerGroup.after(retryDelayMs, () => 
this.processReserve(reserveRecord, nextDelay))
     } finally {
       this.stopOperation(opId);
     }
@@ -1039,8 +1033,7 @@ export class Wallet {
                                retryDelayMs = 200): Promise<void> {
     if (this.processPreCoinConcurrent >= 4 || 
this.processPreCoinThrottle[preCoin.exchangeBaseUrl]) {
       console.log("delaying processPreCoin");
-      setTimeout(() => this.processPreCoin(preCoin, Math.min(retryDelayMs * 2, 
5 * 60 * 1000)),
-                 retryDelayMs);
+      this.timerGroup.after(retryDelayMs, () => this.processPreCoin(preCoin, 
Math.min(retryDelayMs * 2, 5 * 60 * 1000)));
       return;
     }
     console.log("executing processPreCoin");
@@ -1098,12 +1091,11 @@ export class Wallet {
                     "ms", e);
       // exponential backoff truncated at one minute
       const nextRetryDelayMs = Math.min(retryDelayMs * 2, 5 * 60 * 1000);
-      setTimeout(() => this.processPreCoin(preCoin, nextRetryDelayMs),
-                 retryDelayMs);
+      this.timerGroup.after(retryDelayMs, () => this.processPreCoin(preCoin, 
nextRetryDelayMs))
 
       const currentThrottle = 
this.processPreCoinThrottle[preCoin.exchangeBaseUrl] || 0;
       this.processPreCoinThrottle[preCoin.exchangeBaseUrl] = currentThrottle + 
1;
-      setTimeout(() => 
{this.processPreCoinThrottle[preCoin.exchangeBaseUrl]--; }, retryDelayMs);
+      this.timerGroup.after(retryDelayMs, () => 
{this.processPreCoinThrottle[preCoin.exchangeBaseUrl]--; });
     } finally {
       this.processPreCoinConcurrent--;
     }
@@ -2335,4 +2327,10 @@ export class Wallet {
     return await this.q().iter(Stores.reserves).filter((r) => 
r.hasPayback).toArray();
   }
 
+  /**
+   * Stop ongoing processing.
+   */
+  stop() {
+    this.timerGroup.stopCurrentAndFutureTimers();
+  }
 }
diff --git a/src/webex/pages/popup.tsx b/src/webex/pages/popup.tsx
index d7429f83..831147f1 100644
--- a/src/webex/pages/popup.tsx
+++ b/src/webex/pages/popup.tsx
@@ -526,7 +526,7 @@ function openExtensionPage(page: string) {
 
 
 function openTab(page: string) {
-  return (evt) => {
+  return (evt: React.SyntheticEvent<any>) => {
     evt.preventDefault();
     chrome.tabs.create({
       url: page,
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index 30f12734..35fa0b57 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -246,9 +246,9 @@ function handleMessage(db: IDBDatabase,
   }
 }
 
-async function dispatch(db: IDBDatabase, wallet: Wallet, req: any, sender: 
any, sendResponse: any): Promise<void> {
+async function dispatch(wallet: Wallet, req: any, sender: any, sendResponse: 
any): Promise<void> {
   try {
-    const p = handleMessage(db, wallet, sender, req.type, req.detail);
+    const p = handleMessage(wallet.db, wallet, sender, req.type, req.detail);
     const r = await p;
     try {
       sendResponse(r);
@@ -421,6 +421,38 @@ function clearRateLimitCache() {
   rateLimitCache = {};
 }
 
+
+/**
+ * Currently active wallet instance.  Might be unloaded and
+ * re-instantiated when the database is reset.
+ */
+let currentWallet: Wallet|undefined;
+
+
+async function reinitWallet() {
+  if (currentWallet) {
+    currentWallet.stop();
+    currentWallet = undefined;
+  }
+  chrome.browserAction.setBadgeText({ text: "" });
+  const badge = new ChromeBadge();
+  let db: IDBDatabase;
+  try {
+    db = await openTalerDb();
+  } catch (e) {
+    console.error("could not open database", e);
+    return;
+  }
+  const http = new BrowserHttpLib();
+  const notifier = new ChromeNotifier();
+  console.log("setting wallet");
+  const wallet = new Wallet(db, http, badge, notifier);
+  // Useful for debugging in the background page.
+  (window as any).talerWallet = wallet;
+  currentWallet = wallet;
+}
+
+
 /**
  * Main function to run for the WebExtension backend.
  *
@@ -438,9 +470,6 @@ export async function wxMain() {
     logging.record("error", m + error, undefined, source || "(unknown)", 
lineno || 0, colno || 0);
   };
 
-  chrome.browserAction.setBadgeText({ text: "" });
-  const badge = new ChromeBadge();
-
   chrome.tabs.query({}, (tabs) => {
     for (const tab of tabs) {
       if (!tab.url || !tab.id) {
@@ -514,29 +543,28 @@ export async function wxMain() {
 
   chrome.extension.getBackgroundPage().setInterval(clearRateLimitCache, 5000);
 
-  let db: IDBDatabase;
-  try {
-    db = await openTalerDb();
-  } catch (e) {
-    console.error("could not open database", e);
-    return;
-  }
-  const http = new BrowserHttpLib();
-  const notifier = new ChromeNotifier();
-  console.log("setting wallet");
-  const wallet = new Wallet(db, http, badge!, notifier);
-  // Useful for debugging in the background page.
-  (window as any).talerWallet = wallet;
+  reinitWallet();
 
   // Handlers for messages coming directly from the content
   // script on the page
   chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
-    dispatch(db, wallet, req, sender, sendResponse);
+    const wallet = currentWallet;
+    if (!wallet) {
+      console.warn("wallet not available while handling message");
+      console.warn("dropped request message was", req);
+      return;
+    }
+    dispatch(wallet, req, sender, sendResponse);
     return true;
   });
 
+
   // Handlers for catching HTTP requests
   chrome.webRequest.onHeadersReceived.addListener((details) => {
+    const wallet = currentWallet;
+    if (!wallet) {
+      console.warn("wallet not available while handling header");
+    }
     if (details.statusCode === 402) {
       console.log(`got 402 from ${details.url}`);
       return handleHttpPayment(details.responseHeaders || [],
@@ -559,9 +587,15 @@ function openTalerDb(): Promise<IDBDatabase> {
   return new Promise<IDBDatabase>((resolve, reject) => {
     const req = indexedDB.open(DB_NAME, DB_VERSION);
     req.onerror = (e) => {
+      console.log("taler database error", e);
       reject(e);
     };
     req.onsuccess = (e) => {
+      req.result.onversionchange = (evt: IDBVersionChangeEvent) => {
+        console.log(`handling live db version change from ${evt.oldVersion} to 
${evt.newVersion}`);
+        req.result.close();
+        reinitWallet();
+      };
       resolve(req.result);
     };
     req.onupgradeneeded = (e) => {

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]