/* 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 http://mozilla.org/MPL/2.0/. */ use crate::interfaces::nsISupports; use nserror::{nsresult, NS_OK}; use std::cell::Cell; use std::convert::TryInto; use std::fmt; use std::marker::PhantomData; use std::mem; use std::ops::Deref; use std::ptr::{self, NonNull}; use std::sync::atomic::{self, AtomicUsize, Ordering}; use threadbound::ThreadBound; // This should match the definition in mfbt/RefCountType.h, modulo the delicate // effort at maintaining binary compatibility with Microsoft COM on Windows. pub type MozExternalRefCountType = u32; /// A trait representing a type which can be reference counted invasively. /// The object is responsible for freeing its backing memory when its /// reference count reaches 0. pub unsafe trait RefCounted { /// Increment the reference count. unsafe fn addref(&self); /// Decrement the reference count, potentially freeing backing memory. unsafe fn release(&self); } /// A smart pointer holding a RefCounted object. The object itself manages its /// own memory. RefPtr will invoke the addref and release methods at the /// appropriate times to facilitate the bookkeeping. #[repr(transparent)] pub struct RefPtr { _ptr: NonNull, // Tell dropck that we own an instance of T. _marker: PhantomData, } impl RefPtr { /// Construct a new RefPtr from a reference to the refcounted object. #[inline] pub fn new(p: &T) -> RefPtr { unsafe { p.addref(); } RefPtr { _ptr: p.into(), _marker: PhantomData, } } /// Construct a RefPtr from a raw pointer, addrefing it. #[inline] pub unsafe fn from_raw(p: *const T) -> Option> { let ptr = NonNull::new(p as *mut T)?; ptr.as_ref().addref(); Some(RefPtr { _ptr: ptr, _marker: PhantomData, }) } /// Construct a RefPtr from a raw pointer, without addrefing it. #[inline] pub unsafe fn from_raw_dont_addref(p: *const T) -> Option> { Some(RefPtr { _ptr: NonNull::new(p as *mut T)?, _marker: PhantomData, }) } /// Write this RefPtr's value into an outparameter. #[inline] pub fn forget(self, into: &mut *const T) { *into = Self::forget_into_raw(self); } #[inline] pub fn forget_into_raw(this: RefPtr) -> *const T { let into = &*this as *const T; mem::forget(this); into } } impl Deref for RefPtr { type Target = T; #[inline] fn deref(&self) -> &T { unsafe { self._ptr.as_ref() } } } impl Drop for RefPtr { #[inline] fn drop(&mut self) { unsafe { self._ptr.as_ref().release(); } } } impl Clone for RefPtr { #[inline] fn clone(&self) -> RefPtr { RefPtr::new(self) } } impl fmt::Debug for RefPtr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "RefPtr<{:?}>", self.deref()) } } // Both `Send` and `Sync` bounds are required for `RefPtr` to implement // either, as sharing a `RefPtr` also allows transferring ownership, and // vice-versa. unsafe impl Send for RefPtr {} unsafe impl Sync for RefPtr {} macro_rules! assert_layout_eq { ($T:ty, $U:ty) => { const _: [(); std::mem::size_of::<$T>()] = [(); std::mem::size_of::<$U>()]; const _: [(); std::mem::align_of::<$T>()] = [(); std::mem::align_of::<$U>()]; }; } // Assert that `RefPtr` has the correct memory layout. assert_layout_eq!(RefPtr, *const nsISupports); // Assert that the null-pointer optimization applies to `RefPtr`. assert_layout_eq!(RefPtr, Option>); /// A wrapper that binds a RefCounted value to its original thread, /// preventing retrieval from other threads and panicking if the value /// is dropped on a different thread. /// /// These limitations enable values of this type to be Send + Sync, which is /// useful when creating a struct that holds a RefPtr type while being /// Send + Sync. Such a struct can hold a ThreadBoundRefPtr type instead. pub struct ThreadBoundRefPtr(ThreadBound<*const T>); impl ThreadBoundRefPtr { pub fn new(ptr: RefPtr) -> Self { let raw: *const T = &*ptr; mem::forget(ptr); ThreadBoundRefPtr(ThreadBound::new(raw)) } pub fn get_ref(&self) -> Option<&T> { self.0.get_ref().map(|raw| unsafe { &**raw }) } } impl Drop for ThreadBoundRefPtr { fn drop(&mut self) { unsafe { RefPtr::from_raw_dont_addref(self.get_ref().expect("drop() called on wrong thread!")); } } } /// A helper struct for constructing `RefPtr` from raw pointer outparameters. /// Holds a `*const T` internally which will be released if non null when /// destructed, and can be easily transformed into an `Option>`. /// /// It many cases it may be easier to use the `getter_addrefs` method. pub struct GetterAddrefs { _ptr: *const T, _marker: PhantomData, } impl GetterAddrefs { /// Create a `GetterAddrefs`, initializing it with the null pointer. #[inline] pub fn new() -> GetterAddrefs { GetterAddrefs { _ptr: ptr::null(), _marker: PhantomData, } } /// Get a reference to the internal `*const T`. This method is unsafe, /// as the destructor of this class depends on the internal `*const T` /// being either a valid reference to a value of type `T`, or null. #[inline] pub unsafe fn ptr(&mut self) -> &mut *const T { &mut self._ptr } /// Get a reference to the internal `*const T` as a `*mut libc::c_void`. /// This is useful to pass to functions like `GetInterface` which take a /// void pointer outparameter. #[inline] pub unsafe fn void_ptr(&mut self) -> *mut *mut libc::c_void { &mut self._ptr as *mut *const T as *mut *mut libc::c_void } /// Transform this `GetterAddrefs` into an `Option>`, without /// performing any addrefs or releases. #[inline] pub fn refptr(self) -> Option> { let p = self._ptr; // Don't run the destructor because we don't want to release the stored // pointer. mem::forget(self); unsafe { RefPtr::from_raw_dont_addref(p) } } } impl Drop for GetterAddrefs { #[inline] fn drop(&mut self) { if !self._ptr.is_null() { unsafe { (*self._ptr).release(); } } } } /// Helper method for calling XPCOM methods which return a reference counted /// value through an outparameter. Takes a lambda, which is called with a valid /// outparameter argument (`*mut *const T`), and returns a `nsresult`. Returns /// either a `RefPtr` with the value returned from the outparameter, or a /// `nsresult`. /// /// # NOTE: /// /// Can return `Err(NS_OK)` if the call succeeded, but the outparameter was set /// to NULL. /// /// # Usage /// /// ``` /// let x: Result, nsresult> = /// getter_addrefs(|p| iosvc.NewURI(uri, ptr::null(), ptr::null(), p)); /// ``` #[inline] pub fn getter_addrefs(f: F) -> Result, nsresult> where F: FnOnce(*mut *const T) -> nsresult, { let mut ga = GetterAddrefs::::new(); let rv = f(unsafe { ga.ptr() }); if rv.failed() { return Err(rv); } ga.refptr().ok_or(NS_OK) } /// The type of the reference count type for xpcom structs. /// /// `#[xpcom(nonatomic)]` will use this type for the `__refcnt` field. #[derive(Debug)] pub struct Refcnt(Cell); impl Refcnt { /// Create a new reference count value. This is unsafe as manipulating /// Refcnt values is an easy footgun. pub unsafe fn new() -> Self { Refcnt(Cell::new(0)) } /// Increment the reference count. Returns the new reference count. This is /// unsafe as modifying this value can cause a use-after-free. pub unsafe fn inc(&self) -> MozExternalRefCountType { // XXX: Checked add? let new = self.0.get() + 1; self.0.set(new); new.try_into().unwrap() } /// Decrement the reference count. Returns the new reference count. This is /// unsafe as modifying this value can cause a use-after-free. pub unsafe fn dec(&self) -> MozExternalRefCountType { // XXX: Checked sub? let new = self.0.get() - 1; self.0.set(new); new.try_into().unwrap() } /// Get the current value of the reference count. pub fn get(&self) -> usize { self.0.get() } } /// The type of the atomic reference count used for xpcom structs. /// /// `#[xpcom(atomic)]` will use this type for the `__refcnt` field. /// /// See `nsISupportsImpl.h`'s `ThreadSafeAutoRefCnt` class for reasoning behind /// memory ordering decisions. #[derive(Debug)] pub struct AtomicRefcnt(AtomicUsize); impl AtomicRefcnt { /// Create a new reference count value. This is unsafe as manipulating /// Refcnt values is an easy footgun. pub unsafe fn new() -> Self { AtomicRefcnt(AtomicUsize::new(0)) } /// Increment the reference count. Returns the new reference count. This is /// unsafe as modifying this value can cause a use-after-free. pub unsafe fn inc(&self) -> MozExternalRefCountType { let result = self.0.fetch_add(1, Ordering::Relaxed) + 1; result.try_into().unwrap() } /// Decrement the reference count. Returns the new reference count. This is /// unsafe as modifying this value can cause a use-after-free. pub unsafe fn dec(&self) -> MozExternalRefCountType { let result = self.0.fetch_sub(1, Ordering::Release) - 1; if result == 0 { // We're going to destroy the object on this thread, so we need // acquire semantics to synchronize with the memory released by // the last release on other threads, that is, to ensure that // writes prior to that release are now visible on this thread. if cfg!(feature = "thread_sanitizer") { // TSan doesn't understand atomic::fence, so in order to avoid // a false positive for every time a refcounted object is // deleted, we replace the fence with an atomic operation. self.0.load(Ordering::Acquire); } else { atomic::fence(Ordering::Acquire); } } result.try_into().unwrap() } /// Get the current value of the reference count. pub fn get(&self) -> usize { self.0.load(Ordering::Acquire) } } #[cfg(feature = "gecko_refcount_logging")] pub mod trace_refcnt { extern "C" { pub fn NS_LogCtor(aPtr: *mut libc::c_void, aTypeName: *const libc::c_char, aSize: u32); pub fn NS_LogDtor(aPtr: *mut libc::c_void, aTypeName: *const libc::c_char, aSize: u32); pub fn NS_LogAddRef( aPtr: *mut libc::c_void, aRefcnt: usize, aClass: *const libc::c_char, aClassSize: u32, ); pub fn NS_LogRelease( aPtr: *mut libc::c_void, aRefcnt: usize, aClass: *const libc::c_char, aClassSize: u32, ); } } // stub inline methods for the refcount logging functions for when the feature // is disabled. #[cfg(not(feature = "gecko_refcount_logging"))] pub mod trace_refcnt { #[inline] #[allow(non_snake_case)] pub unsafe extern "C" fn NS_LogCtor(_: *mut libc::c_void, _: *const libc::c_char, _: u32) {} #[inline] #[allow(non_snake_case)] pub unsafe extern "C" fn NS_LogDtor(_: *mut libc::c_void, _: *const libc::c_char, _: u32) {} #[inline] #[allow(non_snake_case)] pub unsafe extern "C" fn NS_LogAddRef( _: *mut libc::c_void, _: usize, _: *const libc::c_char, _: u32, ) { } #[inline] #[allow(non_snake_case)] pub unsafe extern "C" fn NS_LogRelease( _: *mut libc::c_void, _: usize, _: *const libc::c_char, _: u32, ) { } }