/* 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 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 { ptr: *mut T, _marker: PhantomData, } impl fmt::Debug for RefPtr { 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, with the additional guarantee that the box can be /// safely interpreted as a RefPtr (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(RefPtr); // There is no safe conversion from &T to RefPtr (like Gecko has) // because this lets you break UniqueRefPtr's guarantee impl RefPtr { /// 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 { 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 UniqueRefPtr { /// 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 { self.0 } } impl Deref for RefPtr { type Target = T; fn deref(&self) -> &T { debug_assert!(!self.ptr.is_null()); unsafe { &*self.ptr } } } impl Deref for UniqueRefPtr { type Target = T; fn deref(&self) -> &T { unsafe { &*self.0.ptr } } } impl DerefMut for UniqueRefPtr { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.0.ptr } } } impl structs::RefPtr { /// Produces a Rust-side RefPtr from an FFI RefPtr, bumping the refcount. /// /// Must be called on a valid, non-null structs::RefPtr. pub unsafe fn to_safe(&self) -> RefPtr { 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 { debug_assert!(!self.mRawPtr.is_null()); RefPtr { ptr: self.mRawPtr, _marker: PhantomData, } } /// Replace a structs::RefPtr 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, 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` with a `RefPtr`, /// consuming the `RefPtr`, and releasing the old /// value in `self` if necessary. /// /// `self` must be valid, possibly null. pub fn set_move(&mut self, other: RefPtr) { if !self.mRawPtr.is_null() { unsafe { (*self.mRawPtr).release(); } } *self = other.forget(); } } impl structs::RefPtr { /// Sets the contents to an `Arc`, releasing the old value in `self` if /// necessary. pub fn set_arc(&mut self, other: Arc) where U: HasArcFFI, { unsafe { U::release_opt(self.mRawPtr.as_ref()); } self.set_arc_leaky(other); } /// Sets the contents to an Arc /// will leak existing contents pub fn set_arc_leaky(&mut self, other: Arc) where U: HasArcFFI, { *self = unsafe { mem::transmute(Arc::into_raw_offset(other)) }; } } impl Drop for RefPtr { fn drop(&mut self) { unsafe { self.release() } } } impl Clone for RefPtr { fn clone(&self) -> Self { self.addref(); RefPtr { ptr: self.ptr, _marker: PhantomData, } } } impl PartialEq for RefPtr { fn eq(&self, other: &Self) -> bool { self.ptr == other.ptr } } unsafe impl Send for RefPtr {} unsafe impl Sync for RefPtr {} 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);