URI: 
       tDeviceMgr: clean-up locks a bit - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 7f1c7955dc401c31d1c519bfcb3e92b714afc334
   DIR parent c0b170acb7e32add65284223329137ed8bc7245d
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Wed,  1 Apr 2020 18:31:08 +0200
       
       DeviceMgr: clean-up locks a bit
       
       Diffstat:
         M electrum/plugin.py                  |      23 ++++++++++++++++-------
         M electrum/plugins/coldcard/coldcard… |       3 +--
         M electrum/plugins/digitalbitbox/dig… |       3 +--
         M electrum/plugins/keepkey/keepkey.py |       3 +--
         M electrum/plugins/ledger/ledger.py   |       3 +--
         M electrum/plugins/safe_t/safe_t.py   |       3 +--
         M electrum/plugins/trezor/trezor.py   |       3 +--
       
       7 files changed, 22 insertions(+), 19 deletions(-)
       ---
   DIR diff --git a/electrum/plugin.py b/electrum/plugin.py
       t@@ -349,24 +349,31 @@ class DeviceMgr(ThreadJob):
            This plugin is thread-safe.  Currently only devices supported by
            hidapi are implemented.'''
        
       -    def __init__(self, config):
       +    def __init__(self, config: SimpleConfig):
                ThreadJob.__init__(self)
                # Keyed by xpub.  The value is the device id
       -        # has been paired, and None otherwise.
       +        # has been paired, and None otherwise. Needs self.lock.
                self.xpub_ids = {}  # type: Dict[str, str]
                # A list of clients.  The key is the client, the value is
       -        # a (path, id_) pair.
       +        # a (path, id_) pair. Needs self.lock.
                self.clients = {}  # type: Dict[HardwareClientBase, Tuple[Union[str, bytes], str]]
                # What we recognise.  Each entry is a (vendor_id, product_id)
                # pair.
                self.recognised_hardware = set()
                # Custom enumerate functions for devices we don't know about.
                self.enumerate_func = set()
       -        # For synchronization
       +        # locks: if you need to take multiple ones, acquire them in the order they are defined here!
       +        self._scan_lock = threading.RLock()
                self.lock = threading.RLock()
       -        self.hid_lock = threading.RLock()
       +
                self.config = config
        
       +    def with_scan_lock(func):
       +        def func_wrapper(self: 'DeviceMgr', *args, **kwargs):
       +            with self._scan_lock:
       +                return func(self, *args, **kwargs)
       +        return func_wrapper
       +
            def thread_jobs(self):
                # Thread job to handle device timeouts
                return [self]
       t@@ -449,6 +456,7 @@ class DeviceMgr(ThreadJob):
                self.scan_devices()
                return self.client_lookup(id_)
        
       +    @with_scan_lock
            def client_for_keystore(self, plugin: 'HW_PluginBase', handler: Optional['HardwareHandlerBase'],
                                    keystore: 'Hardware_KeyStore',
                                    force_pair: bool) -> Optional['HardwareClientBase']:
       t@@ -591,14 +599,14 @@ class DeviceMgr(ThreadJob):
                    wallet.save_keystore()
                return info
        
       +    @with_scan_lock
            def _scan_devices_with_hid(self) -> List['Device']:
                try:
                    import hid
                except ImportError:
                    return []
        
       -        with self.hid_lock:
       -            hid_list = hid.enumerate(0, 0)
       +        hid_list = hid.enumerate(0, 0)
        
                devices = []
                for d in hid_list:
       t@@ -619,6 +627,7 @@ class DeviceMgr(ThreadJob):
                                              transport_ui_string='hid'))
                return devices
        
       +    @with_scan_lock
            def scan_devices(self) -> List['Device']:
                self.logger.info("scanning devices...")
        
   DIR diff --git a/electrum/plugins/coldcard/coldcard.py b/electrum/plugins/coldcard/coldcard.py
       t@@ -547,8 +547,7 @@ class ColdcardPlugin(HW_PluginBase):
                # Acquire a connection to the hardware device (via USB)
                devmgr = self.device_manager()
                handler = keystore.handler
       -        with devmgr.hid_lock:
       -            client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
       +        client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
        
                if client is not None:
                    client.ping_check()
   DIR diff --git a/electrum/plugins/digitalbitbox/digitalbitbox.py b/electrum/plugins/digitalbitbox/digitalbitbox.py
       t@@ -754,8 +754,7 @@ class DigitalBitboxPlugin(HW_PluginBase):
            def get_client(self, keystore, force_pair=True):
                devmgr = self.device_manager()
                handler = keystore.handler
       -        with devmgr.hid_lock:
       -            client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
       +        client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
                if client is not None:
                    client.check_device_dialog()
                return client
   DIR diff --git a/electrum/plugins/keepkey/keepkey.py b/electrum/plugins/keepkey/keepkey.py
       t@@ -182,8 +182,7 @@ class KeepKeyPlugin(HW_PluginBase):
            def get_client(self, keystore, force_pair=True) -> Optional['KeepKeyClient']:
                devmgr = self.device_manager()
                handler = keystore.handler
       -        with devmgr.hid_lock:
       -            client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
       +        client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
                # returns the client for a given keystore. can use xpub
                if client:
                    client.used()
   DIR diff --git a/electrum/plugins/ledger/ledger.py b/electrum/plugins/ledger/ledger.py
       t@@ -612,8 +612,7 @@ class LedgerPlugin(HW_PluginBase):
                # All client interaction should not be in the main GUI thread
                devmgr = self.device_manager()
                handler = keystore.handler
       -        with devmgr.hid_lock:
       -            client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
       +        client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
                # returns the client for a given keystore. can use xpub
                #if client:
                #    client.used()
   DIR diff --git a/electrum/plugins/safe_t/safe_t.py b/electrum/plugins/safe_t/safe_t.py
       t@@ -144,8 +144,7 @@ class SafeTPlugin(HW_PluginBase):
            def get_client(self, keystore, force_pair=True) -> Optional['SafeTClient']:
                devmgr = self.device_manager()
                handler = keystore.handler
       -        with devmgr.hid_lock:
       -            client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
       +        client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
                # returns the client for a given keystore. can use xpub
                if client:
                    client.used()
   DIR diff --git a/electrum/plugins/trezor/trezor.py b/electrum/plugins/trezor/trezor.py
       t@@ -176,8 +176,7 @@ class TrezorPlugin(HW_PluginBase):
            def get_client(self, keystore, force_pair=True) -> Optional['TrezorClientBase']:
                devmgr = self.device_manager()
                handler = keystore.handler
       -        with devmgr.hid_lock:
       -            client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
       +        client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
                # returns the client for a given keystore. can use xpub
                if client:
                    client.used()