Browse Source

blockchain: blockchains_lock needed to write/iterate global dict

master
SomberNight 7 years ago
parent
commit
4360a785ad
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 21
      electrum/blockchain.py
  2. 8
      electrum/interface.py
  3. 6
      electrum/network.py

21
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

8
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

6
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:

Loading…
Cancel
Save