diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /servo/components/style/gecko_bindings/sugar/refptr.rs | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'servo/components/style/gecko_bindings/sugar/refptr.rs')
-rw-r--r-- | servo/components/style/gecko_bindings/sugar/refptr.rs | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/servo/components/style/gecko_bindings/sugar/refptr.rs b/servo/components/style/gecko_bindings/sugar/refptr.rs new file mode 100644 index 0000000000..d8c0cea0f2 --- /dev/null +++ b/servo/components/style/gecko_bindings/sugar/refptr.rs @@ -0,0 +1,326 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! A rust helper to ease the use of Gecko's refcounted types. + +use crate::gecko_bindings::sugar::ownership::HasArcFFI; +use crate::gecko_bindings::{bindings, structs}; +use crate::Atom; +use servo_arc::Arc; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; +use std::{fmt, mem, ptr}; + +/// Trait for all objects that have Addref() and Release +/// methods and can be placed inside RefPtr<T> +pub unsafe trait RefCounted { + /// Bump the reference count. + fn addref(&self); + /// Decrease the reference count. + unsafe fn release(&self); +} + +/// Trait for types which can be shared across threads in RefPtr. +pub unsafe trait ThreadSafeRefCounted: RefCounted {} + +/// A custom RefPtr implementation to take into account Drop semantics and +/// a bit less-painful memory management. +pub struct RefPtr<T: RefCounted> { + ptr: *mut T, + _marker: PhantomData<T>, +} + +impl<T: RefCounted> fmt::Debug for RefPtr<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("RefPtr { ")?; + self.ptr.fmt(f)?; + f.write_str("}") + } +} + +/// A RefPtr that we know is uniquely owned. +/// +/// This is basically Box<T>, with the additional guarantee that the box can be +/// safely interpreted as a RefPtr<T> (with refcount 1) +/// +/// This is useful when you wish to create a refptr and mutate it temporarily, +/// while it is still uniquely owned. +pub struct UniqueRefPtr<T: RefCounted>(RefPtr<T>); + +// There is no safe conversion from &T to RefPtr<T> (like Gecko has) +// because this lets you break UniqueRefPtr's guarantee + +impl<T: RefCounted> RefPtr<T> { + /// Create a new RefPtr from an already addrefed pointer obtained from FFI. + /// + /// The pointer must be valid, non-null and have been addrefed. + pub unsafe fn from_addrefed(ptr: *mut T) -> Self { + debug_assert!(!ptr.is_null()); + RefPtr { + ptr: ptr, + _marker: PhantomData, + } + } + + /// Returns whether the current pointer is null. + pub fn is_null(&self) -> bool { + self.ptr.is_null() + } + + /// Returns a null pointer. + pub fn null() -> Self { + Self { + ptr: ptr::null_mut(), + _marker: PhantomData, + } + } + + /// Create a new RefPtr from a pointer obtained from FFI. + /// + /// This method calls addref() internally + pub unsafe fn new(ptr: *mut T) -> Self { + let ret = RefPtr { + ptr, + _marker: PhantomData, + }; + ret.addref(); + ret + } + + /// Produces an FFI-compatible RefPtr that can be stored in style structs. + /// + /// structs::RefPtr does not have a destructor, so this may leak + pub fn forget(self) -> structs::RefPtr<T> { + let ret = structs::RefPtr { + mRawPtr: self.ptr, + _phantom_0: PhantomData, + }; + mem::forget(self); + ret + } + + /// Returns the raw inner pointer to be fed back into FFI. + pub fn get(&self) -> *mut T { + self.ptr + } + + /// Addref the inner data, obviously leaky on its own. + pub fn addref(&self) { + if !self.ptr.is_null() { + unsafe { + (*self.ptr).addref(); + } + } + } + + /// Release the inner data. + /// + /// Call only when the data actually needs releasing. + pub unsafe fn release(&self) { + if !self.ptr.is_null() { + (*self.ptr).release(); + } + } +} + +impl<T: RefCounted> UniqueRefPtr<T> { + /// Create a unique refptr from an already addrefed pointer obtained from + /// FFI. + /// + /// The refcount must be one. + /// + /// The pointer must be valid and non null + pub unsafe fn from_addrefed(ptr: *mut T) -> Self { + UniqueRefPtr(RefPtr::from_addrefed(ptr)) + } + + /// Convert to a RefPtr so that it can be used. + pub fn get(self) -> RefPtr<T> { + self.0 + } +} + +impl<T: RefCounted> Deref for RefPtr<T> { + type Target = T; + fn deref(&self) -> &T { + debug_assert!(!self.ptr.is_null()); + unsafe { &*self.ptr } + } +} + +impl<T: RefCounted> Deref for UniqueRefPtr<T> { + type Target = T; + fn deref(&self) -> &T { + unsafe { &*self.0.ptr } + } +} + +impl<T: RefCounted> DerefMut for UniqueRefPtr<T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.0.ptr } + } +} + +impl<T: RefCounted> structs::RefPtr<T> { + /// Produces a Rust-side RefPtr from an FFI RefPtr, bumping the refcount. + /// + /// Must be called on a valid, non-null structs::RefPtr<T>. + pub unsafe fn to_safe(&self) -> RefPtr<T> { + let r = RefPtr { + ptr: self.mRawPtr, + _marker: PhantomData, + }; + r.addref(); + r + } + /// Produces a Rust-side RefPtr, consuming the existing one (and not bumping + /// the refcount). + pub unsafe fn into_safe(self) -> RefPtr<T> { + debug_assert!(!self.mRawPtr.is_null()); + RefPtr { + ptr: self.mRawPtr, + _marker: PhantomData, + } + } + + /// Replace a structs::RefPtr<T> with a different one, appropriately + /// addref/releasing. + /// + /// Both `self` and `other` must be valid, but can be null. + /// + /// Safe when called on an aliased pointer because the refcount in that case + /// needs to be at least two. + pub unsafe fn set(&mut self, other: &Self) { + self.clear(); + if !other.mRawPtr.is_null() { + *self = other.to_safe().forget(); + } + } + + /// Clear an instance of the structs::RefPtr<T>, by releasing + /// it and setting its contents to null. + /// + /// `self` must be valid, but can be null. + pub unsafe fn clear(&mut self) { + if !self.mRawPtr.is_null() { + (*self.mRawPtr).release(); + self.mRawPtr = ptr::null_mut(); + } + } + + /// Replace a `structs::RefPtr<T>` with a `RefPtr<T>`, + /// consuming the `RefPtr<T>`, and releasing the old + /// value in `self` if necessary. + /// + /// `self` must be valid, possibly null. + pub fn set_move(&mut self, other: RefPtr<T>) { + if !self.mRawPtr.is_null() { + unsafe { + (*self.mRawPtr).release(); + } + } + *self = other.forget(); + } +} + +impl<T> structs::RefPtr<T> { + /// Sets the contents to an `Arc<T>`, releasing the old value in `self` if + /// necessary. + pub fn set_arc<U>(&mut self, other: Arc<U>) + where + U: HasArcFFI<FFIType = T>, + { + unsafe { + U::release_opt(self.mRawPtr.as_ref()); + } + self.set_arc_leaky(other); + } + + /// Sets the contents to an Arc<T> + /// will leak existing contents + pub fn set_arc_leaky<U>(&mut self, other: Arc<U>) + where + U: HasArcFFI<FFIType = T>, + { + *self = unsafe { mem::transmute(Arc::into_raw_offset(other)) }; + } +} + +impl<T: RefCounted> Drop for RefPtr<T> { + fn drop(&mut self) { + unsafe { self.release() } + } +} + +impl<T: RefCounted> Clone for RefPtr<T> { + fn clone(&self) -> Self { + self.addref(); + RefPtr { + ptr: self.ptr, + _marker: PhantomData, + } + } +} + +impl<T: RefCounted> PartialEq for RefPtr<T> { + fn eq(&self, other: &Self) -> bool { + self.ptr == other.ptr + } +} + +unsafe impl<T: ThreadSafeRefCounted> Send for RefPtr<T> {} +unsafe impl<T: ThreadSafeRefCounted> Sync for RefPtr<T> {} + +macro_rules! impl_refcount { + ($t:ty, $addref:path, $release:path) => { + unsafe impl RefCounted for $t { + #[inline] + fn addref(&self) { + unsafe { $addref(self as *const _ as *mut _) } + } + + #[inline] + unsafe fn release(&self) { + $release(self as *const _ as *mut _) + } + } + }; +} + +// Companion of NS_DECL_THREADSAFE_FFI_REFCOUNTING. +// +// Gets you a free RefCounted impl implemented via FFI. +macro_rules! impl_threadsafe_refcount { + ($t:ty, $addref:path, $release:path) => { + impl_refcount!($t, $addref, $release); + unsafe impl ThreadSafeRefCounted for $t {} + }; +} + +impl_threadsafe_refcount!( + structs::mozilla::URLExtraData, + bindings::Gecko_AddRefURLExtraDataArbitraryThread, + bindings::Gecko_ReleaseURLExtraDataArbitraryThread +); +impl_threadsafe_refcount!( + structs::nsIURI, + bindings::Gecko_AddRefnsIURIArbitraryThread, + bindings::Gecko_ReleasensIURIArbitraryThread +); +impl_threadsafe_refcount!( + structs::SheetLoadDataHolder, + bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread, + bindings::Gecko_ReleaseSheetLoadDataHolderArbitraryThread +); + +#[inline] +unsafe fn addref_atom(atom: *mut structs::nsAtom) { + mem::forget(Atom::from_raw(atom)); +} + +#[inline] +unsafe fn release_atom(atom: *mut structs::nsAtom) { + let _ = Atom::from_addrefed(atom); +} +impl_threadsafe_refcount!(structs::nsAtom, addref_atom, release_atom); |