• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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