URI: 
       tSqlDB: fix thread-safety issues re asyncio.Future - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 6443bb7d8dce267c9c76e63a033526d3d8afe575
   DIR parent 52f4189176842395c0510ee812fc62b5af2f1ce0
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Tue,  6 Oct 2020 19:24:10 +0200
       
       SqlDB: fix thread-safety issues re asyncio.Future
       
       exceptions below are raised when running python3 with "-X dev":
       
       Traceback (most recent call last):
         File "...\electrum\electrum\util.py", line 999, in run_with_except_hook
           run_original(*args2, **kwargs2)
         File "...\Python38\lib\threading.py", line 870, in run
           self._target(*self._args, **self._kwargs)
         File "...\electrum\electrum\sql_db.py", line 55, in run_sql
           future.set_result(result)
         File "...\Python38\lib\asyncio\base_events.py", line 721, in call_soon
           self._check_thread()
         File "...\Python38\lib\asyncio\base_events.py", line 758, in _check_thread
           raise RuntimeError(
       RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one
       
       Traceback (most recent call last):
         File "...\electrum\electrum\gui\qt\main_window.py", line 3009, in closeEvent
           self.clean_up()  #
         File "...\electrum\electrum\gui\qt\main_window.py", line 3026, in clean_up
           self.gui_object.close_window(self)
         File "...\electrum\electrum\gui\qt\__init__.py", line 340, in close_window
           self.daemon.stop_wallet(window.wallet.storage.path)
         File "...\electrum\electrum\daemon.py", line 518, in stop_wallet
           wallet.stop()
         File "...\electrum\electrum\wallet.py", line 344, in stop
           self.lnworker.stop()
         File "...\electrum\electrum\lnworker.py", line 602, in stop
           super().stop()
         File "...\electrum\electrum\lnworker.py", line 273, in stop
           self.listen_server.close()
         File "...\Python38\lib\asyncio\base_events.py", line 337, in close
           self._loop._stop_serving(sock)
         File "...\Python38\lib\asyncio\proactor_events.py", line 849, in _stop_serving
           future.cancel()
         File "...\Python38\lib\asyncio\windows_events.py", line 80, in cancel
           return super().cancel()
         File "...\Python38\lib\asyncio\base_events.py", line 721, in call_soon
           self._check_thread()
         File "...\Python38\lib\asyncio\base_events.py", line 758, in _check_thread
           raise RuntimeError(
       RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one
       
       Diffstat:
         M electrum/lnworker.py                |       2 +-
         M electrum/sql_db.py                  |       8 ++++----
       
       2 files changed, 5 insertions(+), 5 deletions(-)
       ---
   DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py
       t@@ -270,7 +270,7 @@ class LNWorker(Logger, NetworkRetryManager[LNPeerAddr]):
        
            def stop(self):
                if self.listen_server:
       -            self.listen_server.close()
       +            self.network.asyncio_loop.call_soon_threadsafe(self.listen_server.close)
                asyncio.run_coroutine_threadsafe(self.taskgroup.cancel_remaining(), self.network.asyncio_loop)
                util.unregister_callback(self.on_proxy_changed)
        
   DIR diff --git a/electrum/sql_db.py b/electrum/sql_db.py
       t@@ -11,7 +11,7 @@ from .util import test_read_write_permissions
        
        def sql(func):
            """wrapper for sql methods"""
       -    def wrapper(self, *args, **kwargs):
       +    def wrapper(self: 'SqlDB', *args, **kwargs):
                assert threading.currentThread() != self.sql_thread
                f = asyncio.Future()
                self.db_requests.put((f, func, args, kwargs))
       t@@ -21,7 +21,7 @@ def sql(func):
        
        class SqlDB(Logger):
            
       -    def __init__(self, asyncio_loop, path, commit_interval=None):
       +    def __init__(self, asyncio_loop: asyncio.BaseEventLoop, path, commit_interval=None):
                Logger.__init__(self)
                self.asyncio_loop = asyncio_loop
                self.path = path
       t@@ -48,10 +48,10 @@ class SqlDB(Logger):
                    try:
                        result = func(self, *args, **kwargs)
                    except BaseException as e:
       -                future.set_exception(e)
       +                self.asyncio_loop.call_soon_threadsafe(future.set_exception, e)
                        continue
                    if not future.cancelled():
       -                future.set_result(result)
       +                self.asyncio_loop.call_soon_threadsafe(future.set_result, result)
                    # note: in sweepstore session.commit() is called inside
                    # the sql-decorated methods, so commiting to disk is awaited
                    if self.commit_interval: