Path: blob/main/devices/src/virtio/console/sys/windows.rs
5394 views
// Copyright 2022 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::collections::VecDeque;5use std::io;6use std::sync::Arc;7use std::thread;8use std::time::Duration;910use base::error;11use base::named_pipes;12use base::Event;13use base::FileSync;14use base::RawDescriptor;15use base::WorkerThread;16use sync::Mutex;1718use crate::serial_device::SerialInput;19use crate::serial_device::SerialOptions;20use crate::virtio::console::Console;21use crate::virtio::ProtectionType;22use crate::SerialDevice;2324impl SerialDevice for Console {25fn new(26protection_type: ProtectionType,27_event: Event,28_input: Option<Box<dyn SerialInput>>,29out: Option<Box<dyn io::Write + Send>>,30// TODO(b/171331752): connect filesync functionality.31_sync: Option<Box<dyn FileSync + Send>>,32options: SerialOptions,33keep_rds: Vec<RawDescriptor>,34) -> Console {35Console::new(36protection_type,37None,38out,39keep_rds,40options.pci_address,41options.max_queue_sizes,42)43}4445/// Constructs a console with named pipe as input/output connections.46fn new_with_pipe(47protection_type: ProtectionType,48_interrupt_evt: Event,49pipe_in: named_pipes::PipeConnection,50pipe_out: named_pipes::PipeConnection,51options: SerialOptions,52keep_rds: Vec<RawDescriptor>,53) -> Console {54Console::new(55protection_type,56Some(Box::new(pipe_in)),57Some(Box::new(pipe_out)),58keep_rds,59options.pci_address,60options.max_queue_sizes,61)62}63}6465/// Platform-specific function to add a delay for reading rx.66///67/// We can't issue blocking reads here and overlapped I/O is68/// incompatible with the call site where writes to this pipe are being69/// made, so instead we issue a small wait to prevent us from hogging70/// the CPU. This 20ms delay while typing doesn't seem to be noticeable.71fn read_delay_if_needed() {72thread::sleep(Duration::from_millis(20));73}7475fn is_a_fatal_input_error(e: &io::Error) -> bool {76!matches!(77e.kind(),78// Being interrupted is not an error.79io::ErrorKind::Interrupted |80// Ignore two kinds of errors on Windows.81// - ErrorKind::Other when reading a named pipe before a client connects.82// - ErrorKind::WouldBlock when reading a named pipe (we don't use non-blocking I/O).83io::ErrorKind::Other | io::ErrorKind::WouldBlock84)85// Everything else is a fatal input error.86}8788/// Starts a thread that reads rx and sends the input back via the returned buffer.89///90/// The caller should listen on `in_avail_evt` for events. When `in_avail_evt` signals that data91/// is available, the caller should lock the returned `Mutex` and read data out of the inner92/// `VecDeque`. The data should be removed from the beginning of the `VecDeque` as it is processed.93///94/// # Arguments95///96/// * `rx` - Data source that the reader thread will wait on to send data back to the buffer97/// * `in_avail_evt` - Event triggered by the thread when new input is available on the buffer98pub(in crate::virtio::console) fn spawn_input_thread(99mut rx: Box<named_pipes::PipeConnection>,100in_avail_evt: Event,101input_buffer: Arc<Mutex<VecDeque<u8>>>,102) -> WorkerThread<Box<named_pipes::PipeConnection>> {103WorkerThread::start("v_console_input", move |kill_evt| {104// If there is already data, signal immediately.105if !input_buffer.lock().is_empty() {106in_avail_evt.signal().unwrap();107}108109match rx.wait_for_client_connection_overlapped_blocking(&kill_evt) {110Err(e) if e.kind() == io::ErrorKind::Interrupted => return rx,111Err(e) => panic!("failed to wait for client: {e}"),112Ok(()) => (),113}114115read_input(&mut rx, &in_avail_evt, input_buffer, kill_evt);116rx117})118}119120fn read_input(121rx: &mut Box<named_pipes::PipeConnection>,122thread_in_avail_evt: &Event,123buffer: Arc<Mutex<VecDeque<u8>>>,124kill_evt: Event,125) {126let buffer_max_size = 1 << 12;127let mut rx_buf = Vec::with_capacity(buffer_max_size);128129let mut read_overlapped =130named_pipes::OverlappedWrapper::new(true).expect("failed to create OverlappedWrapper");131loop {132let size = rx133.get_available_byte_count()134.expect("failed to get available byte count") as usize;135// Clamp to [1, buffer capacity]. Need to read at least one byte so that the read call136// blocks until data is available.137let size = std::cmp::min(std::cmp::max(size, 1), buffer_max_size);138rx_buf.resize(size, Default::default());139let res = rx.read_overlapped_blocking(&mut rx_buf, &mut read_overlapped, &kill_evt);140141match res {142Ok(()) => {143buffer.lock().extend(&rx_buf[..]);144thread_in_avail_evt.signal().unwrap();145}146Err(e) if e.kind() == io::ErrorKind::Interrupted => {147// Exit event triggered148break;149}150Err(e) => {151// Being interrupted is not an error, but everything else is.152if is_a_fatal_input_error(&e) {153error!(154"failed to read for bytes to queue into console device: {}",155e156);157break;158}159}160}161162// Depending on the platform, a short sleep is needed here (ie. Windows).163read_delay_if_needed();164}165}166167168