Как заставить поток, заблокированный чтением из файла, возобновить работу в Rust?

Поскольку Rust не имеет встроенной возможности чтения из файла неблокирующим способом, я должен порождать поток, который читает файл /dev/input/fs0 чтобы получить события джойстика. Предположим, что джойстик не используется (нечего читать), поэтому поток чтения блокируется во время чтения из файла.

Есть ли способ для основного потока возобновить блокирующее чтение потока чтения, чтобы поток чтения мог завершиться чисто?

На других языках я бы просто закрыл файл в главном потоке. Это приведет к возобновлению чтения блокировки. Но я не нашел способа сделать это в Rust, потому что чтение требует изменяемой ссылки на файл.

1 ответ

Идея состоит в том, чтобы позвонить File::read только когда есть доступные данные. Если доступных данных нет, мы проверяем флаг, чтобы увидеть, если основной поток запросил остановку. Если нет, попробуйте еще раз.

Вот пример использования неблочного ящика:

extern crate nonblock;

use std::fs::File;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use nonblock::NonBlockingReader;

fn main() {
    let f = File::open("/dev/stdin").expect("open failed");
    let mut reader = NonBlockingReader::from_fd(f).expect("from_fd failed");

    let exit = Arc::new(Mutex::new(false));
    let texit = exit.clone();

    println!("start reading, type something and enter");

    thread::spawn(move || {
        let mut buf: Vec<u8> = Vec::new();
        while !*texit.lock().unwrap() {
            let s = reader.read_available(&mut buf).expect("io error");
            if s == 0 {
                if reader.is_eof() {
                    println!("eof");
                    break;
                }
            } else {
                println!("read {:?}", buf);
                buf.clear();
            }
            thread::sleep(Duration::from_millis(200));
        }
        println!("stop reading");
    });

    thread::sleep(Duration::from_secs(5));

    println!("closing file");
    *exit.lock().unwrap() = true;

    thread::sleep(Duration::from_secs(2));
    println!("\"stop reading\" was printed before the main exit!");
}

fn read_async<F>(file: File, fun: F) -> thread::JoinHandle<()>
    where F: Send + 'static + Fn(&Vec<u8>)
{
    let mut reader = NonBlockingReader::from_fd(file).expect("from_fd failed");
    let mut buf: Vec<u8> = Vec::new();
    thread::spawn(move || {
        loop {
            let s = reader.read_available(&mut buf).expect("io error");
            if s == 0 {
                if reader.is_eof() {
                    break;
                }
            } else {
                fun(&buf);
                buf.clear();
            }
            thread::sleep(Duration::from_millis(100));
        }
    })
}

Вот пример использования poll привязка ящика Функция pool ожидает (с тайм-аутом) определенных событий:

extern crate nix;

use std::io::Read;
use std::os::unix::io::AsRawFd;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use nix::poll;

fn main() {
    let mut f = std::fs::File::open("/dev/stdin").expect("open failed");
    let mut pfd = poll::PollFd {
        fd: f.as_raw_fd(),
        events: poll::POLLIN, // is there input data?
        revents: poll::EventFlags::empty(),
    };

    let exit = Arc::new(Mutex::new(false));
    let texit = exit.clone();

    println!("start reading, type something and enter");

    thread::spawn(move || {
        let timeout = 100; // millisecs
        let mut s = unsafe { std::slice::from_raw_parts_mut(&mut pfd, 1) };
        let mut buffer = [0u8; 10];
        loop {
            if poll::poll(&mut s, timeout).expect("poll failed") != 0 {
                let s = f.read(&mut buffer).expect("read failed");
                println!("read {:?}", &buffer[..s]);
            }
            if *texit.lock().unwrap() {
                break;
            }
        }
        println!("stop reading");
    });

    thread::sleep(Duration::from_secs(5));

    println!("closing file");
    *exit.lock().unwrap() = true;

    thread::sleep(Duration::from_secs(2));
    println!("\"stop reading\" was printed before the main exit!");

}
Другие вопросы по тегам