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 in process_renderables #1061

Closed
damsgithub opened this issue Feb 25, 2021 · 5 comments
Closed

[BUG] deadlock in process_renderables #1061

damsgithub opened this issue Feb 25, 2021 · 5 comments

Comments

@damsgithub
Copy link

damsgithub commented Feb 25, 2021

Hello, I encounter a random deadlock in process_renderables, https://github.com/willmcgugan/rich/blob/master/rich/live.py at line 242, it cannot acquire the lock;

        with self._lock:
            # determine the control command needed to clear previous rendering
            reset = (
                Control.home()
                if self._alt_screen
                else self._live_render.position_cursor()
            )
            renderables = [
                reset,
                *renderables,
                self._live_render,
            ]

To reproduce use this program based on your examples:

#!/usr/bin/env python3
# -*- coding: utf-8 -*- 

from concurrent.futures import ThreadPoolExecutor

import faulthandler
faulthandler.enable()

import time
from functools import partial
import os.path
import sys
from typing import Iterable
from urllib.request import urlopen
from datetime import datetime

## Rich definitions ##
from rich.table import Table
from rich.layout import Layout
from rich.panel import Panel
from rich.live import Live
from rich.align import Align
from rich.text import Text
from rich.console import Console, ConsoleOptions, RenderResult
console = Console()


from rich.progress import (
    BarColumn,
    DownloadColumn,
    TextColumn,
    TransferSpeedColumn,
    TimeRemainingColumn,
    Progress,
    TaskID,
)

dl_progress = Progress(
    TextColumn("[bold blue]{task.fields[filename]}", justify="right"),
    BarColumn(bar_width=None),
    "[progress.percentage]{task.percentage:>3.1f}%",
    "•",
    DownloadColumn(),
    "•",
    TransferSpeedColumn(),
    "•",
    TimeRemainingColumn(),
)

def make_layout() -> Layout:
    """Define the layout."""
    layout = Layout(name="root")

    layout.split(
        Layout(name="header", size=3),
        Layout(name="main", ratio=1),
    )
    layout["main"].split(
        Layout(name="side"),
        Layout(name="body", ratio=2, minimum_size=60),
        direction="horizontal",
    )
    layout["side"].split(Layout(name="box1"), Layout(name="box2"))
    return layout

layout = make_layout()

class Header:
    """Display header with clock."""

    def __rich__(self) -> Panel:
        grid = Table.grid(expand=True)
        grid.add_column(justify="center", ratio=1)
        grid.add_column(justify="right")
        grid.add_row(
            "[b]downloader[/b]",
            datetime.now().ctime().replace(":", "[blink]:[/]"),
        )
        return Panel(grid, style="white on black")

infos_table = Table(show_header=False)
errors_table = Table(show_header=False)
progress_table = Table.grid(expand=True)
progress_table.add_row(
    Panel(
        dl_progress,
        title="Tracks Progress",
        border_style="green",
        padding=(2, 2),
    ),
)

def copy_url(task_id: TaskID, url: str, path: str) -> None:
    """Copy data from a url to a local file."""
    response = urlopen(url)
    # This will break if the response doesn't contain content length
    process_id = os.getpid()
    
    dl_progress.update(task_id, total=int(response.info()["Content-length"]))
    with open(path, "wb") as dest_file:
        dl_progress.start_task(task_id)
        for data in iter(partial(response.read, 32768), b""):
            dest_file.write(data)
            dl_progress.update(task_id, advance=len(data))
            try:
                errors_table.add_row("[green] OK")
                infos_table.add_row("[red] KO")
                layout["box1"].update(Panel(infos_table))
                layout["box2"].update(Panel(errors_table))
            except Exception as e:
                console.print_exception()


def download(url, dest_dir: str):
    """Download multuple files to the given directory."""
    with ThreadPoolExecutor(max_workers=4) as pool:
        test_fn = 0
        while True:
            # time.sleep(2) # THIS MITIGATES THE DEADLOCK
            filename = url.split("/")[-1] + str(test_fn)
            dest_path = os.path.join(dest_dir, filename)
            task_id = dl_progress.add_task("download", filename=filename, start=False)
            pool.submit(copy_url, task_id, url, dest_path)
            test_fn += 1

def main():
    try:
        layout["header"].update(Header())
        layout["body"].update(progress_table)
        layout["box1"].update(Panel(infos_table))
        layout["box2"].update(Panel(errors_table))

        with Live(layout, refresh_per_second=2):
            download("http://ovh.net/files/1Mio.dat", "./")

    except Exception as e:
        console.print_exception()


if __name__ == "__main__":
    main()

$ kill -SIGABRT PID
Trace:
File "/home/localadmin/.local/lib/python3.6/site-packages/rich/live.py", line 242 in process_renderables
File "/home/localadmin/.local/lib/python3.6/site-packages/rich/console.py", line 1402 in print
File "/home/localadmin/.local/lib/python3.6/site-packages/rich/live.py", line 228 in refresh
File "/home/localadmin/.local/lib/python3.6/site-packages/rich/progress.py", line 848 in refresh
File "/home/localadmin/.local/lib/python3.6/site-packages/rich/progress.py", line 935 in add_task
File "./deadlock.py", line 122 in download
File "./deadlock.py", line 134 in main
File "./deadlock.py", line 141 in
Aborted

@damsgithub
Copy link
Author

If I comment "with self._lock:", there is no more deadlock without any other issue

@damsgithub
Copy link
Author

An other different problem that arise with the program above, is that the average transfert speed disappear from the task once it is finished

@willmcgugan
Copy link
Collaborator

Please try v9.12.1

@willmcgugan
Copy link
Collaborator

Should be fix. Re-open if you still have a problem.

@damsgithub
Copy link
Author

thanks, it seems to correct the problem.

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

No branches or pull requests

2 participants