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
This commit is contained in:
@@ -270,7 +270,7 @@ class LNWorker(Logger, NetworkRetryManager[LNPeerAddr]):
|
|||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
if self.listen_server:
|
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)
|
asyncio.run_coroutine_threadsafe(self.taskgroup.cancel_remaining(), self.network.asyncio_loop)
|
||||||
util.unregister_callback(self.on_proxy_changed)
|
util.unregister_callback(self.on_proxy_changed)
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from .util import test_read_write_permissions
|
|||||||
|
|
||||||
def sql(func):
|
def sql(func):
|
||||||
"""wrapper for sql methods"""
|
"""wrapper for sql methods"""
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self: 'SqlDB', *args, **kwargs):
|
||||||
assert threading.currentThread() != self.sql_thread
|
assert threading.currentThread() != self.sql_thread
|
||||||
f = asyncio.Future()
|
f = asyncio.Future()
|
||||||
self.db_requests.put((f, func, args, kwargs))
|
self.db_requests.put((f, func, args, kwargs))
|
||||||
@@ -21,7 +21,7 @@ def sql(func):
|
|||||||
|
|
||||||
class SqlDB(Logger):
|
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)
|
Logger.__init__(self)
|
||||||
self.asyncio_loop = asyncio_loop
|
self.asyncio_loop = asyncio_loop
|
||||||
self.path = path
|
self.path = path
|
||||||
@@ -48,10 +48,10 @@ class SqlDB(Logger):
|
|||||||
try:
|
try:
|
||||||
result = func(self, *args, **kwargs)
|
result = func(self, *args, **kwargs)
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
future.set_exception(e)
|
self.asyncio_loop.call_soon_threadsafe(future.set_exception, e)
|
||||||
continue
|
continue
|
||||||
if not future.cancelled():
|
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
|
# note: in sweepstore session.commit() is called inside
|
||||||
# the sql-decorated methods, so commiting to disk is awaited
|
# the sql-decorated methods, so commiting to disk is awaited
|
||||||
if self.commit_interval:
|
if self.commit_interval:
|
||||||
|
|||||||
Reference in New Issue
Block a user