windows: Remove `Send` and `Sync` implementation of `DirectWrite` (#15263)

张小白 created

This PR uses the `AgileReference` provided by the `windows-rs` crate to
correctly implement `Send` and `Sync` for `DirectWrite`.

Release Notes:

- N/A

Change summary

crates/gpui/src/platform/test/platform.rs        | 32 ++++++++++++++++-
crates/gpui/src/platform/windows/direct_write.rs | 32 ++++++-----------
crates/gpui/src/platform/windows/platform.rs     | 24 ++++++++++--
3 files changed, 59 insertions(+), 29 deletions(-)

Detailed changes

crates/gpui/src/platform/test/platform.rs 🔗

@@ -13,6 +13,11 @@ use std::{
     rc::{Rc, Weak},
     sync::Arc,
 };
+#[cfg(target_os = "windows")]
+use windows::Win32::{
+    Graphics::Imaging::{CLSID_WICImagingFactory, IWICImagingFactory},
+    System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER},
+};
 
 /// TestPlatform implements the Platform trait for use in tests.
 pub(crate) struct TestPlatform {
@@ -28,6 +33,8 @@ pub(crate) struct TestPlatform {
     pub(crate) prompts: RefCell<TestPrompts>,
     pub opened_url: RefCell<Option<String>>,
     pub text_system: Arc<dyn PlatformTextSystem>,
+    #[cfg(target_os = "windows")]
+    bitmap_factory: std::mem::ManuallyDrop<IWICImagingFactory>,
     weak: Weak<Self>,
 }
 
@@ -40,10 +47,14 @@ pub(crate) struct TestPrompts {
 impl TestPlatform {
     pub fn new(executor: BackgroundExecutor, foreground_executor: ForegroundExecutor) -> Rc<Self> {
         #[cfg(target_os = "windows")]
-        unsafe {
+        let bitmap_factory = unsafe {
             windows::Win32::System::Ole::OleInitialize(None)
                 .expect("unable to initialize Windows OLE");
-        }
+            std::mem::ManuallyDrop::new(
+                CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER)
+                    .expect("Error creating bitmap factory."),
+            )
+        };
 
         #[cfg(target_os = "macos")]
         let text_system = Arc::new(crate::platform::mac::MacTextSystem::new());
@@ -52,7 +63,10 @@ impl TestPlatform {
         let text_system = Arc::new(crate::platform::linux::CosmicTextSystem::new());
 
         #[cfg(target_os = "windows")]
-        let text_system = Arc::new(crate::platform::windows::DirectWriteTextSystem::new().unwrap());
+        let text_system = Arc::new(
+            crate::platform::windows::DirectWriteTextSystem::new(&bitmap_factory)
+                .expect("Unable to initialize direct write."),
+        );
 
         Rc::new_cyclic(|weak| TestPlatform {
             background_executor: executor,
@@ -66,6 +80,8 @@ impl TestPlatform {
             current_primary_item: Mutex::new(None),
             weak: weak.clone(),
             opened_url: Default::default(),
+            #[cfg(target_os = "windows")]
+            bitmap_factory,
             text_system,
         })
     }
@@ -303,3 +319,13 @@ impl Platform for TestPlatform {
         unimplemented!()
     }
 }
+
+#[cfg(target_os = "windows")]
+impl Drop for TestPlatform {
+    fn drop(&mut self) {
+        unsafe {
+            std::mem::ManuallyDrop::drop(&mut self.bitmap_factory);
+            windows::Win32::System::Ole::OleUninitialize();
+        }
+    }
+}

crates/gpui/src/platform/windows/direct_write.rs 🔗

@@ -1,4 +1,4 @@
-use std::{borrow::Cow, mem::ManuallyDrop, sync::Arc};
+use std::{borrow::Cow, sync::Arc};
 
 use ::util::ResultExt;
 use anyhow::{anyhow, Result};
@@ -16,9 +16,9 @@ use windows::{
             DirectWrite::*,
             Dxgi::Common::*,
             Gdi::LOGFONTW,
-            Imaging::{D2D::IWICImagingFactory2, *},
+            Imaging::*,
         },
-        System::{Com::*, SystemServices::LOCALE_NAME_MAX_LENGTH},
+        System::SystemServices::LOCALE_NAME_MAX_LENGTH,
         UI::WindowsAndMessaging::*,
     },
 };
@@ -40,7 +40,7 @@ pub(crate) struct DirectWriteTextSystem(RwLock<DirectWriteState>);
 struct DirectWriteComponent {
     locale: String,
     factory: IDWriteFactory5,
-    bitmap_factory: ManuallyDrop<IWICImagingFactory2>,
+    bitmap_factory: AgileReference<IWICImagingFactory>,
     d2d1_factory: ID2D1Factory,
     in_memory_loader: IDWriteInMemoryFontFileLoader,
     builder: IDWriteFontSetBuilder1,
@@ -53,10 +53,6 @@ struct GlyphRenderContext {
     dc_target: ID2D1DeviceContext4,
 }
 
-// All use of the IUnknown methods should be "thread-safe".
-unsafe impl Sync for DirectWriteComponent {}
-unsafe impl Send for DirectWriteComponent {}
-
 struct DirectWriteState {
     components: DirectWriteComponent,
     system_ui_font_name: SharedString,
@@ -75,12 +71,10 @@ struct FontIdentifier {
 }
 
 impl DirectWriteComponent {
-    pub fn new() -> Result<Self> {
+    pub fn new(bitmap_factory: &IWICImagingFactory) -> Result<Self> {
         unsafe {
             let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;
-            let bitmap_factory: IWICImagingFactory2 =
-                CoCreateInstance(&CLSID_WICImagingFactory2, None, CLSCTX_INPROC_SERVER)?;
-            let bitmap_factory = ManuallyDrop::new(bitmap_factory);
+            let bitmap_factory = AgileReference::new(bitmap_factory)?;
             let d2d1_factory: ID2D1Factory =
                 D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, None)?;
             // The `IDWriteInMemoryFontFileLoader` here is supported starting from
@@ -145,8 +139,8 @@ impl GlyphRenderContext {
 }
 
 impl DirectWriteTextSystem {
-    pub(crate) fn new() -> Result<Self> {
-        let components = DirectWriteComponent::new()?;
+    pub(crate) fn new(bitmap_factory: &IWICImagingFactory) -> Result<Self> {
+        let components = DirectWriteComponent::new(bitmap_factory)?;
         let system_font_collection = unsafe {
             let mut result = std::mem::zeroed();
             components
@@ -172,11 +166,6 @@ impl DirectWriteTextSystem {
             font_id_by_identifier: HashMap::default(),
         })))
     }
-
-    pub(crate) fn destroy(&self) {
-        let mut lock = self.0.write();
-        unsafe { ManuallyDrop::drop(&mut lock.components.bitmap_factory) };
-    }
 }
 
 impl PlatformTextSystem for DirectWriteTextSystem {
@@ -798,8 +787,9 @@ impl DirectWriteState {
             bitmap_dpi = 192.0;
         }
 
+        let bitmap_factory = self.components.bitmap_factory.resolve()?;
         unsafe {
-            let bitmap = self.components.bitmap_factory.CreateBitmap(
+            let bitmap = bitmap_factory.CreateBitmap(
                 bitmap_width,
                 bitmap_height,
                 bitmap_format,
@@ -901,7 +891,7 @@ impl DirectWriteState {
                     pixel[2] = (pixel[2] as f32 / a) as u8;
                 }
             } else {
-                let scaler = self.components.bitmap_factory.CreateBitmapScaler()?;
+                let scaler = bitmap_factory.CreateBitmapScaler()?;
                 scaler.Initialize(
                     &bitmap,
                     bitmap_size.width.0 as u32,

crates/gpui/src/platform/windows/platform.rs 🔗

@@ -4,6 +4,7 @@
 use std::{
     cell::{Cell, RefCell},
     ffi::{c_void, OsString},
+    mem::ManuallyDrop,
     os::windows::ffi::{OsStrExt, OsStringExt},
     path::{Path, PathBuf},
     rc::Rc,
@@ -21,7 +22,10 @@ use windows::{
     Win32::{
         Foundation::*,
         Globalization::u_memcpy,
-        Graphics::Gdi::*,
+        Graphics::{
+            Gdi::*,
+            Imaging::{CLSID_WICImagingFactory, IWICImagingFactory},
+        },
         Security::Credentials::*,
         System::{
             Com::*,
@@ -53,6 +57,7 @@ pub(crate) struct WindowsPlatform {
     clipboard_hash_format: u32,
     clipboard_metadata_format: u32,
     windows_version: WindowsVersion,
+    bitmap_factory: ManuallyDrop<IWICImagingFactory>,
 }
 
 pub(crate) struct WindowsPlatformState {
@@ -91,8 +96,14 @@ impl WindowsPlatform {
         let dispatcher = Arc::new(WindowsDispatcher::new());
         let background_executor = BackgroundExecutor::new(dispatcher.clone());
         let foreground_executor = ForegroundExecutor::new(dispatcher);
-        let text_system =
-            Arc::new(DirectWriteTextSystem::new().expect("Error creating DirectWriteTextSystem"));
+        let bitmap_factory = ManuallyDrop::new(unsafe {
+            CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER)
+                .expect("Error creating bitmap factory.")
+        });
+        let text_system = Arc::new(
+            DirectWriteTextSystem::new(&bitmap_factory)
+                .expect("Error creating DirectWriteTextSystem"),
+        );
         let icon = load_icon().unwrap_or_default();
         let state = RefCell::new(WindowsPlatformState::new());
         let raw_window_handles = RwLock::new(SmallVec::new());
@@ -111,6 +122,7 @@ impl WindowsPlatform {
             clipboard_hash_format,
             clipboard_metadata_format,
             windows_version,
+            bitmap_factory,
         }
     }
 
@@ -584,8 +596,10 @@ impl Platform for WindowsPlatform {
 
 impl Drop for WindowsPlatform {
     fn drop(&mut self) {
-        self.text_system.destroy();
-        unsafe { OleUninitialize() };
+        unsafe {
+            ManuallyDrop::drop(&mut self.bitmap_factory);
+            OleUninitialize();
+        }
     }
 }