Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Deadlock when exiting Status context #927

Closed
cjolowicz opened this issue Jan 14, 2021 · 2 comments
Closed

[BUG] Deadlock when exiting Status context #927

cjolowicz opened this issue Jan 14, 2021 · 2 comments

Comments

@cjolowicz
Copy link

cjolowicz commented Jan 14, 2021

Describe the bug

rich appears to run into a deadlock when exiting from a Status or Live context. This appears to be analogous to #90. The analysis in that issue appears to apply here, as well: The refresh thread is joined under lock. This means that the refresh thread may be waiting for the lock, while the main thread holds the lock waiting for the refresh thread to exit.

To Reproduce

import itertools
import random
import time
from rich.console import Console
from rich.spinner import SPINNERS

console = Console()

for spinner in itertools.cycle(SPINNERS):
    with console.status(spinner, spinner=spinner):
        time.sleep(random.uniform(0, 0.1))
    print("  " + spinner)

Output and traceback:

  dots
  dots2
  dots3
  dots4
  dots5
  dots6
  dots7
  dots8
  dots9
  dots10
  dots11
  dots12
  dots8Bit
  line
  line2
^CTraceback (most recent call last):
  File "/home/cjolowicz/Code/github.com/willmcgugan/rich/repro.py", line 14, in <module>
    time.sleep(random.uniform(0, MAX_DURATION))
  File "/home/cjolowicz/Code/github.com/willmcgugan/rich/rich/status.py", line 101, in __exit__
    self.stop()
  File "/home/cjolowicz/Code/github.com/willmcgugan/rich/rich/status.py", line 94, in stop
    self._live.stop()
  File "/home/cjolowicz/Code/github.com/willmcgugan/rich/rich/live.py", line 160, in stop
    self._refresh_thread.join()
  File "/usr/lib64/python3.9/threading.py", line 1033, in join
    self._wait_for_tstate_lock()
  File "/usr/lib64/python3.9/threading.py", line 1049, in _wait_for_tstate_lock
    elif lock.acquire(block, timeout):
KeyboardInterrupt

Platform
What platform (Win/Linux/Mac) are you running on? What terminal software are you using?

  • Linux (Fedora 33)
  • Python 3.9.1
  • Kitty 0.19.3

Diagnose

❯ poetry run python -m rich.diagnose
╭───────────────────────────────────────────────────── <class 'rich.console.Console'> ──────────────────────────────────────────────────────╮
│ A high level console interface.                                                                                                           │
│                                                                                                                                           │
│     color_system = 'truecolor'                                                                                                            │
│         encoding = 'utf-8'                                                                                                                │
│             file = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>                                                          │
│ is_dumb_terminal = False                                                                                                                  │
│       is_jupyter = False                                                                                                                  │
│      is_terminal = True                                                                                                                   │
│   legacy_windows = False                                                                                                                  │
│          options = ConsoleOptions(legacy_windows=False, min_width=1, max_width=141, is_terminal=True, encoding='utf-8', justify=None,     │
│                    overflow=None, no_wrap=False, highlight=None)                                                                          │
│           record = False                                                                                                                  │
│         safe_box = True                                                                                                                   │
│             size = ConsoleDimensions(width=141, height=77)                                                                                │
│        soft_wrap = False                                                                                                                  │
│           stderr = False                                                                                                                  │
│            style = None                                                                                                                   │
│         tab_size = 8                                                                                                                      │
│            width = 141                                                                                                                    │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
❯ poetry run python -m rich._windows
platform="Linux"
WindowsConsoleFeatures(vt=False, truecolor=False)
❯ poetry run pip freeze | grep rich
rich @ file:///home/cjolowicz/.cache/pypoetry/artifacts/98/1d/5d/051dc7cc639b654ddd087351e285d0c10d3755f771a13803e0860b239a/rich-9.5.1-py3-
@cjolowicz
Copy link
Author

Looks like the following change fixes the deadlock:

diff --git a/rich/live.py b/rich/live.py
index 0c01a36..db714c3 100644
--- a/rich/live.py
+++ b/rich/live.py
@@ -157,7 +157,11 @@ class Live(JupyterMixin, RenderHook):
             try:
                 if self.auto_refresh and self._refresh_thread is not None:
                     self._refresh_thread.stop()
-                    self._refresh_thread.join()
+                    self._lock.release()
+                    try:
+                        self._refresh_thread.join()
+                    finally:
+                        self._lock.acquire()
                     self._refresh_thread = None
                 # allow it to fully render on the last even if overflow
                 self.vertical_overflow = "visible"

@willmcgugan
Copy link
Collaborator

Thanks for the report. You're absolutely right about the deadlock. Fixed in 9.8.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants