commit 4360a785add13de55e13437bd722d27b91729344
parent 7dc5665ab1eab75cb6e94c5b4a08499d7638706a
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:
3 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/electrum/blockchain.py b/electrum/blockchain.py
@@ -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)
@@ -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:
@@ -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):
@@ -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
diff --git a/electrum/interface.py b/electrum/interface.py
@@ -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
@@ -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
diff --git a/electrum/network.py b/electrum/network.py
@@ -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():
@@ -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
@@ -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: