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