mirror of
https://github.com/chatmail/core.git
synced 2026-10-18 02:43:02 +03:00
fix(deltachat-rpc-client): shutdown all threads after RPC server crash
RPC server subprocess may exit for various reasons,
e.g. because of panic or because of debug_assert! during testing,
or because the process is killed manually or by OOM killer.
We still want to shutdown all threads cleanly in this case
and not fail in Rpc.close(). Otherwise Python process cannot exit
as it waits forever for all threads to terminate.
Without the fix, when added test is run in pytest,
pytest prints "ERROR at teardown of test_shutdown" and gets stuck.
When pytest is manually terminated with ^C, the following error is printed:
```
Exception ignored while joining a thread in _thread._shutdown():
Traceback (most recent call last):
File "/usr/lib/python3.14/threading.py", line 1583, in _shutdown
_thread_shutdown()
KeyboardInterrupt:
```
_thread_shutdown() is the function waiting for all non-daemon threads to finish.
This commit is contained in:
@@ -149,7 +149,10 @@ class Rpc:
|
||||
def close(self) -> None:
|
||||
"""Terminate RPC server process and wait until the reader loop finishes."""
|
||||
self.closing = True
|
||||
self.stop_io_for_all_accounts()
|
||||
# JSON-RPC error may happen if RPC server process has crashed already.
|
||||
# We still want to shutdown all threads in this case.
|
||||
with contextlib.suppress(JsonRpcError):
|
||||
self.stop_io_for_all_accounts()
|
||||
# Let `events_loop` stop cleanly on `closing` before the pipe goes away,
|
||||
# otherwise it might exit through an "RPC server closed" error instead.
|
||||
self.events_thread.join()
|
||||
|
||||
@@ -35,6 +35,19 @@ def test_sleep(rpc) -> None:
|
||||
assert sleep_5_future in pending
|
||||
|
||||
|
||||
def test_shutdown(rpc) -> None:
|
||||
"""Test RPC client shutdown if RPC server process is terminated.
|
||||
|
||||
This is a regression test, at the time of adding it RPC client
|
||||
did not terminate all threads and python process did not exit.
|
||||
Shutting down all threads is tested implicitly by pytest-timeout.
|
||||
"""
|
||||
rpc.process.kill()
|
||||
rpc.process.wait()
|
||||
with pytest.raises(JsonRpcError):
|
||||
rpc.get_system_info()
|
||||
|
||||
|
||||
def test_email_address_validity(rpc) -> None:
|
||||
valid_addresses = [
|
||||
"email@example.com",
|
||||
|
||||
Reference in New Issue
Block a user