tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

refptr.rs (7903B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 //! A rust helper to ease the use of Gecko's refcounted types.
      6 
      7 use crate::gecko_bindings::{bindings, structs};
      8 use crate::Atom;
      9 use servo_arc::Arc;
     10 use std::fmt::Write;
     11 use std::marker::PhantomData;
     12 use std::ops::Deref;
     13 use std::{fmt, mem, ptr};
     14 
     15 /// Trait for all objects that have Addref() and Release
     16 /// methods and can be placed inside RefPtr<T>
     17 pub unsafe trait RefCounted {
     18    /// Bump the reference count.
     19    fn addref(&self);
     20    /// Decrease the reference count.
     21    unsafe fn release(&self);
     22 }
     23 
     24 /// Trait for types which can be shared across threads in RefPtr.
     25 pub unsafe trait ThreadSafeRefCounted: RefCounted {}
     26 
     27 /// A custom RefPtr implementation to take into account Drop semantics and
     28 /// a bit less-painful memory management.
     29 pub struct RefPtr<T: RefCounted> {
     30    ptr: *mut T,
     31    _marker: PhantomData<T>,
     32 }
     33 
     34 impl<T: RefCounted> fmt::Debug for RefPtr<T> {
     35    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     36        f.write_str("RefPtr { ")?;
     37        self.ptr.fmt(f)?;
     38        f.write_char('}')
     39    }
     40 }
     41 
     42 impl<T: RefCounted> RefPtr<T> {
     43    /// Create a new RefPtr from an already addrefed pointer obtained from FFI.
     44    ///
     45    /// The pointer must be valid, non-null and have been addrefed.
     46    pub unsafe fn from_addrefed(ptr: *mut T) -> Self {
     47        debug_assert!(!ptr.is_null());
     48        RefPtr {
     49            ptr,
     50            _marker: PhantomData,
     51        }
     52    }
     53 
     54    /// Returns whether the current pointer is null.
     55    pub fn is_null(&self) -> bool {
     56        self.ptr.is_null()
     57    }
     58 
     59    /// Returns a null pointer.
     60    pub fn null() -> Self {
     61        Self {
     62            ptr: ptr::null_mut(),
     63            _marker: PhantomData,
     64        }
     65    }
     66 
     67    /// Create a new RefPtr from a pointer obtained from FFI.
     68    ///
     69    /// This method calls addref() internally
     70    pub unsafe fn new(ptr: *mut T) -> Self {
     71        let ret = RefPtr {
     72            ptr,
     73            _marker: PhantomData,
     74        };
     75        ret.addref();
     76        ret
     77    }
     78 
     79    /// Produces an FFI-compatible RefPtr that can be stored in style structs.
     80    ///
     81    /// structs::RefPtr does not have a destructor, so this may leak
     82    pub fn forget(self) -> structs::RefPtr<T> {
     83        let ret = structs::RefPtr {
     84            mRawPtr: self.ptr,
     85            _phantom_0: PhantomData,
     86        };
     87        mem::forget(self);
     88        ret
     89    }
     90 
     91    /// Returns the raw inner pointer to be fed back into FFI.
     92    pub fn get(&self) -> *mut T {
     93        self.ptr
     94    }
     95 
     96    /// Addref the inner data, obviously leaky on its own.
     97    pub fn addref(&self) {
     98        if !self.ptr.is_null() {
     99            unsafe {
    100                (*self.ptr).addref();
    101            }
    102        }
    103    }
    104 
    105    /// Release the inner data.
    106    ///
    107    /// Call only when the data actually needs releasing.
    108    pub unsafe fn release(&self) {
    109        if !self.ptr.is_null() {
    110            (*self.ptr).release();
    111        }
    112    }
    113 }
    114 
    115 impl<T: RefCounted> Deref for RefPtr<T> {
    116    type Target = T;
    117    fn deref(&self) -> &T {
    118        debug_assert!(!self.ptr.is_null());
    119        unsafe { &*self.ptr }
    120    }
    121 }
    122 
    123 impl<T: RefCounted> structs::RefPtr<T> {
    124    /// Produces a Rust-side RefPtr from an FFI RefPtr, bumping the refcount.
    125    ///
    126    /// Must be called on a valid, non-null structs::RefPtr<T>.
    127    pub unsafe fn to_safe(&self) -> RefPtr<T> {
    128        let r = RefPtr {
    129            ptr: self.mRawPtr,
    130            _marker: PhantomData,
    131        };
    132        r.addref();
    133        r
    134    }
    135    /// Produces a Rust-side RefPtr, consuming the existing one (and not bumping
    136    /// the refcount).
    137    pub unsafe fn into_safe(self) -> RefPtr<T> {
    138        debug_assert!(!self.mRawPtr.is_null());
    139        RefPtr {
    140            ptr: self.mRawPtr,
    141            _marker: PhantomData,
    142        }
    143    }
    144 
    145    /// Replace a structs::RefPtr<T> with a different one, appropriately
    146    /// addref/releasing.
    147    ///
    148    /// Both `self` and `other` must be valid, but can be null.
    149    ///
    150    /// Safe when called on an aliased pointer because the refcount in that case
    151    /// needs to be at least two.
    152    pub unsafe fn set(&mut self, other: &Self) {
    153        self.clear();
    154        if !other.mRawPtr.is_null() {
    155            *self = other.to_safe().forget();
    156        }
    157    }
    158 
    159    /// Clear an instance of the structs::RefPtr<T>, by releasing
    160    /// it and setting its contents to null.
    161    ///
    162    /// `self` must be valid, but can be null.
    163    pub unsafe fn clear(&mut self) {
    164        if !self.mRawPtr.is_null() {
    165            (*self.mRawPtr).release();
    166            self.mRawPtr = ptr::null_mut();
    167        }
    168    }
    169 
    170    /// Replace a `structs::RefPtr<T>` with a `RefPtr<T>`,
    171    /// consuming the `RefPtr<T>`, and releasing the old
    172    /// value in `self` if necessary.
    173    ///
    174    /// `self` must be valid, possibly null.
    175    pub fn set_move(&mut self, other: RefPtr<T>) {
    176        if !self.mRawPtr.is_null() {
    177            unsafe {
    178                (*self.mRawPtr).release();
    179            }
    180        }
    181        *self = other.forget();
    182    }
    183 }
    184 
    185 impl<T> structs::RefPtr<T> {
    186    /// Returns a new, null refptr.
    187    pub fn null() -> Self {
    188        Self {
    189            mRawPtr: ptr::null_mut(),
    190            _phantom_0: PhantomData,
    191        }
    192    }
    193 
    194    /// Create a new RefPtr from an arc.
    195    pub fn from_arc(s: Arc<T>) -> Self {
    196        Self {
    197            mRawPtr: Arc::into_raw(s) as *mut _,
    198            _phantom_0: PhantomData,
    199        }
    200    }
    201 
    202    /// Sets the contents to an Arc<T>.
    203    pub fn set_arc(&mut self, other: Arc<T>) {
    204        unsafe {
    205            if !self.mRawPtr.is_null() {
    206                let _ = Arc::from_raw(self.mRawPtr);
    207            }
    208            self.mRawPtr = Arc::into_raw(other) as *mut _;
    209        }
    210    }
    211 }
    212 
    213 impl<T: RefCounted> Drop for RefPtr<T> {
    214    fn drop(&mut self) {
    215        unsafe { self.release() }
    216    }
    217 }
    218 
    219 impl<T: RefCounted> Clone for RefPtr<T> {
    220    fn clone(&self) -> Self {
    221        self.addref();
    222        RefPtr {
    223            ptr: self.ptr,
    224            _marker: PhantomData,
    225        }
    226    }
    227 }
    228 
    229 impl<T: RefCounted> PartialEq for RefPtr<T> {
    230    fn eq(&self, other: &Self) -> bool {
    231        self.ptr == other.ptr
    232    }
    233 }
    234 
    235 unsafe impl<T: ThreadSafeRefCounted> Send for RefPtr<T> {}
    236 unsafe impl<T: ThreadSafeRefCounted> Sync for RefPtr<T> {}
    237 
    238 macro_rules! impl_refcount {
    239    ($t:ty, $addref:path, $release:path) => {
    240        unsafe impl RefCounted for $t {
    241            #[inline]
    242            fn addref(&self) {
    243                unsafe { $addref(self as *const _ as *mut _) }
    244            }
    245 
    246            #[inline]
    247            unsafe fn release(&self) {
    248                $release(self as *const _ as *mut _)
    249            }
    250        }
    251    };
    252 }
    253 
    254 // Companion of NS_DECL_THREADSAFE_FFI_REFCOUNTING.
    255 //
    256 // Gets you a free RefCounted impl implemented via FFI.
    257 macro_rules! impl_threadsafe_refcount {
    258    ($t:ty, $addref:path, $release:path) => {
    259        impl_refcount!($t, $addref, $release);
    260        unsafe impl ThreadSafeRefCounted for $t {}
    261    };
    262 }
    263 
    264 impl_threadsafe_refcount!(
    265    structs::mozilla::URLExtraData,
    266    bindings::Gecko_AddRefURLExtraDataArbitraryThread,
    267    bindings::Gecko_ReleaseURLExtraDataArbitraryThread
    268 );
    269 impl_threadsafe_refcount!(
    270    structs::nsIURI,
    271    bindings::Gecko_AddRefnsIURIArbitraryThread,
    272    bindings::Gecko_ReleasensIURIArbitraryThread
    273 );
    274 impl_threadsafe_refcount!(
    275    structs::SheetLoadDataHolder,
    276    bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread,
    277    bindings::Gecko_ReleaseSheetLoadDataHolderArbitraryThread
    278 );
    279 
    280 #[inline]
    281 unsafe fn addref_atom(atom: *mut structs::nsAtom) {
    282    mem::forget(Atom::from_raw(atom));
    283 }
    284 
    285 #[inline]
    286 unsafe fn release_atom(atom: *mut structs::nsAtom) {
    287    let _ = Atom::from_addrefed(atom);
    288 }
    289 impl_threadsafe_refcount!(structs::nsAtom, addref_atom, release_atom);