tblockchain: blockchains_lock needed to write/iterate global dict - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 4360a785add13de55e13437bd722d27b91729344 DIR parent 7dc5665ab1eab75cb6e94c5b4a08499d7638706a HTML Author: SomberNight <somber.night@protonmail.com> Date: Sun, 16 Sep 2018 18:26:40 +0200 blockchain: blockchains_lock needed to write/iterate global dict Diffstat: M electrum/blockchain.py | 21 ++++++++++++++------- M electrum/interface.py | 8 +++++--- M electrum/network.py | 6 ++---- 3 files changed, 21 insertions(+), 14 deletions(-) --- DIR diff --git a/electrum/blockchain.py b/electrum/blockchain.py t@@ -74,6 +74,8 @@ def hash_header(header: dict) -> str: blockchains = {} +blockchains_lock = threading.Lock() + def read_blockchains(config): blockchains[0] = Blockchain(config, 0, None) t@@ -118,7 +120,8 @@ class Blockchain(util.PrintError): return blockchains[self.parent_id] def get_max_child(self) -> Optional[int]: - children = list(filter(lambda y: y.parent_id==self.forkpoint, blockchains.values())) + with blockchains_lock: chains = list(blockchains.values()) + children = list(filter(lambda y: y.parent_id==self.forkpoint, chains)) return max([x.forkpoint for x in children]) if children else None def get_forkpoint(self) -> int: t@@ -237,21 +240,23 @@ class Blockchain(util.PrintError): self.write(parent_data, 0) parent.write(my_data, (forkpoint - parent.forkpoint)*HEADER_SIZE) # store file path - for b in blockchains.values(): + with blockchains_lock: chains = list(blockchains.values()) + for b in chains: b.old_path = b.path() # swap parameters self.parent_id = parent.parent_id; parent.parent_id = parent_id self.forkpoint = parent.forkpoint; parent.forkpoint = forkpoint self._size = parent._size; parent._size = parent_branch_size # move files - for b in blockchains.values(): + for b in chains: if b in [self, parent]: continue if b.old_path != b.path(): self.print_error("renaming", b.old_path, b.path()) os.rename(b.old_path, b.path()) # update pointers - blockchains[self.forkpoint] = self - blockchains[parent.forkpoint] = parent + with blockchains_lock: + blockchains[self.forkpoint] = self + blockchains[parent.forkpoint] = parent def assert_headers_file_available(self, path): if os.path.exists(path): t@@ -417,14 +422,16 @@ class Blockchain(util.PrintError): def check_header(header: dict) -> Optional[Blockchain]: if type(header) is not dict: return None - for b in blockchains.values(): + with blockchains_lock: chains = list(blockchains.values()) + for b in chains: if b.check_header(header): return b return None def can_connect(header: dict) -> Optional[Blockchain]: - for b in blockchains.values(): + with blockchains_lock: chains = list(blockchains.values()) + for b in chains: if b.can_connect(header): return b return None DIR diff --git a/electrum/interface.py b/electrum/interface.py t@@ -510,8 +510,9 @@ class Interface(PrintError): chain = self.blockchain.check_header(bad_header) if not chain: b = forkfun(bad_header) - assert bad not in blockchain.blockchains, (bad, list(blockchain.blockchains.keys())) - blockchain.blockchains[bad] = b + with blockchain.blockchains_lock: + assert bad not in blockchain.blockchains, (bad, list(blockchain.blockchains)) + blockchain.blockchains[bad] = b self.blockchain = b height = b.forkpoint + 1 assert b.forkpoint == bad t@@ -540,7 +541,8 @@ class Interface(PrintError): return True bad, bad_header = height, header - local_max = max([0] + [x.height() for x in blockchain.blockchains.values()]) if 'mock' not in header else float('inf') + with blockchain.blockchains_lock: chains = list(blockchain.blockchains.values()) + local_max = max([0] + [x.height() for x in chains]) if 'mock' not in header else float('inf') height = min(local_max + 1, height - 1) while await iterate(): bad, bad_header = height, header DIR diff --git a/electrum/network.py b/electrum/network.py t@@ -177,7 +177,7 @@ class Network(PrintError): config = {} # Do not use mutables as default values! self.config = SimpleConfig(config) if isinstance(config, dict) else config self.num_server = 10 if not self.config.get('oneserver') else 0 - blockchain.blockchains = blockchain.read_blockchains(self.config) # note: needs self.blockchains_lock + blockchain.blockchains = blockchain.read_blockchains(self.config) self.print_error("blockchains", list(blockchain.blockchains.keys())) self.blockchain_index = config.get('blockchain_index', 0) if self.blockchain_index not in blockchain.blockchains.keys(): t@@ -199,7 +199,6 @@ class Network(PrintError): self.interface_lock = threading.RLock() # <- re-entrant self.callback_lock = threading.Lock() self.recent_servers_lock = threading.RLock() # <- re-entrant - self.blockchains_lock = threading.Lock() self.server_peers = {} # returned by interface (servers that the main interface knows about) self.recent_servers = self.read_recent_servers() # note: needs self.recent_servers_lock t@@ -711,8 +710,7 @@ class Network(PrintError): @with_interface_lock def get_blockchains(self): out = {} - with self.blockchains_lock: - blockchain_items = list(blockchain.blockchains.items()) + with blockchain.blockchains_lock: blockchain_items = list(blockchain.blockchains.items()) for k, b in blockchain_items: r = list(filter(lambda i: i.blockchain==b, list(self.interfaces.values()))) if r: