[Refactor] Rename ThreadSafeDictionary with AtomicDictionary, add AtomicArray implementation #4611

pull/4612/head
Krypto Pank 2 years ago
parent 2892e389bd
commit 2d9088eed3
  1. 2
      AlphaWallet/Activities/ActivitiesService.swift
  2. 2
      AlphaWallet/Core/Coordinators/WalletBalance/WalletBalanceFetcher.swift
  3. 2
      AlphaWallet/Core/Coordinators/WalletBalance/WalletBalanceService.swift
  4. 2
      AlphaWallet/Core/NFT/Enjin/Enjin.swift
  5. 2
      AlphaWallet/Core/NFT/OpenSea/OpenSea.swift
  6. 2
      AlphaWallet/Core/RealmLocalStore.swift
  7. 6
      AlphaWallet/Core/Types/ThreadSafeDictionary.swift
  8. 2
      AlphaWallet/Covalent/TransactionProviders/PendingTransaction/PendingTransactionProvider.swift
  9. 2
      AlphaWallet/Extensions/Date.swift
  10. 2
      AlphaWallet/Extensions/UIView.swift
  11. 2
      AlphaWallet/TokenScriptClient/Models/CallForAssetAttributeCoordinator.swift
  12. 4
      AlphaWallet/TokenScriptClient/Models/XMLHandler.swift
  13. 4
      AlphaWallet/Tokens/Helpers/CallSmartContractFunction.swift
  14. 2
      AlphaWallet/Tokens/Logic/GetBlockTimestamp.swift
  15. 4
      AlphaWallet/UI/TokenObject+UI.swift
  16. 2
      AlphaWalletTests/Factories/FakeRealmLocalStore.swift
  17. 2
      modules/AlphaWalletAddress/AlphaWalletAddress/AlphaWallet.swift
  18. 280
      modules/AlphaWalletAddress/AlphaWalletAddress/AtomicArray.swift
  19. 6
      modules/AlphaWalletAddress/AlphaWalletAddress/AtomicDictionary.swift

@ -37,7 +37,7 @@ class ActivitiesService: NSObject, ActivitiesServiceType {
private var activitiesIndexLookup: [Int: (index: Int, activity: Activity)] = .init()
private var activities: [Activity] = .init()
private var tokensAndTokenHolders: ThreadSafeDictionary<AlphaWallet.Address, (tokenObject: Activity.AssignedToken, tokenHolders: [TokenHolder])> = .init()
private var tokensAndTokenHolders: AtomicDictionary<AlphaWallet.Address, (tokenObject: Activity.AssignedToken, tokenHolders: [TokenHolder])> = .init()
private var rateLimitedViewControllerReloader: RateLimiter?
private var hasLoadedActivitiesTheFirstTime = false

@ -41,7 +41,7 @@ class WalletBalanceFetcher: NSObject, WalletBalanceFetcherType {
private var timer: Timer?
private let wallet: Wallet
private let assetDefinitionStore: AssetDefinitionStore
private var balanceFetchers: ThreadSafeDictionary<RPCServer, PrivateBalanceFetcherType> = .init()
private var balanceFetchers: AtomicDictionary<RPCServer, PrivateBalanceFetcherType> = .init()
private let queue: DispatchQueue
private let coinTickersFetcher: CoinTickersFetcherType
let tokensDataStore: TokensDataStore

@ -38,7 +38,7 @@ class MultiWalletBalanceService: NSObject, WalletBalanceService {
private let config: Config
let assetDefinitionStore: AssetDefinitionStore
var coinTickersFetcher: CoinTickersFetcherType
private var balanceFetchers: ThreadSafeDictionary<Wallet, WalletBalanceFetcherType> = .init()
private var balanceFetchers: AtomicDictionary<Wallet, WalletBalanceFetcherType> = .init()
private lazy var walletsSummarySubject: CurrentValueSubject<WalletSummary, Never> = {
let balances = balanceFetchers.values.map { $0.value.balance }
let summary = WalletSummary(balances: balances)

@ -18,7 +18,7 @@ typealias EnjinSemiFungiblesToTokenId = [String: GetEnjinTokenQuery.Data.EnjinTo
final class Enjin {
private lazy var networkProvider = EnjinNetworkProvider(queue: queue)
private var cachedPromises: ThreadSafeDictionary<AddressAndRPCServer, Promise<EnjinSemiFungiblesToAddress>> = .init()
private var cachedPromises: AtomicDictionary<AddressAndRPCServer, Promise<EnjinSemiFungiblesToAddress>> = .init()
private let queue: DispatchQueue = DispatchQueue(label: "com.Enjin.UpdateQueue")
typealias EnjinBalances = [GetEnjinBalancesQuery.Data.EnjinBalance]
typealias MappedEnjinBalances = [AlphaWallet.Address: EnjinBalances]

@ -6,7 +6,7 @@ import AlphaWalletOpenSea
final class OpenSea {
private let storage: Storage<[AddressAndRPCServer: OpenSeaNonFungiblesToAddress]> = .init(fileName: "OpenSea", defaultValue: [:])
private var cachedPromises: ThreadSafeDictionary<AddressAndRPCServer, Promise<OpenSeaNonFungiblesToAddress>> = .init()
private var cachedPromises: AtomicDictionary<AddressAndRPCServer, Promise<OpenSeaNonFungiblesToAddress>> = .init()
private let queue: DispatchQueue = DispatchQueue(label: "com.OpenSea.UpdateQueue")
private lazy var networkProvider: OpenSeaNetworkProvider = OpenSeaNetworkProvider(queue: queue)

@ -14,7 +14,7 @@ protocol LocalStore {
}
final class RealmLocalStore: LocalStore {
private var cachedStores: ThreadSafeDictionary<Wallet, RealmStore> = .init()
private var cachedStores: AtomicDictionary<Wallet, RealmStore> = .init()
func getOrCreateStore(forWallet wallet: Wallet) -> RealmStore {
if let store = cachedStores[wallet] {

@ -1,10 +1,10 @@
//
// ThreadSafeDictionary.swift
// AtomicDictionary.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 27.05.2021.
//
import AlphaWalletAddress
///Typealias to avoid importing `ThreadSafeDictionary` for every place where its using
public typealias ThreadSafeDictionary = AlphaWalletAddress.ThreadSafeDictionary
///Typealias to avoid importing `AtomicDictionary` for every place where its using
public typealias AtomicDictionary = AlphaWalletAddress.AtomicDictionary

@ -28,7 +28,7 @@ final class PendingTransactionProvider {
return queue
}()
private lazy var store: ThreadSafeDictionary<String, SchedulerProtocol> = .init()
private lazy var store: AtomicDictionary<String, SchedulerProtocol> = .init()
init(session: WalletSession, transactionDataStore: TransactionDataStore, tokensFromTransactionsFetcher: TokensFromTransactionsFetcher, fetcher: PendingTransactionFetcher) {
self.session = session

@ -9,7 +9,7 @@
import Foundation
public extension Date {
private static var formatsMap: ThreadSafeDictionary<String, DateFormatter> = .init()
private static var formatsMap: AtomicDictionary<String, DateFormatter> = .init()
private static var formatsMapLocale: String?
init?(string: String, format: String) {

@ -26,7 +26,7 @@ extension UIView {
return .init()
}
static var tokenSymbolBackgroundImageCache: ThreadSafeDictionary<UIColor, UIImage> = .init()
static var tokenSymbolBackgroundImageCache: AtomicDictionary<UIColor, UIImage> = .init()
static func tokenSymbolBackgroundImage(backgroundColor: UIColor, contractAddress: AlphaWallet.Address) -> UIImage {
if let cachedValue = tokenSymbolBackgroundImageCache[backgroundColor] {
return cachedValue

@ -8,7 +8,7 @@ import web3swift
///This class temporarily stores the promises used to make function calls. This is so we don't make the same function calls (over network) + arguments combination multiple times concurrently. Once the call completes, we remove it from the cache.
class CallForAssetAttributeCoordinator {
private var promiseCache: ThreadSafeDictionary<AssetFunctionCall, Promise<AssetInternalValue>> = .init()
private var promiseCache: AtomicDictionary<AssetFunctionCall, Promise<AssetInternalValue>> = .init()
func getValue(
forAttributeId attributeId: AttributeId,

@ -698,8 +698,8 @@ fileprivate let threadSafeFoXmlHandler = ThreadSafe(label: "org.alphawallet.swif
public class XMLHandler {
//TODO not the best thing to have, especially because it's an optional
static var callForAssetAttributeCoordinator = CallForAssetAttributeCoordinator()
fileprivate static var xmlHandlers = ThreadSafeDictionary<AlphaWallet.Address, PrivateXMLHandler>()
fileprivate static var baseXmlHandlers = ThreadSafeDictionary<String, PrivateXMLHandler>()
fileprivate static var xmlHandlers = AtomicDictionary<AlphaWallet.Address, PrivateXMLHandler>()
fileprivate static var baseXmlHandlers = AtomicDictionary<String, PrivateXMLHandler>()
private let privateXMLHandler: PrivateXMLHandler
private let baseXMLHandler: PrivateXMLHandler?
private let threadSafe: ThreadSafe = threadSafeFoXmlHandler

@ -8,8 +8,8 @@ import web3swift
//TODO wrap callSmartContract() and cache into a type
// swiftlint:disable private_over_fileprivate
fileprivate var smartContractCallsCache = ThreadSafeDictionary<String, (promise: Promise<[String: Any]>, timestamp: Date)>()
fileprivate var web3s = ThreadSafeDictionary<RPCServer, [TimeInterval: web3]>()
fileprivate var smartContractCallsCache = AtomicDictionary<String, (promise: Promise<[String: Any]>, timestamp: Date)>()
fileprivate var web3s = AtomicDictionary<RPCServer, [TimeInterval: web3]>()
// swiftlint:enable private_over_fileprivate
private let web3Queue: OperationQueue = {

@ -6,7 +6,7 @@ import PromiseKit
import web3swift
class GetBlockTimestamp {
private static var blockTimestampCache = ThreadSafeDictionary<RPCServer, [BigUInt: Promise<Date>]>()
private static var blockTimestampCache = AtomicDictionary<RPCServer, [BigUInt: Promise<Date>]>()
func getBlockTimestamp(_ blockNumber: BigUInt, onServer server: RPCServer) -> Promise<Date> {
var cacheForServer = Self.blockTimestampCache[server] ?? .init()

@ -83,7 +83,7 @@ extension RPCServer {
class RPCServerImageFetcher {
static var instance = RPCServerImageFetcher()
private static var subscribables: ThreadSafeDictionary<Int, Subscribable<Image>> = .init()
private static var subscribables: AtomicDictionary<Int, Subscribable<Image>> = .init()
private static func programmaticallyGenerateIcon(for server: RPCServer) -> Image {
return UIView.tokenSymbolBackgroundImage(backgroundColor: server.blockChainNameColor)
@ -132,7 +132,7 @@ class TokenImageFetcher {
static var instance = TokenImageFetcher()
private static var subscribables: ThreadSafeDictionary<String, Subscribable<TokenImage>> = .init()
private static var subscribables: AtomicDictionary<String, Subscribable<TokenImage>> = .init()
private let queue: DispatchQueue = .global()
private static func programmaticallyGenerateIcon(for contractAddress: AlphaWallet.Address, type: TokenType, server: RPCServer, symbol: String) -> TokenImage? {

@ -15,7 +15,7 @@ fileprivate extension Realm {
}
class FakeRealmLocalStore: LocalStore {
private var cachedStores: ThreadSafeDictionary<Wallet, RealmStore> = .init()
private var cachedStores: AtomicDictionary<Wallet, RealmStore> = .init()
func getOrCreateStore(forWallet wallet: Wallet) -> RealmStore {
if let store = cachedStores[wallet] {

@ -11,7 +11,7 @@ extension AlphaWallet {
public enum Address: Hashable, Codable {
//Computing EIP55 is really slow. Cache needed when we need to create many addresses, like parsing a whole lot of Ethereum event logs
//there is cases when cache accessing from different treads, fro this case we need to use sync access for it
private static var cache: ThreadSafeDictionary<String, AlphaWallet.Address> = .init()
private static var cache: AtomicDictionary<String, AlphaWallet.Address> = .init()
case ethereumAddress(eip55String: String)

@ -0,0 +1,280 @@
//
// AtomicArray.swift
// AlphaWalletAddress
//
// Created by Vladyslav Shepitko on 16.05.2022.
//
import Foundation
// https://developer.apple.com/documentation/swift/rangereplaceablecollection
public struct AtomicArray<T>: RangeReplaceableCollection {
public typealias Element = T
public typealias Index = Int
public typealias SubSequence = AtomicArray<T>
public typealias Indices = Range<Int>
public var array: [T]
public var startIndex: Int { return array.startIndex }
public var endIndex: Int { return array.endIndex }
public var indices: Range<Int> { return array.indices }
public func index(after i: Int) -> Int { return array.index(after: i) }
private var semaphore = DispatchSemaphore(value: 1)
public func _wait() { semaphore.wait() }
public func _signal() { semaphore.signal() }
}
// MARK: - Instance Methods
public extension AtomicArray {
init<S>(_ elements: S) where S: Sequence, AtomicArray.Element == S.Element {
array = Array<S.Element>(elements)
}
init() { self.init([]) }
init(repeating repeatedValue: AtomicArray.Element, count: Int) {
let array = Array(repeating: repeatedValue, count: count)
self.init(array)
}
}
// MARK: - Instance Methods
public extension AtomicArray {
public mutating func append(_ newElement: AtomicArray.Element) {
_wait(); defer { _signal() }
array.append(newElement)
}
public mutating func append<S>(contentsOf newElements: S) where S: Sequence, AtomicArray.Element == S.Element {
_wait(); defer { _signal() }
array.append(contentsOf: newElements)
}
func filter(_ isIncluded: (AtomicArray.Element) throws -> Bool) rethrows -> AtomicArray {
_wait(); defer { _signal() }
let subArray = try array.filter(isIncluded)
return AtomicArray(subArray)
}
public mutating func insert(_ newElement: AtomicArray.Element, at i: AtomicArray.Index) {
_wait(); defer { _signal() }
array.insert(newElement, at: i)
}
mutating func insert<S>(contentsOf newElements: S, at i: AtomicArray.Index) where S: Collection, AtomicArray.Element == S.Element {
_wait(); defer { _signal() }
array.insert(contentsOf: newElements, at: i)
}
mutating func popLast() -> AtomicArray.Element? {
_wait(); defer { _signal() }
return array.popLast()
}
@discardableResult mutating func remove(at i: AtomicArray.Index) -> AtomicArray.Element {
_wait(); defer { _signal() }
return array.remove(at: i)
}
mutating func removeAll() {
_wait(); defer { _signal() }
array.removeAll()
}
mutating func removeAll(keepingCapacity keepCapacity: Bool) {
_wait(); defer { _signal() }
array.removeAll()
}
mutating func removeAll(where shouldBeRemoved: (AtomicArray.Element) throws -> Bool) rethrows {
_wait(); defer { _signal() }
try array.removeAll(where: shouldBeRemoved)
}
@discardableResult mutating func removeFirst() -> AtomicArray.Element {
_wait(); defer { _signal() }
return array.removeFirst()
}
mutating func removeFirst(_ k: Int) {
_wait(); defer { _signal() }
array.removeFirst(k)
}
@discardableResult mutating func removeLast() -> AtomicArray.Element {
_wait(); defer { _signal() }
return array.removeLast()
}
mutating func removeLast(_ k: Int) {
_wait(); defer { _signal() }
array.removeLast(k)
}
@inlinable public func forEach(_ body: (Element) throws -> Void) rethrows {
_wait(); defer { _signal() }
try array.forEach(body)
}
mutating func removeFirstIfExist(where shouldBeRemoved: (AtomicArray.Element) throws -> Bool) {
_wait(); defer { _signal() }
guard let index = try? array.firstIndex(where: shouldBeRemoved), let index = index else { return }
array.remove(at: index)
}
mutating func removeSubrange(_ bounds: Range<Int>) {
_wait(); defer { _signal() }
array.removeSubrange(bounds)
}
mutating func replaceSubrange<C, R>(_ subrange: R, with newElements: C) where C: Collection, R: RangeExpression, T == C.Element, AtomicArray<Element>.Index == R.Bound {
_wait(); defer { _signal() }
array.replaceSubrange(subrange, with: newElements)
}
mutating func reserveCapacity(_ n: Int) {
_wait(); defer { _signal() }
array.reserveCapacity(n)
}
public var count: Int {
_wait(); defer { _signal() }
return array.count
}
public var isEmpty: Bool {
_wait(); defer { _signal() }
return array.isEmpty
}
}
// MARK: - Get/Set
extension AtomicArray {
// Single action
public func get() -> [T] {
_wait(); defer { _signal() }
return array
}
public mutating func set(array: [T]) {
_wait(); defer { _signal() }
self.array = array
}
// Multy actions
public mutating func get(closure: ([T]) -> Void) {
_wait(); defer { _signal() }
closure(array)
}
public mutating func set(closure: ([T]) -> ([T])) {
_wait(); defer { _signal() }
array = closure(array)
}
}
// MARK: - Subscripts
extension AtomicArray {
public subscript(bounds: Range<AtomicArray.Index>) -> AtomicArray.SubSequence {
_wait(); defer { _signal() }
return AtomicArray(array[bounds])
}
public subscript(bounds: AtomicArray.Index) -> AtomicArray.Element {
get {
_wait(); defer { _signal() }
return array[bounds]
}
set(value) {
_wait(); defer { _signal() }
array[bounds] = value
}
}
}
// MARK: - Operator Functions
extension AtomicArray {
static func + <Other>(lhs: Other, rhs: AtomicArray) -> AtomicArray where Other: Sequence, AtomicArray.Element == Other.Element {
return AtomicArray(lhs + rhs.get())
}
static func + <Other>(lhs: AtomicArray, rhs: Other) -> AtomicArray where Other: Sequence, AtomicArray.Element == Other.Element {
return AtomicArray(lhs.get() + rhs)
}
static func + <Other>(lhs: AtomicArray, rhs: Other) -> AtomicArray where Other: RangeReplaceableCollection, AtomicArray.Element == Other.Element {
return AtomicArray(lhs.get() + rhs)
}
static func + (lhs: AtomicArray<Element>, rhs: AtomicArray<Element>) -> AtomicArray {
return AtomicArray(lhs.get() + rhs.get())
}
static func += <Other>(lhs: inout AtomicArray, rhs: Other) where Other: Sequence, AtomicArray.Element == Other.Element {
lhs._wait(); defer { lhs._signal() }
lhs.array += rhs
}
}
// MARK: - CustomStringConvertible
extension AtomicArray: CustomStringConvertible {
public var description: String {
_wait(); defer { _signal() }
return "\(array)"
}
}
// MARK: - Equatable
extension AtomicArray where Element: Equatable {
func split(separator: Element, maxSplits: Int, omittingEmptySubsequences: Bool) -> [ArraySlice<Element>] {
_wait(); defer { _signal() }
return array.split(separator: separator, maxSplits: maxSplits, omittingEmptySubsequences: omittingEmptySubsequences)
}
func firstIndex(of element: Element) -> Int? {
_wait(); defer { _signal() }
return array.firstIndex(of: element)
}
func lastIndex(of element: Element) -> Int? {
_wait(); defer { _signal() }
return array.lastIndex(of: element)
}
func starts<PossiblePrefix>(with possiblePrefix: PossiblePrefix) -> Bool where PossiblePrefix: Sequence, Element == PossiblePrefix.Element {
_wait(); defer { _signal() }
return array.starts(with: possiblePrefix)
}
func elementsEqual<OtherSequence>(_ other: OtherSequence) -> Bool where OtherSequence: Sequence, Element == OtherSequence.Element {
_wait(); defer { _signal() }
return array.elementsEqual(other)
}
func contains(_ element: Element) -> Bool {
_wait(); defer { _signal() }
return array.contains(element)
}
static func != (lhs: AtomicArray<Element>, rhs: AtomicArray<Element>) -> Bool {
lhs._wait(); defer { lhs._signal() }
rhs._wait(); defer { rhs._signal() }
return lhs.array != rhs.array
}
static func == (lhs: AtomicArray<Element>, rhs: AtomicArray<Element>) -> Bool {
lhs._wait(); defer { lhs._signal() }
rhs._wait(); defer { rhs._signal() }
return lhs.array == rhs.array
}
}

@ -1,5 +1,5 @@
//
// ThreadSafeDictionary.swift
// AtomicDictionary.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 27.05.2021.
@ -7,7 +7,7 @@
import Foundation
public class ThreadSafeDictionary<Key: Hashable, Value> {
public class AtomicDictionary<Key: Hashable, Value> {
private var cache = [Key: Value]()
private let queue: DispatchQueue
@ -28,7 +28,7 @@ public class ThreadSafeDictionary<Key: Hashable, Value> {
}
}
public init(queue: DispatchQueue = DispatchQueue(label: "SynchronizedArrayAccess", qos: .background)) {
public init(queue: DispatchQueue = DispatchQueue(label: "org.alphawallet.swift.atomicDictionary", qos: .background)) {
self.queue = queue
}
Loading…
Cancel
Save