From 5dc80d8fa3533dd9d4ed721025522f35f3d2f485 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 3 Dec 2024 11:41:01 -0800 Subject: [PATCH] Add `sync` feature to make `Image` implement `Send`/`Sync` This uses a global mutex when ref counts are changed, which should prevent data races, as long as `&mut` prevent other mutation. --- pixman/Cargo.toml | 1 + pixman/src/image/bits.rs | 16 ++++++++++++++++ pixman/src/image/mod.rs | 6 ++++++ pixman/src/lib.rs | 3 +++ 4 files changed, 26 insertions(+) diff --git a/pixman/Cargo.toml b/pixman/Cargo.toml index 96b1f78..e36bd43 100644 --- a/pixman/Cargo.toml +++ b/pixman/Cargo.toml @@ -28,3 +28,4 @@ image = "0.24.7" [features] default = [] drm-fourcc = ["dep:drm-fourcc"] +sync = [] diff --git a/pixman/src/image/bits.rs b/pixman/src/image/bits.rs index bb5c207..1ddf77d 100644 --- a/pixman/src/image/bits.rs +++ b/pixman/src/image/bits.rs @@ -13,6 +13,18 @@ pub struct Image<'bits, 'alpha> { _alpha: PhantomData<&'alpha ()>, } +// SAFETY: A reference to the image is only created by `set_alpha_map`. +// Which returns a type with a lifetime bound, so `&mut self` methods cannot +// be called while this additional reference is in use. +// +// Thus the only mutability allowed is reference counting, which is made +// thread-safe with the `REF_COUNT_LOCK` mutex, used when calling +// `pixman_image_unref`, or `pixman_image_set_alpha_map`. +#[cfg(feature = "sync")] +unsafe impl<'bits, 'alpha> Send for Image<'bits, 'alpha> {} +#[cfg(feature = "sync")] +unsafe impl<'bits, 'alpha> Sync for Image<'bits, 'alpha> {} + impl<'bits, 'alpha> std::ops::Deref for Image<'bits, 'alpha> { type Target = ImageRef; @@ -140,6 +152,8 @@ impl<'bits, 'a> Image<'bits, 'a> { x: i16, y: i16, ) -> Image<'bits, 'alpha> { + #[cfg(feature = "sync")] + let _lock = crate::REF_COUNT_LOCK.lock().unwrap(); unsafe { ffi::pixman_image_set_alpha_map(self.as_ptr(), alpha_map.as_ptr(), x, y); } @@ -152,6 +166,8 @@ impl<'bits, 'a> Image<'bits, 'a> { /// Clear a previously set alpha map pub fn clear_alpha_map(self) -> Image<'bits, 'static> { + #[cfg(feature = "sync")] + let _lock = crate::REF_COUNT_LOCK.lock().unwrap(); unsafe { ffi::pixman_image_set_alpha_map(self.as_ptr(), std::ptr::null_mut(), 0, 0); } diff --git a/pixman/src/image/mod.rs b/pixman/src/image/mod.rs index 76351ff..219a1e8 100644 --- a/pixman/src/image/mod.rs +++ b/pixman/src/image/mod.rs @@ -174,6 +174,8 @@ impl ImageRef { impl Drop for ImageRef { fn drop(&mut self) { + #[cfg(feature = "sync")] + let _lock = crate::REF_COUNT_LOCK.lock().unwrap(); unsafe { ffi::pixman_image_unref(self.0); } @@ -197,6 +199,8 @@ macro_rules! image_type { x: i16, y: i16, ) -> $name<'alpha> { + #[cfg(feature = "sync")] + let _lock = $crate::REF_COUNT_LOCK.lock().unwrap(); unsafe { $crate::ffi::pixman_image_set_alpha_map( self.as_ptr(), @@ -213,6 +217,8 @@ macro_rules! image_type { /// Clear a previously set alpha map pub fn clear_alpha_map(self) -> $name<'static> { + #[cfg(feature = "sync")] + let _lock = $crate::REF_COUNT_LOCK.lock().unwrap(); unsafe { $crate::ffi::pixman_image_set_alpha_map( self.as_ptr(), diff --git a/pixman/src/lib.rs b/pixman/src/lib.rs index 78bfd26..0fa31d0 100644 --- a/pixman/src/lib.rs +++ b/pixman/src/lib.rs @@ -66,6 +66,9 @@ pub use vector::*; #[error("The requested operation failed")] pub struct OperationFailed; +#[cfg(feature = "sync")] +static REF_COUNT_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(()); + /// Blit the src into the dst with the specified values #[allow(clippy::too_many_arguments)] pub fn blit(