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