From c5b17ac637edbfbb85d4613f640b79e0311812fe Mon Sep 17 00:00:00 2001 From: Eric Dvorsak Date: Wed, 22 Aug 2018 15:00:10 +0200 Subject: [PATCH] [fix #5599] add semaphore for recurring events :sync-wallet-transaction and :sync-state are self firing events that dispatch themselves later. this commit introduces semaphores to prevent these event-loops from starting multiple times when the user logs out and logs in again. Signed-off-by: Eric Dvorsak --- src/status_im/models/transactions.cljs | 42 +++++++++++-------- src/status_im/protocol/handlers.cljs | 40 +++++------------- src/status_im/protocol/models.cljs | 40 ++++++++++++++++++ src/status_im/ui/screens/db.cljs | 6 ++- src/status_im/ui/screens/events.cljs | 7 ++-- .../ui/screens/wallet/send/events.cljs | 6 +-- src/status_im/utils/semaphores.cljs | 13 ++++++ 7 files changed, 97 insertions(+), 57 deletions(-) create mode 100644 src/status_im/protocol/models.cljs create mode 100644 src/status_im/utils/semaphores.cljs diff --git a/src/status_im/models/transactions.cljs b/src/status_im/models/transactions.cljs index 5f50ce58df..8e44e2620b 100644 --- a/src/status_im/models/transactions.cljs +++ b/src/status_im/models/transactions.cljs @@ -4,6 +4,7 @@ [status-im.utils.ethereum.tokens :as tokens] [status-im.utils.ethereum.core :as ethereum] [status-im.utils.handlers-macro :as handlers-macro] + [status-im.utils.semaphores :as semaphores] [taoensso.timbre :as log])) (def sync-interval-ms 15000) @@ -88,21 +89,28 @@ (< sync-interval-ms (- (time/timestamp) last-updated-at))))) -(defn set-sync-started [{:keys [db]}] - {:db (assoc-in db [:wallet :transactions-sync-started?] true)}) +(defn sync + "Fetch updated data for any unconfirmed transactions or incoming chat transactions missing in wallet + and schedule new recurring sync request" + [{:keys [db] :as cofx}] + (if (:account/account db) + (let [in-progress? (get-in db [:wallet :transactions-loading?]) + {:keys [app-state network-status]} db] + (if (and (not= network-status :offline) + (= app-state "active") + (not in-progress?) + (time-to-sync? cofx) + (or (have-unconfirmed-transactions? cofx) + (have-missing-chat-transactions? cofx))) + (handlers-macro/merge-fx cofx + (run-update) + (schedule-sync)) + (schedule-sync cofx))) + {:db (semaphores/free :sync-wallet-transactions? cofx)})) -; Fetch updated data for any unconfirmed transactions or incoming chat transactions missing in wallet -; and schedule new recurring sync request -(defn sync [{:keys [db] :as cofx}] - (let [in-progress? (get-in db [:wallet :transactions-loading?]) - {:keys [app-state network-status]} db] - (if (and (not= network-status :offline) - (= app-state "active") - (not in-progress?) - (time-to-sync? cofx) - (or (have-unconfirmed-transactions? cofx) - (have-missing-chat-transactions? cofx))) - (handlers-macro/merge-fx cofx - (run-update) - (schedule-sync)) - (schedule-sync cofx)))) +(defn start-sync [cofx] + (when-not (semaphores/locked? :sync-wallet-transactions? cofx) + (handlers-macro/merge-fx cofx + (load-missing-chat-transactions) + (semaphores/lock :sync-wallet-transactions?) + (sync)))) diff --git a/src/status_im/protocol/handlers.cljs b/src/status_im/protocol/handlers.cljs index 39edbf3441..35ec3152fb 100644 --- a/src/status_im/protocol/handlers.cljs +++ b/src/status_im/protocol/handlers.cljs @@ -5,13 +5,13 @@ [status-im.native-module.core :as status] [status-im.utils.utils :as utils] [status-im.utils.datetime :as datetime] - [status-im.utils.ethereum.core :as ethereum-utils] [status-im.utils.handlers :as handlers] [status-im.utils.handlers-macro :as handlers-macro] [status-im.utils.web3-provider :as web3-provider] [status-im.transport.core :as transport] [status-im.transport.inbox :as transport.inbox] - [status-im.utils.ethereum.core :as ethereum])) + [status-im.utils.ethereum.core :as ethereum] + [status-im.protocol.models :as models])) ;;;; COFX (re-frame/reg-cofx @@ -71,35 +71,15 @@ (handlers/register-handler-db :update-sync-state - (fn [{:keys [sync-state sync-data] :as db} [_ error sync]] - (let [{:keys [highestBlock currentBlock] :as state} - (js->clj sync :keywordize-keys true) - syncing? (> (- highestBlock currentBlock) constants/blocks-per-hour) - new-state (cond - error :offline - syncing? (if (= sync-state :done) - :pending - :in-progress) - :else (if (or (= sync-state :done) - (= sync-state :pending)) - :done - :synced))] - (cond-> db - (when (and (not= sync-data state) (= :in-progress new-state))) - (assoc :sync-data state) - (when (not= sync-state new-state)) - (assoc :sync-state new-state))))) + (fn [cofx [_ error sync]] + (models/update-sync-state cofx error sync))) (handlers/register-handler-fx - :check-sync - (fn [{{:keys [web3]} :db} _] - {::web3-get-syncing web3 - :dispatch-later [{:ms 10000 :dispatch [:check-sync]}]})) + :check-sync-state + (fn [cofx _] + (models/check-sync-state cofx))) (handlers/register-handler-fx - :initialize-sync-listener - (fn [{{:keys [sync-listening-started network account/account] :as db} :db} _] - (when (and (not sync-listening-started) - (not (ethereum-utils/network-with-upstream-rpc? (get-in account [:networks network])))) - {:db (assoc db :sync-listening-started true) - :dispatch [:check-sync]}))) + :start-check-sync-state + (fn [cofx _] + (models/start-check-sync-state cofx))) diff --git a/src/status_im/protocol/models.cljs b/src/status_im/protocol/models.cljs new file mode 100644 index 0000000000..7284fa0d66 --- /dev/null +++ b/src/status_im/protocol/models.cljs @@ -0,0 +1,40 @@ +(ns status-im.protocol.models + (:require [status-im.constants :as constants] + [status-im.utils.semaphores :as semaphores] + [status-im.utils.ethereum.core :as ethereum] + [status-im.utils.handlers-macro :as handlers-macro])) + +(defn update-sync-state + [{:keys [sync-state sync-data] :as db} error sync] + (let [{:keys [highestBlock currentBlock] :as state} + (js->clj sync :keywordize-keys true) + syncing? (> (- highestBlock currentBlock) constants/blocks-per-hour) + new-state (cond + error :offline + syncing? (if (= sync-state :done) + :pending + :in-progress) + :else (if (or (= sync-state :done) + (= sync-state :pending)) + :done + :synced))] + (cond-> db + (and (not= sync-data state) (= :in-progress new-state)) + (assoc :sync-data state) + (not= sync-state new-state) + (assoc :sync-state new-state)))) + +(defn check-sync-state + [{{:keys [web3] :as db} :db :as cofx}] + (if (:account/account db) + {::web3-get-syncing web3 + :dispatch-later [{:ms 10000 :dispatch [:check-sync-state]}]} + (semaphores/free :check-sync-state? cofx))) + +(defn start-check-sync-state + [{{:keys [network account/account] :as db} :db :as cofx}] + (when (and (not (semaphores/locked? :check-sync-state? cofx)) + (not (ethereum/network-with-upstream-rpc? (get-in account [:networks network])))) + (handlers-macro/merge-fx cofx + {:dispatch [:check-sync-state]} + (semaphores/lock :check-sync-state?)))) diff --git a/src/status_im/ui/screens/db.cljs b/src/status_im/ui/screens/db.cljs index d8689fc4f4..f880115101 100644 --- a/src/status_im/ui/screens/db.cljs +++ b/src/status_im/ui/screens/db.cljs @@ -41,6 +41,7 @@ :peers-count 0 :peers-summary [] :notifications {} + :semaphores #{} :network constants/default-network :networks/networks constants/default-networks :inbox/wnodes constants/default-wnodes @@ -84,7 +85,6 @@ ;;;;NODE -(spec/def ::sync-listening-started (spec/nilable boolean?)) (spec/def ::sync-state (spec/nilable #{:pending :in-progress :synced :done :offline})) (spec/def ::sync-data (spec/nilable map?)) @@ -169,6 +169,8 @@ ; Shows that push notification used to start the application is processed (spec/def :push-notifications/initial? (spec/nilable boolean?)) +(spec/def ::semaphores set?) + (spec/def ::db (allowed-keys :opt [:contacts/contacts @@ -241,12 +243,12 @@ ::mailserver-status ::peers-count ::peers-summary - ::sync-listening-started ::sync-state ::sync-data ::network ::chain ::app-state + ::semaphores :navigation/view-id :navigation/navigation-stack :navigation/prev-tab-view-id diff --git a/src/status_im/ui/screens/events.cljs b/src/status_im/ui/screens/events.cljs index b3e78557ba..9467a196af 100644 --- a/src/status_im/ui/screens/events.cljs +++ b/src/status_im/ui/screens/events.cljs @@ -298,7 +298,7 @@ (fn [{:keys [accounts/accounts accounts/create contacts/contacts networks/networks network network-status peers-count peers-summary view-id navigation-stack status-module-initialized? status-node-started? device-UUID - push-notifications/initial?] + push-notifications/initial? semaphores] :or [network (get app-db :network)]} [_ address]] (let [console-contact (get contacts constants/console-chat-id) current-account (accounts address) @@ -319,7 +319,8 @@ :push-notifications/initial? initial? :peers-summary peers-summary :peers-count peers-count - :device-UUID device-UUID) + :device-UUID device-UUID + :semaphores semaphores) console-contact (assoc :contacts/contacts {constants/console-chat-id console-contact}))))) @@ -329,7 +330,7 @@ {:dispatch-n (cond-> [[:initialize-account-db address] [:initialize-protocol address] [:fetch-web3-node-version] - [:initialize-sync-listener] + [:start-check-sync-state] [:load-contacts] [:initialize-chats] [:initialize-browsers] diff --git a/src/status_im/ui/screens/wallet/send/events.cljs b/src/status_im/ui/screens/wallet/send/events.cljs index f42153f749..b413484b3a 100644 --- a/src/status_im/ui/screens/wallet/send/events.cljs +++ b/src/status_im/ui/screens/wallet/send/events.cljs @@ -273,8 +273,4 @@ (handlers/register-handler-fx :start-wallet-transactions-sync (fn [cofx _] - (when-not (get-in cofx [:db :wallet :transactions-sync-started?]) - (handlers-macro/merge-fx cofx - (wallet.transactions/load-missing-chat-transactions) - (wallet.transactions/sync) - (wallet.transactions/set-sync-started))))) + (wallet.transactions/start-sync cofx))) diff --git a/src/status_im/utils/semaphores.cljs b/src/status_im/utils/semaphores.cljs new file mode 100644 index 0000000000..732788f3e8 --- /dev/null +++ b/src/status_im/utils/semaphores.cljs @@ -0,0 +1,13 @@ +(ns status-im.utils.semaphores) + +(defn lock [semaphore {:keys [db]}] + {:pre [(keyword? semaphore)]} + {:db (update db :semaphores conj semaphore)}) + +(defn free [semaphore {:keys [db]}] + {:pre [(keyword? semaphore)]} + (update db :semaphores disj semaphore)) + +(defn locked? [semaphore cofx] + {:pre [(keyword? semaphore)]} + ((get-in cofx [:db :semaphores]) semaphore))