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::marker::PhantomData; 6 use std::mem; 7 use std::pin::Pin; 8 use std::ptr::null_mut; 9 use std::sync::atomic::AtomicI32; 10 use std::sync::atomic::Ordering; 11 use std::sync::mpsc::channel; 12 use std::sync::mpsc::Sender; 13 use std::sync::Arc; 14 use std::thread::Builder as ThreadBuilder; 15 use std::thread::JoinHandle; 16 17 use anyhow::anyhow; 18 use anyhow::bail; 19 use anyhow::Context; 20 use anyhow::Result; 21 use base::error; 22 use base::info; 23 use base::warn; 24 use base::Event; 25 use base::Tube; 26 use euclid::size2; 27 use sync::Mutex; 28 #[cfg(feature = "kiwi")] 29 use vm_control::ServiceSendToGpu; 30 use win_util::win32_wide_string; 31 use winapi::shared::minwindef::LPARAM; 32 use winapi::shared::minwindef::LRESULT; 33 use winapi::shared::minwindef::UINT; 34 use winapi::shared::minwindef::WPARAM; 35 use winapi::shared::windef::HWND; 36 use winapi::um::errhandlingapi::GetLastError; 37 use winapi::um::processthreadsapi::GetCurrentThreadId; 38 use winapi::um::winuser::*; 39 40 #[cfg(feature = "kiwi")] 41 use super::message_relay_thread::MessageRelayThread; 42 use super::message_relay_thread::MessageRelayThreadTrait; 43 use super::thread_message_util; 44 use super::window::MessagePacket; 45 use super::window::Window; 46 use super::window_message_dispatcher::WindowMessageDispatcher; 47 use super::window_message_dispatcher::DISPATCHER_PROPERTY_NAME; 48 use super::window_message_processor::*; 49 50 // The default app icon id, which is defined in crosvm-manifest.rc. 51 const APP_ICON_ID: u16 = 1; 52 53 #[derive(Debug)] 54 enum MessageLoopState { 55 /// The initial state. 56 NotStarted = 0, 57 /// The loop is running normally. 58 Running, 59 /// The loop has ended normally. 60 ExitedNormally, 61 /// The loop never started because errors occurred. 62 EarlyTerminatedWithError, 63 /// The loop has ended because errors occurred. 64 ExitedWithError, 65 } 66 67 /// This class runs the WndProc thread, and provides helper functions for other threads to 68 /// communicate with it. 69 pub struct WindowProcedureThread<T: HandleWindowMessage> { 70 thread: Option<JoinHandle<()>>, 71 thread_id: u32, 72 message_loop_state: Option<Arc<AtomicI32>>, 73 thread_terminated_event: Event, 74 _marker: PhantomData<T>, 75 } 76 77 impl<T: HandleWindowMessage> WindowProcedureThread<T> { start_thread(vm_tube: Option<Arc<Mutex<Tube>>>) -> Result<Self>78 pub fn start_thread(vm_tube: Option<Arc<Mutex<Tube>>>) -> Result<Self> { 79 let (thread_id_sender, thread_id_receiver) = channel(); 80 let message_loop_state = Arc::new(AtomicI32::new(MessageLoopState::NotStarted as i32)); 81 let thread_terminated_event = Event::new().unwrap(); 82 83 let message_loop_state_clone = Arc::clone(&message_loop_state); 84 let thread_terminated_event_clone = thread_terminated_event 85 .try_clone() 86 .map_err(|e| anyhow!("Failed to clone thread_terminated_event: {}", e))?; 87 88 let thread = match ThreadBuilder::new() 89 .name("gpu_display_wndproc".into()) 90 .spawn(move || { 91 // Safe because GetCurrentThreadId has no failure mode. 92 let thread_id = unsafe { GetCurrentThreadId() }; 93 94 // Must be called before any other threads post messages to the WndProc thread. 95 thread_message_util::force_create_message_queue(); 96 97 // Safe because the message queue has been created, and the returned thread will go 98 // out of scope and get dropped before the WndProc thread exits. 99 let _message_relay_thread = unsafe { 100 vm_tube.and_then(|tube| Self::start_message_relay_thread(tube, thread_id)) 101 }; 102 103 Self::run_message_loop(thread_id_sender, message_loop_state_clone); 104 105 if let Err(e) = thread_terminated_event_clone.signal() { 106 error!("Failed to signal thread terminated event: {}", e); 107 } 108 }) { 109 Ok(thread) => thread, 110 Err(e) => bail!("Failed to spawn WndProc thread: {}", e), 111 }; 112 113 match thread_id_receiver.recv() { 114 Ok(thread_id_res) => match thread_id_res { 115 Ok(thread_id) => Ok(Self { 116 thread: Some(thread), 117 thread_id, 118 message_loop_state: Some(message_loop_state), 119 thread_terminated_event, 120 _marker: PhantomData, 121 }), 122 Err(e) => bail!("WndProc internal failure: {:?}", e), 123 }, 124 Err(e) => bail!("Failed to receive WndProc thread ID: {}", e), 125 } 126 } 127 try_clone_thread_terminated_event(&self) -> Result<Event>128 pub fn try_clone_thread_terminated_event(&self) -> Result<Event> { 129 self.thread_terminated_event 130 .try_clone() 131 .map_err(|e| anyhow!("Failed to clone thread_terminated_event: {}", e)) 132 } 133 post_display_command(&self, message: DisplaySendToWndProc<T>) -> Result<()>134 pub fn post_display_command(&self, message: DisplaySendToWndProc<T>) -> Result<()> { 135 if !self.is_message_loop_running() { 136 bail!("Host window has been destroyed!"); 137 } 138 139 // Safe because the WndProc thread is still running the message loop. 140 unsafe { 141 thread_message_util::post_message_carrying_object( 142 self.thread_id, 143 WM_USER_HANDLE_DISPLAY_MESSAGE_INTERNAL, 144 message, 145 ) 146 .context("When posting DisplaySendToWndProc message") 147 } 148 } 149 run_message_loop(thread_id_sender: Sender<Result<u32>>, message_loop_state: Arc<AtomicI32>)150 fn run_message_loop(thread_id_sender: Sender<Result<u32>>, message_loop_state: Arc<AtomicI32>) { 151 // Safe because the dispatcher will take care of the lifetime of the `Window` object. 152 let create_window_res = unsafe { Self::create_window() }; 153 match create_window_res.and_then(|window| WindowMessageDispatcher::<T>::create(window)) { 154 Ok(dispatcher) => { 155 info!("WndProc thread entering message loop"); 156 message_loop_state.store(MessageLoopState::Running as i32, Ordering::SeqCst); 157 if let Err(e) = thread_id_sender.send(Ok(unsafe { GetCurrentThreadId() })) { 158 error!("Failed to send WndProc thread ID: {}", e); 159 } 160 Self::run_message_loop_body(dispatcher, message_loop_state); 161 } 162 Err(e) => { 163 error!("WndProc thread didn't enter message loop: {:?}", e); 164 message_loop_state.store( 165 MessageLoopState::EarlyTerminatedWithError as i32, 166 Ordering::SeqCst, 167 ); 168 if let Err(e) = thread_id_sender.send(Err(e)) { 169 error!("Failed to report message loop early termination: {}", e) 170 } 171 } 172 } 173 } 174 run_message_loop_body( mut message_dispatcher: Pin<Box<WindowMessageDispatcher<T>>>, message_loop_state: Arc<AtomicI32>, )175 fn run_message_loop_body( 176 mut message_dispatcher: Pin<Box<WindowMessageDispatcher<T>>>, 177 message_loop_state: Arc<AtomicI32>, 178 ) { 179 loop { 180 let mut message = mem::MaybeUninit::uninit(); 181 // Safe because we know the lifetime of `message`. 182 match unsafe { GetMessageW(message.as_mut_ptr(), null_mut(), 0, 0) } { 183 0 => { 184 info!("WndProc thread exiting message loop since WM_QUIT is received"); 185 message_loop_state 186 .store(MessageLoopState::ExitedNormally as i32, Ordering::SeqCst); 187 break; 188 } 189 -1 => { 190 error!( 191 "WndProc thread exiting message loop because GetMessageW() failed with \ 192 error code {}", 193 unsafe { GetLastError() } 194 ); 195 message_loop_state 196 .store(MessageLoopState::ExitedWithError as i32, Ordering::SeqCst); 197 break; 198 } 199 _ => (), 200 } 201 202 // Safe because `GetMessageW()` will block until `message` is populated. 203 let new_message = unsafe { message.assume_init() }; 204 if new_message.hwnd.is_null() { 205 // Thread messages don't target a specific window and `DispatchMessageW()` won't 206 // send them to `wnd_proc()` function, hence we need to handle it as a special case. 207 message_dispatcher 208 .as_mut() 209 .process_thread_message(&new_message.into()); 210 } else { 211 // Safe because `GetMessageW()` will block until `message` is populated. 212 unsafe { 213 TranslateMessage(&new_message); 214 DispatchMessageW(&new_message); 215 } 216 } 217 } 218 } 219 is_message_loop_running(&self) -> bool220 fn is_message_loop_running(&self) -> bool { 221 self.message_loop_state.as_ref().map_or(false, |state| { 222 state.load(Ordering::SeqCst) == MessageLoopState::Running as i32 223 }) 224 } 225 226 /// In the normal case, when all windows are closed by the user, the WndProc thread exits the 227 /// message loop and terminates naturally. If we have to shutdown the VM before all windows are 228 /// closed because of errors, this function will post a message to let the WndProc thread kill 229 /// all windows and terminate. signal_exit_message_loop_if_needed(&self)230 fn signal_exit_message_loop_if_needed(&self) { 231 if !self.is_message_loop_running() { 232 return; 233 } 234 235 info!("WndProc thread is still in message loop before dropping. Signaling killing windows"); 236 // Safe because the WndProc thread is still running the message loop. 237 if let Err(e) = unsafe { 238 thread_message_util::post_message( 239 self.thread_id, 240 WM_USER_WNDPROC_THREAD_DROP_KILL_WINDOW_INTERNAL, 241 /* w_param */ 0, 242 /* l_param */ 0, 243 ) 244 } { 245 error!("Failed to signal WndProc thread to kill windows: {:?}", e); 246 } 247 } 248 249 /// Checks if the message loop has exited normally. This should be called after joining with the 250 /// WndProc thread. check_message_loop_final_state(&mut self)251 fn check_message_loop_final_state(&mut self) { 252 match Arc::try_unwrap(self.message_loop_state.take().unwrap()) { 253 Ok(state) => { 254 let state = state.into_inner(); 255 if state == MessageLoopState::ExitedNormally as i32 { 256 info!("WndProc thread exited gracefully"); 257 } else { 258 warn!("WndProc thread exited with message loop state: {:?}", state); 259 } 260 } 261 Err(e) => error!( 262 "WndProc thread exited but message loop state retrieval failed: {:?}", 263 e 264 ), 265 } 266 } 267 268 /// # Safety 269 /// The owner of the returned `Window` object is responsible for dropping it before we finish 270 /// processing `WM_NCDESTROY`, because the window handle will become invalid afterwards. create_window() -> Result<Window>271 unsafe fn create_window() -> Result<Window> { 272 // Gfxstream window is a child window of crosvm window. Without WS_CLIPCHILDREN, the parent 273 // window may use the background brush to clear the gfxstream window client area when 274 // drawing occurs. This caused the screen flickering issue during resizing. 275 // See b/197786842 for details. 276 let dw_style = WS_POPUP | WS_CLIPCHILDREN; 277 Window::new( 278 Some(Self::wnd_proc), 279 /* class_name */ "CROSVM", 280 /* title */ "crosvm", 281 APP_ICON_ID, 282 dw_style, 283 // The window size and style can be adjusted later when `Surface` is created. 284 &size2(1, 1), 285 ) 286 } 287 288 /// # Safety 289 /// The message queue must have been created on the WndProc thread before calling this, and the 290 /// returned thread must not outlive the WndProc thread. start_message_relay_thread( #[allow(unused_variables)] vm_tube: Arc<Mutex<Tube>>, #[allow(unused_variables)] wndproc_thread_id: u32, ) -> Option<Box<dyn MessageRelayThreadTrait>>291 unsafe fn start_message_relay_thread( 292 #[allow(unused_variables)] vm_tube: Arc<Mutex<Tube>>, 293 #[allow(unused_variables)] wndproc_thread_id: u32, 294 ) -> Option<Box<dyn MessageRelayThreadTrait>> { 295 #[cfg(feature = "kiwi")] 296 match MessageRelayThread::<ServiceSendToGpu>::start_thread( 297 vm_tube, 298 wndproc_thread_id, 299 WM_USER_HANDLE_SERVICE_MESSAGE_INTERNAL, 300 ) { 301 Ok(thread) => Some(thread), 302 // We won't get messages from the service if we failed to spawn this thread. It may not 303 // worth terminating the WndProc thread and crashing the emulator in that case, so we 304 // just log the error. 305 Err(e) => { 306 error!("{:?}", e); 307 None 308 } 309 } 310 311 #[cfg(not(feature = "kiwi"))] 312 None 313 } 314 wnd_proc( hwnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM, ) -> LRESULT315 unsafe extern "system" fn wnd_proc( 316 hwnd: HWND, 317 msg: UINT, 318 w_param: WPARAM, 319 l_param: LPARAM, 320 ) -> LRESULT { 321 let dispatcher_ptr = GetPropW(hwnd, win32_wide_string(DISPATCHER_PROPERTY_NAME).as_ptr()) 322 as *mut WindowMessageDispatcher<T>; 323 if let Some(dispatcher) = dispatcher_ptr.as_mut() { 324 if let Some(ret) = 325 dispatcher.dispatch_window_message(hwnd, &MessagePacket::new(msg, w_param, l_param)) 326 { 327 return ret; 328 } 329 } 330 DefWindowProcW(hwnd, msg, w_param, l_param) 331 } 332 } 333 334 impl<T: HandleWindowMessage> Drop for WindowProcedureThread<T> { drop(&mut self)335 fn drop(&mut self) { 336 self.signal_exit_message_loop_if_needed(); 337 match self.thread.take().unwrap().join() { 338 Ok(_) => self.check_message_loop_final_state(), 339 Err(e) => error!("Failed to join with WndProc thread: {:?}", e), 340 } 341 } 342 } 343 344 // `Send` may not be automatically inherited because of the `PhantomData`. 345 // Since `WindowProcedureThread` does not hold anything that cannot be transferred between threads, 346 // we can implement `Send` for it. 347 unsafe impl<T: HandleWindowMessage> Send for WindowProcedureThread<T> {} 348