Skip to content

use-dev-tty causes panic #755

Closed
Closed
@markus-bauer

Description

@markus-bauer

Description

The new feature use-dev-tty sometimes causes this panic:

panicked at 'polling tty: Poll(Os { code: 4, kind: Interrupted, message: "Interrupted system call" })',
XXX/.cargo/registry/src/.../crossterm-0.26.0/src/event/source/unix/tty.rs:135:28  ...

res => res.expect("polling tty"),

How to reproduce

I have not found a minimal example, and my full code is not public. But I can reliably trigger it with the code below:

[dependencies]
crossterm = {version="0.26.0", features=["event-stream", "use-dev-tty"]}
futures = {version = "0.3"}
tokio = { version = "1", features = ["process", "macros", "rt-multi-thread"]}
use crossterm::event::EventStream;
use futures::{future::FutureExt, stream::StreamExt};
use std::process::Stdio;
use tokio;

#[tokio::main]
async fn main() {
    let mut reader = EventStream::new();

    loop {
        let event = reader.next().fuse();
        tokio::select! {
            maybe_event = event => {
                match maybe_event {
                    Some(Ok(event)) => println!("Event::{:?}\r", event),
                    Some(Err(e)) => println!("Error: {:?}\r", e),
                    None => break,
                }
            }
        };

        // Spawn enough tasks, and the event stream will panic.
        for _ in 0..100 {
            tokio::spawn(async {
                // I don't think the actual command matters.
                tokio::process::Command::new("pwd")
                    // null i/o is not needed, but I want to rule this out.
                    .stdin(Stdio::null())
                    .stdout(Stdio::null())
                    .stderr(Stdio::null())
                    .status()
                    .await
            });
        }
    }
}

Note that this won't panic without the tty feature. And the panic seems to only happen with this combination (task + command). I don't know enough about tokio to be of help here.

There's another problem: When the EventStream panics, it will do it silently (not counting the panic message). At that point it will stop sending events, and the main loop is just blocked forever. Shouldn't the event stream return a Result<Event> to handle cases where the EventSource is found to be dead? In any case, please avoid panics.

Environment

  • Ubuntu 22.10
  • Rust 1.69.0--nightly
  • Terminal doesn't matter

I've also tested this with the current crossterm on github, with the same results.


Update:

I've changed the code to be more like the official example, which first fuses the future. This still doesn't stop the loop from being blocked after panic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions