From 335c55b54ffc53e668cf6f32927dc4145d0c32d1 Mon Sep 17 00:00:00 2001 From: Matt Campbell Date: Sat, 4 Nov 2023 10:03:32 -0500 Subject: [PATCH] fix: Make the test focus event handler thread-safe again --- platforms/windows/src/tests/mod.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/platforms/windows/src/tests/mod.rs b/platforms/windows/src/tests/mod.rs index 0b1f7f8c..6160346a 100644 --- a/platforms/windows/src/tests/mod.rs +++ b/platforms/windows/src/tests/mod.rs @@ -8,7 +8,7 @@ use once_cell::{sync::Lazy as SyncLazy, unsync::Lazy}; use std::{ cell::RefCell, rc::Rc, - sync::{Condvar, Mutex}, + sync::{Arc, Condvar, Mutex}, thread, time::Duration, }; @@ -262,14 +262,19 @@ where }) } +/// This must only be used to wrap UIA elements returned by a UIA client +/// that was created in the MTA. Those are safe to send between threads. +struct SendableUiaElement(IUIAutomationElement); +unsafe impl Send for SendableUiaElement {} + pub(crate) struct ReceivedFocusEvent { - mutex: Mutex>, + mutex: Mutex>, cv: Condvar, } impl ReceivedFocusEvent { - fn new() -> Rc { - Rc::new(Self { + fn new() -> Arc { + Arc::new(Self { mutex: Mutex::new(None), cv: Condvar::new(), }) @@ -281,7 +286,7 @@ impl ReceivedFocusEvent { { let mut received = self.mutex.lock().unwrap(); loop { - if let Some(element) = received.take() { + if let Some(SendableUiaElement(element)) = received.take() { if f(&element) { return element; } @@ -294,26 +299,30 @@ impl ReceivedFocusEvent { fn put(&self, element: IUIAutomationElement) { let mut received = self.mutex.lock().unwrap(); - *received = Some(element); + *received = Some(SendableUiaElement(element)); self.cv.notify_one(); } } #[implement(Windows::Win32::UI::Accessibility::IUIAutomationFocusChangedEventHandler)] pub(crate) struct FocusEventHandler { - received: Rc, + received: Arc, } +// Because we create a UIA client in the COM MTA, this event handler +// _will_ be called from a different thread, and possibly multiple threads +// at once. +static_assertions::assert_impl_all!(FocusEventHandler: Send, Sync); impl FocusEventHandler { #[allow(clippy::new_ret_no_self)] // it does return self, but wrapped pub(crate) fn new() -> ( IUIAutomationFocusChangedEventHandler, - Rc, + Arc, ) { let received = ReceivedFocusEvent::new(); ( Self { - received: received.clone(), + received: Arc::clone(&received), } .into(), received,