servo/components/style/gecko_bindings/sugar/refptr.rs
Emilio Cobos Álvarez 14b176019c style: Simplify a bit our generated bindings by getting rid of FooBorrowed and FooBorrowedMut.
This reduces a lot the boilerplate that's needed in order to add simple binding
functions.

This starts using &Foo and Option<&Foo> instead, and as a result we need to
remove the servo_function_signatures test, which is a bit unfortunate.

I think it's worth though, this causes problems on some platforms (see bug
1534844), and messing up the functions signature is not something that I've ever
seen (other than bug 1308234, which already had all the FooBorrowed mess which
I'm removing).

Also, cbindgen understands references and Option<&Foo>, so it will be the way to
go in the future.

After this patch we can also remove HasSimpleFFI, but I've kept it for now since
I still use it in a few places, and this patch is quite big on its own.

Differential Revision: https://phabricator.services.mozilla.com/D24092
2019-03-27 14:29:19 +01:00

326 lines
9.2 KiB
Rust

/* 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,
}
}
/// Create a new RefPtr from a pointer obtained from FFI.
///
/// The pointer must be valid and non null.
///
/// This method calls addref() internally
pub unsafe fn new(ptr: *mut T) -> Self {
debug_assert!(!ptr.is_null());
let ret = RefPtr {
ptr: 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) {
unsafe {
(*self.ptr).addref();
}
}
/// Release the inner data.
///
/// Call only when the data actually needs releasing.
pub unsafe fn release(&self) {
(*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 {
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> {
debug_assert!(!self.mRawPtr.is_null());
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::nsCSSValueSharedList,
bindings::Gecko_AddRefCSSValueSharedListArbitraryThread,
bindings::Gecko_ReleaseCSSValueSharedListArbitraryThread
);
impl_threadsafe_refcount!(
structs::mozilla::css::URLValue,
bindings::Gecko_AddRefCSSURLValueArbitraryThread,
bindings::Gecko_ReleaseCSSURLValueArbitraryThread
);
impl_threadsafe_refcount!(
structs::mozilla::css::GridTemplateAreasValue,
bindings::Gecko_AddRefGridTemplateAreasValueArbitraryThread,
bindings::Gecko_ReleaseGridTemplateAreasValueArbitraryThread
);
impl_threadsafe_refcount!(
structs::SharedFontList,
bindings::Gecko_AddRefSharedFontListArbitraryThread,
bindings::Gecko_ReleaseSharedFontListArbitraryThread
);
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);