From 1200632989ffcf78086053f96f0d2c5a225fcd57 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Thu, 18 Mar 2021 19:23:15 -0500 Subject: [PATCH] feat: remove all remaining spawnAndSends refactor: move threadpool task declarations inline with views Co-authored-by: Michael Bradley Jr. Co-authored-by: Eric Mastro --- src/app/chat/view.nim | 105 ++++++++--- src/app/chat/views/stickers.nim | 62 +++++- src/app/profile/view.nim | 1 - src/app/profile/views/contacts.nim | 28 ++- src/app/profile/views/ens_manager.nim | 104 ++++++---- src/app/provider/view.nim | 2 +- src/app/wallet/core.nim | 2 +- src/app/wallet/view.nim | 261 +++++++++++++++++--------- src/app/wallet/views/token_list.nim | 52 +++-- src/nim_status_client.nim | 9 +- src/status/status.nim | 15 +- src/status/tasks/common.nim | 20 +- src/status/tasks/qt.nim | 16 ++ src/status/tasks/stickers.nim | 50 ----- src/status/tasks/task_manager.nim | 27 --- src/status/tasks/task_runner_impl.nim | 24 +++ src/status/tasks/threadpool.nim | 118 ++++++------ src/status/threads.nim | 12 -- src/status/wallet.nim | 27 +-- 19 files changed, 551 insertions(+), 384 deletions(-) create mode 100644 src/status/tasks/qt.nim delete mode 100644 src/status/tasks/stickers.nim delete mode 100644 src/status/tasks/task_manager.nim create mode 100644 src/status/tasks/task_runner_impl.nim delete mode 100644 src/status/threads.nim diff --git a/src/app/chat/view.nim b/src/app/chat/view.nim index 4bb33e2f0f..e80876fae2 100644 --- a/src/app/chat/view.nim +++ b/src/app/chat/view.nim @@ -14,18 +14,87 @@ import ../../status/ens as status_ens import ../../status/chat/[chat, message] import ../../status/profile/profile import web3/[conversions, ethtypes] -import ../../status/threads import views/[channels_list, message_list, chat_item, suggestions_list, reactions, stickers, groups, transactions, communities, community_list, community_item] -import json_serialization import ../utils/image_utils +import ../../status/tasks/[qt, task_runner_impl] logScope: topics = "chats-view" - type ChatViewRoles {.pure.} = enum MessageList = UserRole + 1 + GetLinkPreviewDataTaskArg = ref object of QObjectTaskArg + link: string + uuid: string + AsyncMessageLoadTaskArg = ref object of QObjectTaskArg + chatId: string + ResolveEnsTaskArg = ref object of QObjectTaskArg + ens: string + +const getLinkPreviewDataTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[GetLinkPreviewDataTaskArg](argEncoded) + var success: bool + # We need to call directly on libstatus because going through the status model is not thread safe + let + response = libstatus_chat.getLinkPreviewData(arg.link, success) + responseJson = %* { "result": %response, "success": %success, "uuid": %arg.uuid } + arg.finish(responseJson) + +proc getLinkPreviewData[T](self: T, slot: string, link: string, uuid: string) = + let arg = GetLinkPreviewDataTaskArg( + tptr: cast[ByteAddress](getLinkPreviewDataTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + link: link, + uuid: uuid + ) + self.status.tasks.threadpool.start(arg) + +const asyncMessageLoadTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[AsyncMessageLoadTaskArg](argEncoded) + var messages: JsonNode + var msgCallSuccess: bool + let msgCallResult = rpcChatMessages(arg.chatId, newJString(""), 20, msgCallSuccess) + if(msgCallSuccess): + messages = msgCallResult.parseJson()["result"] + + var reactions: JsonNode + var reactionsCallSuccess: bool + let reactionsCallResult = rpcReactions(arg.chatId, newJString(""), 20, reactionsCallSuccess) + if(reactionsCallSuccess): + reactions = reactionsCallResult.parseJson()["result"] + + let responseJson = %*{ + "chatId": arg.chatId, + "messages": messages, + "reactions": reactions + } + arg.finish(responseJson) + +proc asyncMessageLoad[T](self: T, slot: string, chatId: string) = + let arg = AsyncMessageLoadTaskArg( + tptr: cast[ByteAddress](asyncMessageLoadTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + chatId: chatId + ) + self.status.tasks.threadpool.start(arg) + +const resolveEnsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let + arg = decode[ResolveEnsTaskArg](argEncoded) + result = status_ens.pubkey(arg.ens) + arg.finish(result) + +proc resolveEns[T](self: T, slot: string, ens: string) = + let arg = ResolveEnsTaskArg( + tptr: cast[ByteAddress](asyncMessageLoadTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + ens: ens + ) + self.status.tasks.threadpool.start(arg) QtObject: type @@ -487,11 +556,7 @@ QtObject: self.linkPreviewDataWasReceived(previewData) proc getLinkPreviewData*(self: ChatsView, link: string, uuid: string) {.slot.} = - spawnAndSend(self, "linkPreviewDataReceived") do: - var success: bool - # We need to call directly on libstatus because going through the status model is not thread safe - let response = libstatus_chat.getLinkPreviewData(link, success) - $(%* { "result": %response, "success": %success, "uuid": %uuid }) + self.getLinkPreviewData("linkPreviewDataReceived", link, uuid) proc joinChat*(self: ChatsView, channel: string, chatTypeInt: int): int {.slot.} = self.status.chat.join(channel, ChatType(chatTypeInt)) @@ -524,26 +589,7 @@ QtObject: proc loadingMessagesChanged*(self: ChatsView, value: bool) {.signal.} proc asyncMessageLoad*(self: ChatsView, chatId: string) {.slot.} = - spawnAndSend(self, "asyncMessageLoaded") do: # Call self.ensResolved(string) when ens is resolved - - var messages: JsonNode - var msgCallSuccess: bool - let msgCallResult = rpcChatMessages(chatId, newJString(""), 20, msgCallSuccess) - if(msgCallSuccess): - messages = msgCallResult.parseJson()["result"] - - var reactions: JsonNode - var reactionsCallSuccess: bool - let reactionsCallResult = rpcReactions(chatId, newJString(""), 20, reactionsCallSuccess) - if(reactionsCallSuccess): - reactions = reactionsCallResult.parseJson()["result"] - - - $(%*{ - "chatId": chatId, - "messages": messages, - "reactions": reactions - }) + self.asyncMessageLoad("asyncMessageLoaded", chatId) proc asyncMessageLoaded*(self: ChatsView, rpcResponse: string) {.slot.} = let rpcResponseObj = rpcResponse.parseJson @@ -677,8 +723,7 @@ QtObject: # Resolving a ENS name proc resolveENS*(self: ChatsView, ens: string) {.slot.} = - spawnAndSend(self, "ensResolved") do: # Call self.ensResolved(string) when ens is resolved - status_ens.pubkey(ens) + self.resolveEns("ensResolved", ens) # Call self.ensResolved(string) when ens is resolved proc ensWasResolved*(self: ChatsView, resolvedPubKey: string) {.signal.} diff --git a/src/app/chat/views/stickers.nim b/src/app/chat/views/stickers.nim index 19e961aaa5..f1c7477b24 100644 --- a/src/app/chat/views/stickers.nim +++ b/src/app/chat/views/stickers.nim @@ -1,15 +1,67 @@ import NimQml, tables, json, chronicles, sets, strutils -import ../../../status/[status, stickers, threads] +import ../../../status/[status, stickers] import ../../../status/libstatus/[types, utils] import ../../../status/libstatus/stickers as status_stickers import ../../../status/libstatus/wallet as status_wallet import sticker_pack_list, sticker_list, chat_item -import json_serialization -import ../../../status/tasks/task_manager +import ../../../status/tasks/[qt, task_runner_impl] logScope: topics = "stickers-view" +type + EstimateTaskArg = ref object of QObjectTaskArg + packId: int + address: string + price: string + uuid: string + ObtainAvailableStickerPacksTaskArg = ref object of QObjectTaskArg + +# The pragmas `{.gcsafe, nimcall.}` in this context do not force the compiler +# to accept unsafe code, rather they work in conjunction with the proc +# signature for `type Task` in tasks/common.nim to ensure that the proc really +# is gcsafe and that a helpful error message is displayed +const estimateTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[EstimateTaskArg](argEncoded) + var success: bool + var estimate = estimateGas(arg.packId, arg.address, arg.price, + success) + if not success: + estimate = 325000 + let tpl: tuple[estimate: int, uuid: string] = (estimate, arg.uuid) + arg.finish(tpl) + +# the [T] here is annoying but the QtObject template only allows for one type +# definition so we'll need to setup the type, task, and helper outside of body +# passed to `QtObject:` +proc estimate[T](self: T, slot: string, packId: int, address: string, price: string, + uuid: string) = + let arg = EstimateTaskArg( + tptr: cast[ByteAddress](estimateTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + packId: packId, + address: address, + price: price, + uuid: uuid + ) + self.status.tasks.threadpool.start(arg) + +const obtainAvailableStickerPacksTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[ObtainAvailableStickerPacksTaskArg](argEncoded) + let availableStickerPacks = status_stickers.getAvailableStickerPacks() + var packs: seq[StickerPack] = @[] + for packId, stickerPack in availableStickerPacks.pairs: + packs.add(stickerPack) + arg.finish(%*(packs)) + +proc obtainAvailableStickerPacks[T](self: T, slot: string) = + let arg = ObtainAvailableStickerPacksTaskArg( + tptr: cast[ByteAddress](obtainAvailableStickerPacksTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot) + self.status.tasks.threadpool.start(arg) + QtObject: type StickersView* = ref object of QObject status: Status @@ -46,7 +98,7 @@ QtObject: proc transactionCompleted*(self: StickersView, success: bool, txHash: string, revertReason: string = "") {.signal.} proc estimate*(self: StickersView, packId: int, address: string, price: string, uuid: string) {.slot.} = - self.status.taskManager.threadPool.stickers.stickerPackPurchaseGasEstimate(cast[pointer](self.vptr), "setGasEstimate", packId, address, price, uuid) + self.estimate("setGasEstimate", packId, address, price, uuid) proc gasEstimateReturned*(self: StickersView, estimate: int, uuid: string) {.signal.} @@ -65,7 +117,7 @@ QtObject: self.transactionWasSent(response) proc obtainAvailableStickerPacks*(self: StickersView) = - self.status.taskManager.threadPool.stickers.obtainAvailableStickerPacks(cast[pointer](self.vptr), "setAvailableStickerPacks") + self.obtainAvailableStickerPacks("setAvailableStickerPacks") proc stickerPacksLoaded*(self: StickersView) {.signal.} diff --git a/src/app/profile/view.nim b/src/app/profile/view.nim index 264bb88f18..c01ff77200 100644 --- a/src/app/profile/view.nim +++ b/src/app/profile/view.nim @@ -10,7 +10,6 @@ import ../../status/libstatus/settings as status_settings import ../../status/status import ../../status/ens as status_ens import ../../status/chat/chat -import ../../status/threads import ../../status/libstatus/types import ../../status/libstatus/accounts/constants as accountConstants import qrcode/qrcode diff --git a/src/app/profile/views/contacts.nim b/src/app/profile/views/contacts.nim index 89e36086de..4640838119 100644 --- a/src/app/profile/views/contacts.nim +++ b/src/app/profile/views/contacts.nim @@ -1,15 +1,35 @@ import NimQml, chronicles, sequtils, sugar, strutils import ../../../status/libstatus/utils as status_utils import ../../../status/status -import ../../../status/threads import ../../../status/chat/chat import contact_list import ../../../status/profile/profile import ../../../status/ens as status_ens +import ../../../status/tasks/[qt, task_runner_impl] logScope: topics = "contacts-view" +type + LookupContactTaskArg = ref object of QObjectTaskArg + value: string + +const lookupContactTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[LookupContactTaskArg](argEncoded) + var id = arg.value + if not id.startsWith("0x"): + id = status_ens.pubkey(id) + arg.finish(id) + +proc lookupContact[T](self: T, slot: string, value: string) = + let arg = LookupContactTaskArg( + tptr: cast[ByteAddress](lookupContactTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + value: value + ) + self.status.tasks.threadpool.start(arg) + QtObject: type ContactsView* = ref object of QObject status: Status @@ -113,11 +133,7 @@ QtObject: if value == "": return - spawnAndSend(self, "ensResolved") do: # Call self.ensResolved(string) when ens is resolved - var id = value - if not id.startsWith("0x"): - id = status_ens.pubkey(id) - id + self.lookupContact("ensResolved", value) proc ensWasResolved*(self: ContactsView, resolvedPubKey: string) {.signal.} diff --git a/src/app/profile/views/ens_manager.nim b/src/app/profile/views/ens_manager.nim index e980dca99f..7acbe62b1e 100644 --- a/src/app/profile/views/ens_manager.nim +++ b/src/app/profile/views/ens_manager.nim @@ -1,11 +1,9 @@ import NimQml import Tables import json -import json_serialization import sequtils import strutils from ../../../status/libstatus/types import Setting, PendingTransactionType, RpcException -import ../../../status/threads import ../../../status/ens as status_ens import ../../../status/libstatus/wallet as status_wallet import ../../../status/libstatus/settings as status_settings @@ -14,11 +12,78 @@ import ../../../status/status import ../../../status/wallet import sets import web3/ethtypes +import ../../../status/tasks/[qt, task_runner_impl] type EnsRoles {.pure.} = enum UserName = UserRole + 1 IsPending = UserRole + 2 + ValidateTaskArg = ref object of QObjectTaskArg + ens: string + isStatus: bool + usernames: seq[string] + DetailsTaskArg = ref object of QObjectTaskArg + username: string + +const validateTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let + arg = decode[ValidateTaskArg](argEncoded) + username = arg.ens & (if(arg.isStatus): status_ens.domain else: "") + var output = "" + if arg.usernames.filter(proc(x: string):bool = x == username).len > 0: + output = "already-connected" + else: + let ownerAddr = status_ens.owner(username) + if ownerAddr == "" and arg.isStatus: + output = "available" + else: + let userPubKey = status_settings.getSetting[string](Setting.PublicKey, "0x0") + let userWallet = status_wallet.getWalletAccounts()[0].address + let pubkey = status_ens.pubkey(arg.ens) + if ownerAddr != "": + if pubkey == "" and ownerAddr == userWallet: + output = "owned" # "Continuing will connect this username with your chat key." + elif pubkey == userPubkey: + output = "connected" + elif ownerAddr == userWallet: + output = "connected-different-key" # "Continuing will require a transaction to connect the username with your current chat key.", + else: + output = "taken" + else: + output = "taken" + arg.finish(output) + +proc validate[T](self: T, slot: string, ens: string, isStatus: bool, usernames: seq[string]) = + let arg = ValidateTaskArg( + tptr: cast[ByteAddress](validateTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + ens: ens, + isStatus: isStatus, + usernames: usernames + ) + self.status.tasks.threadpool.start(arg) + +const detailsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let + arg = decode[DetailsTaskArg](argEncoded) + address = status_ens.address(arg.username) + pubkey = status_ens.pubkey(arg.username) + json = %* { + "ensName": arg.username, + "address": address, + "pubkey": pubkey + } + arg.finish(json) + +proc details[T](self: T, slot: string, username: string) = + let arg = DetailsTaskArg( + tptr: cast[ByteAddress](detailsTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + username: username + ) + self.status.tasks.threadpool.start(arg) QtObject: type EnsManager* = ref object of QAbstractListModel @@ -58,31 +123,7 @@ QtObject: self.ensWasResolved(ensResult) proc validate*(self: EnsManager, ens: string, isStatus: bool) {.slot.} = - let username = ens & (if(isStatus): status_ens.domain else: "") - if self.usernames.filter(proc(x: string):bool = x == username).len > 0: - self.ensResolved("already-connected") - else: - spawnAndSend(self, "ensResolved") do: - let ownerAddr = status_ens.owner(username) - var output = "" - if ownerAddr == "" and isStatus: - output = "available" - else: - let userPubKey = status_settings.getSetting[string](Setting.PublicKey, "0x0") - let userWallet = status_wallet.getWalletAccounts()[0].address - let pubkey = status_ens.pubkey(ens) - if ownerAddr != "": - if pubkey == "" and ownerAddr == userWallet: - output = "owned" # "Continuing will connect this username with your chat key." - elif pubkey == userPubkey: - output = "connected" - elif ownerAddr == userWallet: - output = "connected-different-key" # "Continuing will require a transaction to connect the username with your current chat key.", - else: - output = "taken" - else: - output = "taken" - output + self.validate("ensResolved", ens, isStatus, self.usernames) proc add*(self: EnsManager, username: string) = self.beginInsertRows(newQModelIndex(), self.usernames.len, self.usernames.len) @@ -119,14 +160,7 @@ QtObject: proc details(self: EnsManager, username: string) {.slot.} = self.loading(true) - spawnAndSend(self, "setDetails") do: - let address = status_ens.address(username) - let pubkey = status_ens.pubkey(username) - $(%* { - "ensName": username, - "address": address, - "pubkey": pubkey - }) + self.details("setDetails", username) proc detailsObtained(self: EnsManager, ensName: string, address: string, pubkey: string) {.signal.} diff --git a/src/app/provider/view.nim b/src/app/provider/view.nim index 7e03328de7..ef1746acb6 100644 --- a/src/app/provider/view.nim +++ b/src/app/provider/view.nim @@ -129,7 +129,7 @@ QtObject: var success: bool # TODO make this async - let response = status.wallet.sendTransaction(fromAddress, to, value, selectedGasLimit, selectedGasPrice, password, success, txData) + let response = wallet.sendTransaction(fromAddress, to, value, selectedGasLimit, selectedGasPrice, password, success, txData) let errorMessage = if not success: if response == "": "web3-response-error" diff --git a/src/app/wallet/core.nim b/src/app/wallet/core.nim index 0c18077aac..25a41a07f7 100644 --- a/src/app/wallet/core.nim +++ b/src/app/wallet/core.nim @@ -71,7 +71,7 @@ proc init*(self: WalletController) = self.view.transactionCompleted(tx.success, tx.transactionHash, tx.revertReason) proc checkPendingTransactions*(self: WalletController) = - self.status.wallet.checkPendingTransactions() # TODO: consider doing this in a spawnAndSend + self.status.wallet.checkPendingTransactions() # TODO: consider doing this in a threadpool task proc start*(self: WalletController) = status_wallet.startWallet(false) diff --git a/src/app/wallet/view.nim b/src/app/wallet/view.nim index e72eb2c37d..27b41b9d63 100644 --- a/src/app/wallet/view.nim +++ b/src/app/wallet/view.nim @@ -1,5 +1,5 @@ -import NimQml, Tables, strformat, strutils, chronicles, sequtils, json, std/wrapnils, parseUtils, stint, tables, json_serialization -import ../../status/[status, wallet, threads] +import NimQml, Tables, strformat, strutils, chronicles, sequtils, json, std/wrapnils, parseUtils, stint, tables +import ../../status/[status, wallet] import ../../status/wallet/collectibles as status_collectibles import ../../status/libstatus/accounts/constants import ../../status/libstatus/wallet as status_wallet @@ -10,6 +10,160 @@ import ../../status/libstatus/utils as status_utils import ../../status/libstatus/eth/contracts import ../../status/ens as status_ens import views/[asset_list, account_list, account_item, token_list, transaction_list, collectibles_list] +import ../../status/tasks/[qt, task_runner_impl] + +type + SendTransactionTaskArg = ref object of QObjectTaskArg + from_addr: string + to: string + assetAddress: string + value: string + gas: string + gasPrice: string + password: string + uuid: string + InitBalancesTaskArg = ref object of QObjectTaskArg + address: string + tokenList: seq[string] + LoadCollectiblesTaskArg = ref object of QObjectTaskArg + address: string + collectiblesType: string + GasPredictionsTaskArg = ref object of QObjectTaskArg + LoadTransactionsTaskArg = ref object of QObjectTaskArg + address: string + blockNumber: string + ResolveEnsTaskArg = ref object of QObjectTaskArg + ens: string + uuid: string + +const sendTransactionTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[SendTransactionTaskArg](argEncoded) + var + success: bool + response: string + if arg.assetAddress != ZERO_ADDRESS and not arg.assetAddress.isEmptyOrWhitespace: + response = wallet.sendTokenTransaction(arg.from_addr, arg.to, arg.assetAddress, arg.value, arg.gas, arg.gasPrice, arg.password, success) + else: + response = wallet.sendTransaction(arg.from_addr, arg.to, arg.value, arg.gas, arg.gasPrice, arg.password, success) + let output = %* { "result": %response, "success": %success, "uuid": %arg.uuid } + arg.finish(output) + +proc sendTransaction[T](self: T, slot: string, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) = + let arg = SendTransactionTaskArg( + tptr: cast[ByteAddress](sendTransactionTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + from_addr: from_addr, + to: to, + assetAddress: assetAddress, + value: value, + gas: gas, + gasPrice: gasPrice, + password: password, + uuid: uuid + ) + self.status.tasks.threadpool.start(arg) + +const initBalancesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[InitBalancesTaskArg](argEncoded) + var tokenBalances = initTable[string, string]() + for token in arg.tokenList: + tokenBalances[token] = getTokenBalance(token, arg.address) + let output = %* { + "address": arg.address, + "eth": getEthBalance(arg.address), + "tokens": tokenBalances + } + arg.finish(output) + +proc initBalances[T](self: T, slot: string, address: string, tokenList: seq[string]) = + let arg = InitBalancesTaskArg( + tptr: cast[ByteAddress](initBalancesTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + address: address, + tokenList: tokenList + ) + self.status.tasks.threadpool.start(arg) + +const loadCollectiblesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[LoadCollectiblesTaskArg](argEncoded) + var collectiblesOrError = "" + case arg.collectiblesType: + of status_collectibles.CRYPTOKITTY: + collectiblesOrError = status_collectibles.getCryptoKitties(arg.address) + of status_collectibles.KUDO: + collectiblesOrError = status_collectibles.getKudos(arg.address) + of status_collectibles.ETHERMON: + collectiblesOrError = status_collectibles.getEthermons(arg.address) + of status_collectibles.STICKER: + collectiblesOrError = status_collectibles.getStickers(arg.address) + + let output = %*{ + "address": arg.address, + "collectibleType": arg.collectiblesType, + "collectiblesOrError": collectiblesOrError + } + arg.finish(output) + +proc loadCollectibles[T](self: T, slot: string, address: string, collectiblesType: string) = + let arg = LoadCollectiblesTaskArg( + tptr: cast[ByteAddress](loadCollectiblesTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + address: address, + collectiblesType: collectiblesType, + ) + self.status.tasks.threadpool.start(arg) + +const getGasPredictionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let + arg = decode[GasPredictionsTaskArg](argEncoded) + output = %getGasPricePredictions() + arg.finish(output) + +proc getGasPredictions[T](self: T, slot: string) = + let arg = GasPredictionsTaskArg( + tptr: cast[ByteAddress](getGasPredictionsTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot + ) + self.status.tasks.threadpool.start(arg) + +const loadTransactionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let + arg = decode[LoadTransactionsTaskArg](argEncoded) + output = %*{ + "address": arg.address, + "history": getTransfersByAddress(arg.address, arg.blockNumber) + } + arg.finish(output) + +proc loadTransactions[T](self: T, slot: string, address: string, blockNumber: string) = + let arg = LoadTransactionsTaskArg( + tptr: cast[ByteAddress](loadTransactionsTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + address: address, + blockNumber: blockNumber + ) + self.status.tasks.threadpool.start(arg) + +const resolveEnsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let + arg = decode[ResolveEnsTaskArg](argEncoded) + output = %* { "address": status_ens.address(arg.ens), "uuid": arg.uuid } + arg.finish(output) + +proc resolveEns[T](self: T, slot: string, ens: string, uuid: string) = + let arg = ResolveEnsTaskArg( + tptr: cast[ByteAddress](resolveEnsTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + ens: ens, + uuid: uuid + ) + self.status.tasks.threadpool.start(arg) QtObject: type @@ -60,8 +214,8 @@ QtObject: result.currentAssetList = newAssetList() result.currentTransactions = newTransactionList() result.currentCollectiblesLists = newCollectiblesList() - result.defaultTokenList = newTokenList() - result.customTokenList = newTokenList() + result.defaultTokenList = newTokenList(status) + result.customTokenList = newTokenList(status) result.totalFiatBalance = "" result.etherscanLink = "" result.safeLowGasPrice = "0" @@ -292,17 +446,7 @@ QtObject: self.transactionWasSent(txResult) proc sendTransaction*(self: WalletView, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) {.slot.} = - let wallet = self.status.wallet - if assetAddress != ZERO_ADDRESS and not assetAddress.isEmptyOrWhitespace: - spawnAndSend(self, "transactionSent") do: - var success: bool - let response = wallet.sendTokenTransaction(from_addr, to, assetAddress, value, gas, gasPrice, password, success) - $(%* { "result": %response, "success": %success, "uuid": %uuid }) - else: - spawnAndSend(self, "transactionSent") do: - var success: bool - let response = wallet.sendTransaction(from_addr, to, value, gas, gasPrice, password, success) - $(%* { "result": %response, "success": %success, "uuid": %uuid }) + self.sendTransaction("transactionSent", from_addr, to, assetAddress, value, gas, gasPrice, password, uuid) proc getDefaultAccount*(self: WalletView): string {.slot.} = self.currentAccount.address @@ -364,15 +508,7 @@ QtObject: for acc in self.status.wallet.accounts: let accountAddress = acc.address let tokenList = acc.assetList.filter(proc(x:Asset): bool = x.address != "").map(proc(x: Asset): string = x.address) - spawnAndSend(self, "getAccountBalanceSuccess") do: - var tokenBalances = initTable[string, string]() - for token in tokenList: - tokenBalances[token] = getTokenBalance(token, accountAddress) - $ %* { - "address": accountAddress, - "eth": getEthBalance(accountAddress), - "tokens": tokenBalances - } + self.initBalances("getAccountBalanceSuccess", accountAddress, tokenList) proc getAccountBalanceSuccess*(self: WalletView, jsonResponse: string) {.slot.} = let jsonObj = jsonResponse.parseJson() @@ -396,31 +532,11 @@ QtObject: )) # TODO find a way to use a loop to streamline this code - # Spawn for each collectible. They can end in whichever order - spawnAndSend(self, "setCollectiblesResult") do: - $(%*{ - "address": address, - "collectibleType": status_collectibles.CRYPTOKITTY, - "collectiblesOrError": status_collectibles.getCryptoKitties(address) - }) - spawnAndSend(self, "setCollectiblesResult") do: - $(%*{ - "address": address, - "collectibleType": status_collectibles.KUDO, - "collectiblesOrError": status_collectibles.getKudos(address) - }) - spawnAndSend(self, "setCollectiblesResult") do: - $(%*{ - "address": address, - "collectibleType": status_collectibles.ETHERMON, - "collectiblesOrError": status_collectibles.getEthermons(address) - }) - spawnAndSend(self, "setCollectiblesResult") do: - $(%*{ - "address": address, - "collectibleType": status_collectibles.STICKER, - "collectiblesOrError": status_collectibles.getStickers(address) - }) + # Create a thread in the threadpool for each collectible. They can end in whichever order + self.loadCollectibles("setCollectiblesResult", address, status_collectibles.CRYPTOKITTY) + self.loadCollectibles("setCollectiblesResult", address, status_collectibles.KUDO) + self.loadCollectibles("setCollectiblesResult", address, status_collectibles.ETHERMON) + self.loadCollectibles("setCollectiblesResult", address, status_collectibles.STICKER) proc setCollectiblesResult(self: WalletView, collectiblesJSON: string) {.slot.} = let collectibleData = parseJson(collectiblesJSON) @@ -452,47 +568,13 @@ QtObject: proc reloadCollectible*(self: WalletView, collectibleType: string) {.slot.} = let address = self.currentAccount.address - # TODO find a cooler way to do this - case collectibleType: - of CRYPTOKITTY: - spawnAndSend(self, "setCollectiblesResult") do: - $(%*{ - "address": address, - "collectibleType": status_collectibles.CRYPTOKITTY, - "collectiblesOrError": status_collectibles.getCryptoKitties(address) - }) - of KUDO: - spawnAndSend(self, "setCollectiblesResult") do: - $(%*{ - "address": address, - "collectibleType": status_collectibles.KUDO, - "collectiblesOrError": status_collectibles.getKudos(address) - }) - of ETHERMON: - spawnAndSend(self, "setCollectiblesResult") do: - $(%*{ - "address": address, - "collectibleType": status_collectibles.ETHERMON, - "collectiblesOrError": status_collectibles.getEthermons(address) - }) - of status_collectibles.STICKER: - spawnAndSend(self, "setCollectiblesResult") do: - $(%*{ - "address": address, - "collectibleType": status_collectibles.STICKER, - "collectiblesOrError": status_collectibles.getStickers(address) - }) - else: - error "Unrecognized collectible" - return - + self.loadCollectibles("setCollectiblesResult", address, collectibleType) self.currentCollectiblesLists.setLoadingByType(collectibleType, 1) proc gasPricePredictionsChanged*(self: WalletView) {.signal.} proc getGasPricePredictions*(self: WalletView) {.slot.} = - spawnAndSend(self, "getGasPricePredictionsResult") do: - $ %getGasPricePredictions2() + self.getGasPredictions("getGasPricePredictionsResult") proc getGasPricePredictionsResult(self: WalletView, gasPricePredictionsJson: string) {.slot.} = let prediction = Json.decode(gasPricePredictionsJson, GasPricePrediction) @@ -586,11 +668,7 @@ QtObject: # spawn'ed function cannot have a 'var' parameter let blockNumber = bn self.loadingTrxHistoryChanged(true) - spawnAndSend(self, "setTrxHistoryResult") do: - $(%*{ - "address": address, - "history": getTransfersByAddress(address, blockNumber) - }) + self.loadTransactions("setTrxHistoryResult", address, bn) proc setTrxHistoryResult(self: WalletView, historyJSON: string) {.slot.} = let historyData = parseJson(historyJSON) @@ -605,8 +683,7 @@ QtObject: self.loadingTrxHistoryChanged(false) proc resolveENS*(self: WalletView, ens: string, uuid: string) {.slot.} = - spawnAndSend(self, "ensResolved") do: - $ %* { "address": status_ens.address(ens), "uuid": uuid } + self.resolveEns("ensResolved", ens, uuid) proc ensWasResolved*(self: WalletView, resolvedAddress: string, uuid: string) {.signal.} diff --git a/src/app/wallet/views/token_list.nim b/src/app/wallet/views/token_list.nim index 9173223fc5..1ecc7c203b 100644 --- a/src/app/wallet/views/token_list.nim +++ b/src/app/wallet/views/token_list.nim @@ -1,7 +1,13 @@ -import NimQml, tables, json -import ../../../status/libstatus/[tokens, settings, utils, eth/contracts] +import # nim libs + tables, json + +import # vendor libs + NimQml + +import # status-desktop libs + ../../../status/libstatus/[tokens, settings, utils, eth/contracts], + ../../../status/tasks/[qt, task_runner_impl], ../../../status/status from web3/conversions import `$` -import ../../../status/threads type TokenRoles {.pure.} = enum @@ -11,9 +17,34 @@ type Address = UserRole + 4, Decimals = UserRole + 5 IsCustom = UserRole + 6 + GetTokenDetailsTaskArg = ref object of QObjectTaskArg + address: string + +const getTokenDetailsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let + arg = decode[GetTokenDetailsTaskArg](argEncoded) + tkn = newErc20Contract(getCurrentNetwork(), arg.address.parseAddress) + decimals = tkn.tokenDecimals() + + let output = %* { + "address": arg.address, + "name": tkn.tokenName(), + "symbol": tkn.tokenSymbol(), + "decimals": (if decimals == 0: "" else: $decimals) + } + arg.finish(output) + +proc getTokenDetails[T](self: T, slot: string, address: string) = + let arg = GetTokenDetailsTaskArg( + tptr: cast[ByteAddress](getTokenDetailsTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot, + address: address) + self.status.tasks.threadpool.start(arg) QtObject: type TokenList* = ref object of QAbstractListModel + status*: Status tokens*: seq[Erc20Contract] isCustom*: bool @@ -39,9 +70,10 @@ QtObject: self.isCustom = true self.endResetModel() - proc newTokenList*(): TokenList = + proc newTokenList*(status: Status): TokenList = new(result, delete) result.tokens = @[] + result.status = status result.setup proc rowData(self: TokenList, index: int, column: string): string {.slot.} = @@ -82,17 +114,7 @@ QtObject: TokenRoles.IsCustom.int:"isCustom"}.toTable proc getTokenDetails*(self: TokenList, address: string) {.slot.} = - spawnAndSend(self, "tokenDetailsResolved") do: - let tkn = newErc20Contract(getCurrentNetwork(), address.parseAddress) - - let decimals = tkn.tokenDecimals() - - $ (%* { - "address": address, - "name": tkn.tokenName(), - "symbol": tkn.tokenSymbol(), - "decimals": (if decimals == 0: "" else: $decimals) - }) + self.getTokenDetails("tokenDetailsResolved", address) proc tokenDetailsWereResolved*(self: TokenList, tokenDetails: string) {.signal.} diff --git a/src/nim_status_client.nim b/src/nim_status_client.nim index d089d97a0d..62aa2a73b5 100644 --- a/src/nim_status_client.nim +++ b/src/nim_status_client.nim @@ -10,13 +10,11 @@ import app/onboarding/core as onboarding import app/login/core as login import app/provider/core as provider import status/signals/core as signals -import status/tasks/task_manager import status/libstatus/types import status/libstatus/accounts/constants import status_go import status/status as statuslib import ./eventemitter -import chronos, task_runner var signalsQObjPointer: pointer @@ -30,9 +28,7 @@ proc mainProc() = else: "/../fleets.json" - let taskManager = newTaskManager() - taskManager.init() - let status = statuslib.newStatusInstance(taskManager, readFile(joinPath(getAppDir(), fleets))) + let status = statuslib.newStatusInstance(readFile(joinPath(getAppDir(), fleets))) status.initNode() enableHDPI() @@ -157,7 +153,7 @@ proc mainProc() = profile.delete() utilsController.delete() browserController.delete() - taskManager.teardown() + status.tasks.teardown() # Initialize only controllers whose init functions @@ -205,4 +201,3 @@ proc mainProc() = when isMainModule: mainProc() - GC_fullcollect() diff --git a/src/status/status.nim b/src/status/status.nim index adf604303b..852f366a26 100644 --- a/src/status/status.nim +++ b/src/status/status.nim @@ -4,9 +4,9 @@ import libstatus/settings as libstatus_settings import libstatus/types as libstatus_types import chat, accounts, wallet, node, network, mailservers, messages, contacts, profile, stickers, permissions, fleet import ../eventemitter -import tasks/task_manager +import ./tasks/task_runner_impl -export chat, accounts, node, mailservers, messages, contacts, profile, network, permissions, fleet +export chat, accounts, node, mailservers, messages, contacts, profile, network, permissions, fleet, task_runner_impl type Status* = ref object events*: EventEmitter @@ -22,11 +22,11 @@ type Status* = ref object network*: NetworkModel stickers*: StickersModel permissions*: PermissionsModel - taskManager*: TaskManager + tasks*: TaskRunner -proc newStatusInstance*(taskManager: TaskManager, fleetConfig: string): Status = +proc newStatusInstance*(fleetConfig: string): Status = result = Status() - result.taskManager = taskManager + result.tasks = newTaskRunner() result.events = createEventEmitter() result.fleet = fleet.newFleetModel(result.events, fleetConfig) result.chat = chat.newChatModel(result.events) @@ -42,7 +42,8 @@ proc newStatusInstance*(taskManager: TaskManager, fleetConfig: string): Status = result.stickers = stickers.newStickersModel(result.events) result.permissions = permissions.newPermissionsModel(result.events) -proc initNode*(self: Status) = +proc initNode*(self: Status) = + self.tasks.init() libstatus_accounts.initNode() proc startMessenger*(self: Status) = @@ -51,7 +52,7 @@ proc startMessenger*(self: Status) = proc reset*(self: Status) = # TODO: remove this once accounts are not tracked in the AccountsModel self.accounts.reset() - + # NOT NEEDED self.chat.reset() # NOT NEEDED self.wallet.reset() # NOT NEEDED self.node.reset() diff --git a/src/status/tasks/common.nim b/src/status/tasks/common.nim index 6120b1e5c9..af30cca370 100644 --- a/src/status/tasks/common.nim +++ b/src/status/tasks/common.nim @@ -1,17 +1,13 @@ -import +import # vendor libs json_serialization, NimQml, task_runner type - BaseTasks* = ref object of RootObj - chanSendToPool*: AsyncChannel[ThreadSafeString] - BaseTask* = ref object of RootObj - vptr*: ByteAddress - slot*: string + Task* = proc(arg: string): void {.gcsafe, nimcall.} + TaskArg* = ref object of RootObj + tptr*: ByteAddress -proc start*[T: BaseTask](self: BaseTasks, task: T) = - let payload = task.toJson(typeAnnotations = true) - self.chanSendToPool.sendSync(payload.safe) +proc decode*[T](arg: string): T = + Json.decode(arg, T, allowUnknownFields = true) -proc finish*[T](task: BaseTask, payload: T) = - let resultPayload = Json.encode(payload) - signal_handler(cast[pointer](task.vptr), resultPayload, task.slot) \ No newline at end of file +proc encode*[T](arg: T): string = + arg.toJson(typeAnnotations = true) diff --git a/src/status/tasks/qt.nim b/src/status/tasks/qt.nim new file mode 100644 index 0000000000..06d11ad648 --- /dev/null +++ b/src/status/tasks/qt.nim @@ -0,0 +1,16 @@ +import # vendor libs + NimQml, json_serialization + +import # status-desktop libs + ./common + +type + QObjectTaskArg* = ref object of TaskArg + vptr*: ByteAddress + slot*: string + +proc finish*[T](arg: QObjectTaskArg, payload: T) = + signal_handler(cast[pointer](arg.vptr), Json.encode(payload), arg.slot) + +proc finish*(arg: QObjectTaskArg, payload: string) = + signal_handler(cast[pointer](arg.vptr), payload, arg.slot) diff --git a/src/status/tasks/stickers.nim b/src/status/tasks/stickers.nim deleted file mode 100644 index 557d2cb226..0000000000 --- a/src/status/tasks/stickers.nim +++ /dev/null @@ -1,50 +0,0 @@ -import # nim libs - tables - -import # vendor libs - chronos, NimQml, json, json_serialization, task_runner - -import # status-desktop libs - ./common, ../libstatus/types, ../stickers - -type - StickerPackPurchaseGasEstimate* = ref object of BaseTask - packId*: int - address*: string - price*: string - uuid*: string - ObtainAvailableStickerPacks* = ref object of BaseTask - StickersTasks* = ref object of BaseTasks - -proc newStickersTasks*(chanSendToPool: AsyncChannel[ThreadSafeString]): StickersTasks = - new(result) - result.chanSendToPool = chanSendToPool - -proc run*(task: StickerPackPurchaseGasEstimate) = - var success: bool - var estimate = estimateGas( - task.packId, - task.address, - task.price, - success - ) - if not success: - estimate = 325000 - let result: tuple[estimate: int, uuid: string] = (estimate, task.uuid) - task.finish(result) - -proc run*(task: ObtainAvailableStickerPacks) = - var success: bool - let availableStickerPacks = getAvailableStickerPacks() - var packs: seq[StickerPack] = @[] - for packId, stickerPack in availableStickerPacks.pairs: - packs.add(stickerPack) - task.finish(%*(packs)) - -proc stickerPackPurchaseGasEstimate*(self: StickersTasks, vptr: pointer, slot: string, packId: int, address: string, price: string, uuid: string) = - let task = StickerPackPurchaseGasEstimate(vptr: cast[ByteAddress](vptr), slot: slot, packId: packId, address: address, price: price, uuid: uuid) - self.start(task) - -proc obtainAvailableStickerPacks*(self: StickersTasks, vptr: pointer, slot: string) = - let task = ObtainAvailableStickerPacks(vptr: cast[ByteAddress](vptr), slot: slot) - self.start(task) \ No newline at end of file diff --git a/src/status/tasks/task_manager.nim b/src/status/tasks/task_manager.nim deleted file mode 100644 index b31aeeff12..0000000000 --- a/src/status/tasks/task_manager.nim +++ /dev/null @@ -1,27 +0,0 @@ -import # vendor libs - chronicles, task_runner - -import # status-desktop libs - ./threadpool - -export threadpool - -logScope: - topics = "task-manager" - -type - TaskManager* = ref object - threadPool*: ThreadPool - -proc newTaskManager*(): TaskManager = - new(result) - result.threadPool = newThreadPool() - -proc init*(self: TaskManager) = - self.threadPool.init() - -proc teardown*(self: TaskManager) = - self.threadPool.teardown() - - - diff --git a/src/status/tasks/task_runner_impl.nim b/src/status/tasks/task_runner_impl.nim new file mode 100644 index 0000000000..91727ed1c6 --- /dev/null +++ b/src/status/tasks/task_runner_impl.nim @@ -0,0 +1,24 @@ +import # vendor libs + chronicles, task_runner + +import # status-desktop libs + ./threadpool + +export task_runner, threadpool + +logScope: + topics = "task-runner" + +type + TaskRunner* = ref object + threadpool*: ThreadPool + +proc newTaskRunner*(): TaskRunner = + new(result) + result.threadpool = newThreadPool() + +proc init*(self: TaskRunner) = + self.threadpool.init() + +proc teardown*(self: TaskRunner) = + self.threadpool.teardown() diff --git a/src/status/tasks/threadpool.nim b/src/status/tasks/threadpool.nim index 87282f2c70..25b72bb1f8 100644 --- a/src/status/tasks/threadpool.nim +++ b/src/status/tasks/threadpool.nim @@ -1,26 +1,28 @@ -import - chronicles, chronos, json, json_serialization, NimQml, sequtils, tables, - task_runner +import # std libs + json, sequtils, tables + +import # vendor libs + chronicles, chronos, json_serialization, NimQml, task_runner + +import # status-desktop libs + ./common -import - ./common, ./stickers export - stickers + chronos, common, json_serialization logScope: topics = "task-threadpool" type ThreadPool* = ref object - chanRecvFromPool*: AsyncChannel[ThreadSafeString] - chanSendToPool*: AsyncChannel[ThreadSafeString] + chanRecvFromPool: AsyncChannel[ThreadSafeString] + chanSendToPool: AsyncChannel[ThreadSafeString] thread: Thread[PoolThreadArg] size: int - stickers*: StickersTasks - PoolThreadArg* = object - chanSendToMain*: AsyncChannel[ThreadSafeString] - chanRecvFromMain*: AsyncChannel[ThreadSafeString] - size*: int + PoolThreadArg = object + chanSendToMain: AsyncChannel[ThreadSafeString] + chanRecvFromMain: AsyncChannel[ThreadSafeString] + size: int TaskThreadArg = object id: int chanRecvFromPool: AsyncChannel[ThreadSafeString] @@ -28,7 +30,6 @@ type ThreadNotification = object id: int notice: string - # forward declarations proc poolThread(arg: PoolThreadArg) {.thread.} @@ -41,7 +42,6 @@ proc newThreadPool*(size: int = MaxThreadPoolSize): ThreadPool = result.chanSendToPool = newAsyncChannel[ThreadSafeString](-1) result.thread = Thread[PoolThreadArg]() result.size = size - result.stickers = newStickersTasks(result.chanSendToPool) proc init*(self: ThreadPool) = self.chanRecvFromPool.open() @@ -52,64 +52,60 @@ proc init*(self: ThreadPool) = size: self.size ) createThread(self.thread, poolThread, arg) - # block until we receive "ready" - let received = $(self.chanRecvFromPool.recvSync()) + discard $(self.chanRecvFromPool.recvSync()) proc teardown*(self: ThreadPool) = self.chanSendToPool.sendSync("shutdown".safe) self.chanRecvFromPool.close() self.chanSendToPool.close() + debug "[threadpool] waiting for the control thread to stop" joinThread(self.thread) -proc task(arg: TaskThreadArg) {.async.} = +proc start*[T: TaskArg](self: Threadpool, arg: T) = + self.chanSendToPool.sendSync(arg.encode.safe) + +proc runner(arg: TaskThreadArg) {.async.} = arg.chanRecvFromPool.open() arg.chanSendToPool.open() let noticeToPool = ThreadNotification(id: arg.id, notice: "ready") - info "[threadpool task thread] sending 'ready'", threadid=arg.id - await arg.chanSendToPool.send(noticeToPool.toJson(typeAnnotations = true).safe) + debug "[threadpool task thread] sending 'ready'", threadid=arg.id + await arg.chanSendToPool.send(noticeToPool.encode.safe) while true: - info "[threadpool task thread] waiting for message" + debug "[threadpool task thread] waiting for message" let received = $(await arg.chanRecvFromPool.recv()) if received == "shutdown": - info "[threadpool task thread] received 'shutdown'" - info "[threadpool task thread] breaking while loop" + debug "[threadpool task thread] received 'shutdown'" break let - jsonNode = parseJson(received) - messageType = jsonNode{"$type"}.getStr - - info "[threadpool task thread] received task", messageType=messageType - info "[threadpool task thread] initiating task", messageType=messageType, + parsed = parseJson(received) + messageType = parsed{"$type"}.getStr + debug "[threadpool task thread] initiating task", messageType=messageType, threadid=arg.id try: - case messageType - of "StickerPackPurchaseGasEstimate:ObjectType": - let decoded = Json.decode(received, StickerPackPurchaseGasEstimate, allowUnknownFields = true) - decoded.run() - of "ObtainAvailableStickerPacks:ObjectType": - let decoded = Json.decode(received, ObtainAvailableStickerPacks, allowUnknownFields = true) - decoded.run() - else: - error "[threadpool task thread] unknown message", message=received + let task = cast[Task](parsed{"tptr"}.getInt) + try: + task(received) + except Exception as e: + error "[threadpool task thread] exception", error=e.msg except Exception as e: - error "[threadpool task thread] exception", error=e.msg + error "[threadpool task thread] unknown message", message=received let noticeToPool = ThreadNotification(id: arg.id, notice: "done") - info "[threadpool task thread] sending 'done' notice to pool", + debug "[threadpool task thread] sending 'done' notice to pool", threadid=arg.id - await arg.chanSendToPool.send(noticeToPool.toJson(typeAnnotations = true).safe) + await arg.chanSendToPool.send(noticeToPool.encode.safe) arg.chanRecvFromPool.close() arg.chanSendToPool.close() proc taskThread(arg: TaskThreadArg) {.thread.} = - waitFor task(arg) + waitFor runner(arg) proc pool(arg: PoolThreadArg) {.async.} = let @@ -124,14 +120,14 @@ proc pool(arg: PoolThreadArg) {.async.} = chanSendToMain.open() chanRecvFromMainOrTask.open() - info "[threadpool] sending 'ready' to main thread" + debug "[threadpool] sending 'ready' to main thread" await chanSendToMain.send("ready".safe) for i in 0.. 0: - info "[threadpool] removing from taskQueue", + debug "[threadpool] removing from taskQueue", newlength=(taskQueue.len - 1) task = taskQueue[0] taskQueue.delete 0, 0 - info "[threadpool] removing from threadsIdle", + debug "[threadpool] removing from threadsIdle", newlength=(threadsIdle.len - 1) let tpl = threadsIdle[0] threadsIdle.delete 0, 0 - info "[threadpool] adding to threadsBusy", + debug "[threadpool] adding to threadsBusy", newlength=(threadsBusy.len + 1), threadid=tpl.id threadsBusy.add tpl.id, (tpl.thr, tpl.chanSendToTask) await tpl.chanSendToTask.send(task.safe) @@ -214,7 +208,7 @@ proc pool(arg: PoolThreadArg) {.async.} = else: # must be a request to do task work if allReady < arg.size or threadsBusy.len == arg.size: # add to queue - info "[threadpool] adding to taskQueue", + debug "[threadpool] adding to taskQueue", newlength=(taskQueue.len + 1) taskQueue.add task @@ -223,24 +217,23 @@ proc pool(arg: PoolThreadArg) {.async.} = # check if we have tasks waiting on queue if taskQueue.len > 0: # remove first element from the task queue - info "[threadpool] adding to taskQueue", + debug "[threadpool] adding to taskQueue", newlength=(taskQueue.len + 1) taskQueue.add task - info "[threadpool] removing from taskQueue", + debug "[threadpool] removing from taskQueue", newlength=(taskQueue.len - 1) task = taskQueue[0] taskQueue.delete 0, 0 - info "[threadpool] removing from threadsIdle", + debug "[threadpool] removing from threadsIdle", newlength=(threadsIdle.len - 1) let tpl = threadsIdle[0] threadsIdle.delete 0, 0 - info "[threadpool] adding to threadsBusy", + debug "[threadpool] adding to threadsBusy", newlength=(threadsBusy.len + 1), threadid=tpl.id threadsBusy.add tpl.id, (tpl.thr, tpl.chanSendToTask) await tpl.chanSendToTask.send(task.safe) - var allTaskThreads: seq[Thread[TaskThreadArg]] = @[] for tpl in threadsIdle: @@ -253,6 +246,7 @@ proc pool(arg: PoolThreadArg) {.async.} = chanSendToMain.close() chanRecvFromMainOrTask.close() + debug "[threadpool] waiting for all task threads to stop" joinThreads(allTaskThreads) proc poolThread(arg: PoolThreadArg) {.thread.} = diff --git a/src/status/threads.nim b/src/status/threads.nim deleted file mode 100644 index 57a4232d2e..0000000000 --- a/src/status/threads.nim +++ /dev/null @@ -1,12 +0,0 @@ -import nimqml -import threadpool -import stew/faux_closures - -export fauxClosure - -template spawnAndSend*(view: untyped, signalName: string, exprBlock: untyped) = - let viewPtr = cast[pointer](view.vptr) - proc backgroundTask() {.fauxClosure.} = - let data = exprBlock - signal_handler(viewPtr, data, signalName) - spawn backgroundTask() \ No newline at end of file diff --git a/src/status/wallet.nim b/src/status/wallet.nim index 6d6846288e..95a08f45ee 100644 --- a/src/status/wallet.nim +++ b/src/status/wallet.nim @@ -63,7 +63,7 @@ proc initEvents*(self: WalletModel) = proc delete*(self: WalletModel) = discard -proc buildTokenTransaction(self: WalletModel, source, to, assetAddress: Address, value: float, transfer: var Transfer, contract: var Erc20Contract, gas = "", gasPrice = ""): EthSend = +proc buildTokenTransaction(source, to, assetAddress: Address, value: float, transfer: var Transfer, contract: var Erc20Contract, gas = "", gasPrice = ""): EthSend = contract = getErc20Contract(assetAddress) if contract == nil: raise newException(ValueError, fmt"Could not find ERC-20 contract with address '{assetAddress}' for the current network") @@ -114,7 +114,7 @@ proc estimateTokenGas*(self: WalletModel, source, to, assetAddress, value: strin var transfer: Transfer contract: Erc20Contract - tx = self.buildTokenTransaction( + tx = buildTokenTransaction( parseAddress(source), parseAddress(to), parseAddress(assetAddress), @@ -125,7 +125,7 @@ proc estimateTokenGas*(self: WalletModel, source, to, assetAddress, value: strin result = contract.methods["transfer"].estimateGas(tx, transfer, success) -proc sendTransaction*(self: WalletModel, source, to, value, gas, gasPrice, password: string, success: var bool, data = ""): string = +proc sendTransaction*(source, to, value, gas, gasPrice, password: string, success: var bool, data = ""): string = var tx = transactions.buildTransaction( parseAddress(source), eth2Wei(parseFloat(value), 18), gas, gasPrice, data @@ -138,11 +138,11 @@ proc sendTransaction*(self: WalletModel, source, to, value, gas, gasPrice, passw if success: trackPendingTransaction(result, $source, $to, PendingTransactionType.WalletTransfer, "") -proc sendTokenTransaction*(self: WalletModel, source, to, assetAddress, value, gas, gasPrice, password: string, success: var bool): string = +proc sendTokenTransaction*(source, to, assetAddress, value, gas, gasPrice, password: string, success: var bool): string = var transfer: Transfer contract: Erc20Contract - tx = self.buildTokenTransaction( + tx = buildTokenTransaction( parseAddress(source), parseAddress(to), parseAddress(assetAddress), @@ -333,22 +333,7 @@ proc getTransfersByAddress*(self: WalletModel, address: string): seq[Transaction proc validateMnemonic*(self: WalletModel, mnemonic: string): string = result = status_wallet.validateMnemonic(mnemonic).parseJSON()["error"].getStr -proc getGasPricePredictions*(self: WalletModel): GasPricePrediction = - if status_settings.getCurrentNetwork() != Network.Mainnet: - # TODO: what about other chains like xdai? - return GasPricePrediction(safeLow: 1.0, standard: 2.0, fast: 3.0, fastest: 4.0) - try: - let url: string = fmt"https://etherchain.org/api/gasPriceOracle" - let secureSSLContext = newContext() - let client = newHttpClient(sslContext = secureSSLContext) - client.headers = newHttpHeaders({ "Content-Type": "application/json" }) - let response = client.request(url) - result = Json.decode(response.body, GasPricePrediction) - except Exception as e: - echo "error getting gas price predictions" - echo e.msg - -proc getGasPricePredictions2*(): GasPricePrediction = +proc getGasPricePredictions*(): GasPricePrediction = if status_settings.getCurrentNetwork() != Network.Mainnet: # TODO: what about other chains like xdai? return GasPricePrediction(safeLow: 1.0, standard: 2.0, fast: 3.0, fastest: 4.0)