From 0f90c3cad02542a6c3e6b4a41ffa4c3421f868d8 Mon Sep 17 00:00:00 2001 From: DQ Date: Wed, 7 Apr 2021 14:54:51 +0200 Subject: [PATCH] Replace vendored `servo_arc` with updated `triomphe` (#23) --- Cargo.toml | 1 + src/arc.rs | 970 ---------------------------------------- src/green/node.rs | 2 +- src/green/token.rs | 5 +- src/lib.rs | 4 +- src/syntax/node.rs | 2 +- tests/it/main.rs | 1 + tests/it/regressions.rs | 34 ++ 8 files changed, 42 insertions(+), 977 deletions(-) delete mode 100644 src/arc.rs create mode 100644 tests/it/regressions.rs diff --git a/Cargo.toml b/Cargo.toml index 3334d12..d181fd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ parking_lot= "0.11.1" # Arc stable_deref_trait = "1.0.0" nodrop = "0.1.8" +triomphe = "0.1.2" [dependencies.serde] version = "1.0" diff --git a/src/arc.rs b/src/arc.rs deleted file mode 100644 index 05e015a..0000000 --- a/src/arc.rs +++ /dev/null @@ -1,970 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Fork of fork of Arc for Servo. This has the following advantages over std::sync::Arc: -//! -//! * We don't waste storage on the weak reference count. -//! * We don't do extra RMU operations to handle the possibility of weak references. -//! * We can experiment with arena allocation (todo). -//! * We can add methods to support our custom use cases [1]. -//! * We have support for dynamically-sized types (see from_header_and_iter). -//! * We have support for thin arcs to unsized types (see ThinArc). -//! -//! [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1360883 - -// The semantics of Arc are alread documented in the Rust docs, so we don't -// duplicate those here. -#![allow(warnings)] -#![allow(missing_docs)] -#![allow(clippy::all)] - -extern crate nodrop; -#[cfg(feature = "serde1")] -extern crate serde; -extern crate stable_deref_trait; - -use nodrop::NoDrop; -#[cfg(feature = "serde1")] -use serde::{Deserialize, Serialize}; -use stable_deref_trait::{CloneStableDeref, StableDeref}; -use std::{ - alloc::Layout, - borrow, - cmp::Ordering, - convert::From, - fmt, - hash::{Hash, Hasher}, - isize, - iter::{ExactSizeIterator, Iterator}, - mem, - mem::align_of_val, - ops::{Deref, DerefMut}, - os::raw::c_void, - process, - ptr::{self, NonNull}, - slice, - sync::{ - atomic, - atomic::Ordering::{Acquire, Relaxed, Release}, - }, - usize, -}; - -/// Get the offset within an `ArcInner` for -/// a payload of type described by a pointer. -/// -/// # Safety -/// -/// This has the same safety requirements as `align_of_val_raw`. In effect: -/// -/// - This function is safe for any argument if `T` is sized, and -/// - if `T` is unsized, the pointer must have appropriate pointer metadata acquired from the real instance that you are -/// getting this offset for. -unsafe fn data_offset(ptr: *const T) -> isize { - // Align the unsized value to the end of the `ArcInner`. - // Because it is `?Sized`, it will always be the last field in memory. - // Note: This is a detail of the current implementation of the compiler, - // and is not a guaranteed language detail. Do not rely on it outside of std. - data_offset_align(align_of_val(&*ptr)) -} - -#[inline] -fn data_offset_align(align: usize) -> isize { - let layout = Layout::new::>(); - (layout.size() + padding_needed_for(&layout, align)) as isize -} - -#[inline] -fn padding_needed_for(layout: &Layout, align: usize) -> usize { - let len = layout.size(); - let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); - len_rounded_up.wrapping_sub(len) -} - -/// A soft limit on the amount of references that may be made to an `Arc`. -/// -/// Going above this limit will abort your program (although not -/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. -const MAX_REFCOUNT: usize = (isize::MAX) as usize; - -/// See [`std::sync::Arc`]. -#[repr(C)] -pub struct Arc { - p: NonNull>, -} - -/// An Arc that is known to be uniquely owned -/// -/// This lets us build arcs that we can mutate before -/// freezing, without needing to change the allocation -pub struct UniqueArc(Arc); - -impl UniqueArc { - #[inline] - /// Construct a new UniqueArc - pub fn new(data: T) -> Self { - UniqueArc(Arc::new(data)) - } - - #[inline] - /// Convert to a shareable Arc once we're done using it - pub fn shareable(self) -> Arc { - self.0 - } -} - -impl Deref for UniqueArc { - type Target = T; - - fn deref(&self) -> &T { - &*self.0 - } -} - -impl DerefMut for UniqueArc { - fn deref_mut(&mut self) -> &mut T { - // We know this to be uniquely owned - unsafe { &mut (*self.0.ptr()).data } - } -} - -unsafe impl Send for Arc {} -unsafe impl Sync for Arc {} - -#[repr(C)] -struct ArcInner { - count: atomic::AtomicUsize, - data: T, -} - -unsafe impl Send for ArcInner {} -unsafe impl Sync for ArcInner {} - -impl Arc { - #[inline] - pub fn new(data: T) -> Self { - let x = Box::new(ArcInner { - count: atomic::AtomicUsize::new(1), - data, - }); - Arc { - // safety: we created `x` - p: unsafe { NonNull::new_unchecked(Box::into_raw(x)) }, - } - } - - #[inline] - pub fn into_raw(this: Self) -> *const T { - let ptr = unsafe { &((*this.ptr()).data) as *const _ }; - mem::forget(this); - ptr - } - - #[inline] - pub unsafe fn from_raw(ptr: *const T) -> Self { - // To find the corresponding pointer to the `ArcInner` we need - // to subtract the offset of the `data` field from the pointer. - let offset = data_offset(ptr); - let ptr = (ptr as *const u8).offset(-offset); - Arc { - p: NonNull::new_unchecked(ptr as *mut ArcInner), - } - } - - /// Produce a pointer to the data that can be converted back - /// to an arc - #[inline] - pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> { - ArcBorrow(&**self) - } - - /// Temporarily converts |self| into a bonafide RawOffsetArc and exposes it to the - /// provided callback. The refcount is not modified. - #[inline(always)] - pub fn with_raw_offset_arc(&self, f: F) -> U - where - F: FnOnce(&RawOffsetArc) -> U, - { - // Synthesize transient Arc, which never touches the refcount of the ArcInner. - let transient = unsafe { NoDrop::new(Arc::into_raw_offset(ptr::read(self))) }; - - // Expose the transient Arc to the callback, which may clone it if it wants. - let result = f(&transient); - - // Forget the transient Arc to leave the refcount untouched. - mem::forget(transient); - - // Forward the result. - result - } - - /// Returns the address on the heap of the Arc itself -- not the T within it -- for memory - /// reporting. - pub fn heap_ptr(&self) -> *const c_void { - self.p.as_ptr() as *const ArcInner as *const c_void - } -} - -impl Arc { - #[inline] - fn inner(&self) -> &ArcInner { - // This unsafety is ok because while this arc is alive we're guaranteed - // that the inner pointer is valid. Furthermore, we know that the - // `ArcInner` structure itself is `Sync` because the inner data is - // `Sync` as well, so we're ok loaning out an immutable pointer to these - // contents. - unsafe { &*self.ptr() } - } - - // Non-inlined part of `drop`. Just invokes the destructor. - #[inline(never)] - unsafe fn drop_slow(&mut self) { - let _ = Box::from_raw(self.ptr()); - } - - #[inline] - pub fn ptr_eq(this: &Self, other: &Self) -> bool { - this.ptr() == other.ptr() - } - - fn ptr(&self) -> *mut ArcInner { - self.p.as_ptr() - } -} - -impl Clone for Arc { - #[inline] - fn clone(&self) -> Self { - // Using a relaxed ordering is alright here, as knowledge of the - // original reference prevents other threads from erroneously deleting - // the object. - // - // As explained in the [Boost documentation][1], Increasing the - // reference counter can always be done with memory_order_relaxed: New - // references to an object can only be formed from an existing - // reference, and passing an existing reference from one thread to - // another must already provide any required synchronization. - // - // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - let old_size = self.inner().count.fetch_add(1, Relaxed); - - // However we need to guard against massive refcounts in case someone - // is `mem::forget`ing Arcs. If we don't do this the count can overflow - // and users will use-after free. We racily saturate to `isize::MAX` on - // the assumption that there aren't ~2 billion threads incrementing - // the reference count at once. This branch will never be taken in - // any realistic program. - // - // We abort because such a program is incredibly degenerate, and we - // don't care to support it. - if old_size > MAX_REFCOUNT { - process::abort(); - } - - Arc { - // safety: as described above, as long as the original reference is alive, the - // allocation is valid. Since the allocation existed previously, the pointer to it is - // not null. - p: unsafe { NonNull::new_unchecked(self.ptr()) }, - } - } -} - -impl Deref for Arc { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - &self.inner().data - } -} - -impl Arc { - #[inline] - pub fn make_mut(this: &mut Self) -> &mut T { - if !this.is_unique() { - // Another pointer exists; clone - *this = Arc::new((**this).clone()); - } - - unsafe { - // This unsafety is ok because we're guaranteed that the pointer - // returned is the *only* pointer that will ever be returned to T. Our - // reference count is guaranteed to be 1 at this point, and we required - // the Arc itself to be `mut`, so we're returning the only possible - // reference to the inner data. - &mut (*this.ptr()).data - } - } -} - -impl Arc { - #[inline] - pub fn get_mut(this: &mut Self) -> Option<&mut T> { - if this.is_unique() { - unsafe { - // See make_mut() for documentation of the threadsafety here. - Some(&mut (*this.ptr()).data) - } - } else { - None - } - } - - #[inline] - pub fn is_unique(&self) -> bool { - // We can use Relaxed here, but the justification is a bit subtle. - // - // The reason to use Acquire would be to synchronize with other threads - // that are modifying the refcount with Release, i.e. to ensure that - // their writes to memory guarded by this refcount are flushed. However, - // we know that threads only modify the contents of the Arc when they - // observe the refcount to be 1, and no other thread could observe that - // because we're holding one strong reference here. - self.inner().count.load(Relaxed) == 1 - } -} - -impl Drop for Arc { - #[inline] - fn drop(&mut self) { - // Because `fetch_sub` is already atomic, we do not need to synchronize - // with other threads unless we are going to delete the object. - if self.inner().count.fetch_sub(1, Release) != 1 { - return; - } - - // FIXME(bholley): Use the updated comment when [2] is merged. - // - // This load is needed to prevent reordering of use of the data and - // deletion of the data. Because it is marked `Release`, the decreasing - // of the reference count synchronizes with this `Acquire` load. This - // means that use of the data happens before decreasing the reference - // count, which happens before this load, which happens before the - // deletion of the data. - // - // As explained in the [Boost documentation][1], - // - // > It is important to enforce any possible access to the object in one - // > thread (through an existing reference) to *happen before* deleting - // > the object in a different thread. This is achieved by a "release" - // > operation after dropping a reference (any access to the object - // > through this reference must obviously happened before), and an - // > "acquire" operation before deleting the object. - // - // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - // [2]: https://github.com/rust-lang/rust/pull/41714 - self.inner().count.load(Acquire); - - unsafe { - self.drop_slow(); - } - } -} - -impl PartialEq for Arc { - fn eq(&self, other: &Arc) -> bool { - Self::ptr_eq(self, other) || *(*self) == *(*other) - } - - fn ne(&self, other: &Arc) -> bool { - !Self::ptr_eq(self, other) && *(*self) != *(*other) - } -} -impl PartialOrd for Arc { - fn partial_cmp(&self, other: &Arc) -> Option { - (**self).partial_cmp(&**other) - } - - fn lt(&self, other: &Arc) -> bool { - *(*self) < *(*other) - } - - fn le(&self, other: &Arc) -> bool { - *(*self) <= *(*other) - } - - fn gt(&self, other: &Arc) -> bool { - *(*self) > *(*other) - } - - fn ge(&self, other: &Arc) -> bool { - *(*self) >= *(*other) - } -} -impl Ord for Arc { - fn cmp(&self, other: &Arc) -> Ordering { - (**self).cmp(&**other) - } -} -impl Eq for Arc {} - -impl fmt::Display for Arc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} - -impl fmt::Debug for Arc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl fmt::Pointer for Arc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Pointer::fmt(&self.ptr(), f) - } -} - -impl Default for Arc { - fn default() -> Arc { - Arc::new(Default::default()) - } -} - -impl Hash for Arc { - fn hash(&self, state: &mut H) { - (**self).hash(state) - } -} - -impl From for Arc { - #[inline] - fn from(t: T) -> Self { - Arc::new(t) - } -} - -impl borrow::Borrow for Arc { - #[inline] - fn borrow(&self) -> &T { - &**self - } -} - -impl AsRef for Arc { - #[inline] - fn as_ref(&self) -> &T { - &**self - } -} - -unsafe impl StableDeref for Arc {} -unsafe impl CloneStableDeref for Arc {} - -#[cfg(feature = "serde1")] -impl<'de, T: Deserialize<'de>> Deserialize<'de> for Arc { - fn deserialize(deserializer: D) -> Result, D::Error> - where - D: ::serde::de::Deserializer<'de>, - { - T::deserialize(deserializer).map(Arc::new) - } -} - -#[cfg(feature = "serde1")] -impl Serialize for Arc { - fn serialize(&self, serializer: S) -> Result - where - S: ::serde::ser::Serializer, - { - (**self).serialize(serializer) - } -} - -/// Structure to allow Arc-managing some fixed-sized data and a variably-sized -/// slice in a single allocation. -#[derive(Debug, Eq, PartialEq, PartialOrd)] -pub struct HeaderSlice { - /// The fixed-sized data. - pub header: H, - - /// The dynamically-sized data. - pub slice: T, -} - -#[inline(always)] -fn divide_rounding_up(dividend: usize, divisor: usize) -> usize { - (dividend + divisor - 1) / divisor -} - -impl Arc> { - /// Creates an Arc for a HeaderSlice using the given header struct and - /// iterator to generate the slice. The resulting Arc will be fat. - #[inline] - pub fn from_header_and_iter(header: H, mut items: I) -> Self - where - I: Iterator + ExactSizeIterator, - { - use std::mem::size_of; - assert_ne!(size_of::(), 0, "Need to think about ZST"); - - // Compute the required size for the allocation. - let num_items = items.len(); - let size = { - let inner_layout = Layout::new::>>(); - let slice_layout = - Layout::array::(num_items).expect("arithmetic overflow when trying to create array layout"); - let slice_align = mem::align_of_val::<[T]>(&[]); - assert_eq!(slice_layout.align(), slice_align); - let padding = padding_needed_for(&inner_layout, slice_align); - inner_layout.size() + padding + slice_layout.size() - }; - - let ptr: *mut ArcInner>; - unsafe { - // Allocate the buffer. We use Vec because the underlying allocation - // machinery isn't available in stable Rust. - // - // To avoid alignment issues, we allocate words rather than bytes, - // rounding up to the nearest word size. - let buffer = if mem::align_of::() <= mem::align_of::() { - Self::allocate_buffer::(size) - } else if mem::align_of::() <= mem::align_of::() { - // On 32-bit platforms may have 8 byte alignment while usize has 4 byte aligment. - // Use u64 to avoid over-alignment. - // This branch will compile away in optimized builds. - Self::allocate_buffer::(size) - } else { - panic!("Over-aligned type not handled"); - }; - - // Synthesize the fat pointer. We do this by claiming we have a direct - // pointer to a [T], and then changing the type of the borrow. The key - // point here is that the length portion of the fat pointer applies - // only to the number of elements in the dynamically-sized portion of - // the type, so the value will be the same whether it points to a [T] - // or something else with a [T] as its last member. - let fake_slice: &mut [T] = slice::from_raw_parts_mut(buffer as *mut T, num_items); - ptr = fake_slice as *mut [T] as *mut ArcInner>; - - // Write the data. - // - // Note that any panics here (i.e. from the iterator) are safe, since - // we'll just leak the uninitialized memory. - ptr::write(&mut ((*ptr).count), atomic::AtomicUsize::new(1)); - ptr::write(&mut ((*ptr).data.header), header); - if let Some(current) = (*ptr).data.slice.get_mut(0) { - let mut current: *mut T = current; - for _ in 0..num_items { - ptr::write(current, items.next().expect("ExactSizeIterator over-reported length")); - current = current.offset(1); - } - assert!(items.next().is_none(), "ExactSizeIterator under-reported length"); - - // We should have consumed the buffer exactly. - debug_assert_eq!(current as *mut u8, buffer.offset(size as isize)); - } - } - - // Return the fat Arc. - assert_eq!(size_of::(), size_of::() * 2, "The Arc will be fat"); - Arc { - // safety: we have just created the underlying allocation - p: unsafe { NonNull::new_unchecked(ptr) }, - } - } - - #[inline] - unsafe fn allocate_buffer(size: usize) -> *mut u8 { - let words_to_allocate = divide_rounding_up(size, mem::size_of::()); - let mut vec = Vec::::with_capacity(words_to_allocate); - vec.set_len(words_to_allocate); - Box::into_raw(vec.into_boxed_slice()) as *mut W as *mut u8 - } -} - -/// Header data with an inline length. Consumers that use HeaderWithLength as the -/// Header type in HeaderSlice can take advantage of ThinArc. -#[derive(Debug, Eq, PartialEq, PartialOrd)] -pub struct HeaderWithLength { - /// The fixed-sized data. - pub header: H, - - /// The slice length. - length: usize, -} - -impl HeaderWithLength { - /// Creates a new HeaderWithLength. - pub fn new(header: H, length: usize) -> Self { - HeaderWithLength { header, length } - } -} - -type HeaderSliceWithLength = HeaderSlice, T>; -pub struct ThinArc { - ptr: *mut ArcInner>, -} - -unsafe impl Send for ThinArc {} -unsafe impl Sync for ThinArc {} - -// Synthesize a fat pointer from a thin pointer. -// -// See the comment around the analogous operation in from_header_and_iter. -fn thin_to_thick( - thin: *mut ArcInner>, -) -> *mut ArcInner> { - let len = unsafe { (*thin).data.header.length }; - let fake_slice: *mut [T] = unsafe { slice::from_raw_parts_mut(thin as *mut T, len) }; - - fake_slice as *mut ArcInner> -} - -impl ThinArc { - /// Temporarily converts |self| into a bonafide Arc and exposes it to the - /// provided callback. The refcount is not modified. - #[inline] - pub fn with_arc(&self, f: F) -> U - where - F: FnOnce(&Arc>) -> U, - { - // Synthesize transient Arc, which never touches the refcount of the ArcInner. - let transient = NoDrop::new(Arc { - // safety: the original thin arc guarantees the object was and is still alive - p: unsafe { NonNull::new_unchecked(thin_to_thick(self.ptr)) }, - }); - - // Expose the transient Arc to the callback, which may clone it if it wants. - let result = f(&transient); - - // Forget the transient Arc to leave the refcount untouched. - // XXXManishearth this can be removed when unions stabilize, - // since then NoDrop becomes zero overhead - mem::forget(transient); - - // Forward the result. - result - } - - /// Returns the address on the heap of the ThinArc itself -- not the T - /// within it -- for memory reporting. - #[inline] - pub fn heap_ptr(&self) -> *const c_void { - self.ptr as *const ArcInner as *const c_void - } -} - -impl Deref for ThinArc { - type Target = HeaderSliceWithLength; - - #[inline] - fn deref(&self) -> &Self::Target { - unsafe { &(*thin_to_thick(self.ptr)).data } - } -} - -impl Clone for ThinArc { - #[inline] - fn clone(&self) -> Self { - ThinArc::with_arc(self, |a| Arc::into_thin(a.clone())) - } -} - -impl Drop for ThinArc { - #[inline] - fn drop(&mut self) { - let _ = Arc::from_thin(ThinArc { ptr: self.ptr }); - } -} - -impl Arc> { - /// Converts an Arc into a ThinArc. This consumes the Arc, so the refcount - /// is not modified. - #[inline] - pub fn into_thin(a: Self) -> ThinArc { - assert_eq!( - a.header.length, - a.slice.len(), - "Length needs to be correct for ThinArc to work" - ); - let fat_ptr: *mut ArcInner> = a.ptr(); - mem::forget(a); - let thin_ptr = fat_ptr as *mut [usize] as *mut usize; - ThinArc { - ptr: thin_ptr as *mut ArcInner>, - } - } - - /// Converts a ThinArc into an Arc. This consumes the ThinArc, so the refcount - /// is not modified. - #[inline] - pub fn from_thin(a: ThinArc) -> Self { - let ptr = thin_to_thick(a.ptr); - mem::forget(a); - Arc { - // safety: as above - p: unsafe { NonNull::new_unchecked(ptr) }, - } - } -} - -impl PartialEq for ThinArc { - #[inline] - fn eq(&self, other: &ThinArc) -> bool { - ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| *a == *b)) - } -} - -impl Eq for ThinArc {} - -/// An Arc, except it holds a pointer to the T instead of to the -/// entire ArcInner. -/// -/// ```text -/// Arc RawOffsetArc -/// | | -/// v v -/// --------------------- -/// | RefCount | T (data) | [ArcInner] -/// --------------------- -/// ``` -/// -/// This means that this is a direct pointer to -/// its contained data (and can be read from by both C++ and Rust), -/// but we can also convert it to a "regular" Arc by removing the offset -#[derive(Eq)] -#[repr(C)] -pub struct RawOffsetArc { - ptr: NonNull, -} - -unsafe impl Send for RawOffsetArc {} -unsafe impl Sync for RawOffsetArc {} - -impl Deref for RawOffsetArc { - type Target = T; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr.as_ptr() } - } -} - -impl Clone for RawOffsetArc { - #[inline] - fn clone(&self) -> Self { - Arc::into_raw_offset(self.clone_arc()) - } -} - -impl Drop for RawOffsetArc { - fn drop(&mut self) { - let _ = Arc::from_raw_offset(RawOffsetArc { ptr: self.ptr.clone() }); - } -} - -impl fmt::Debug for RawOffsetArc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl PartialEq for RawOffsetArc { - fn eq(&self, other: &RawOffsetArc) -> bool { - *(*self) == *(*other) - } - - fn ne(&self, other: &RawOffsetArc) -> bool { - *(*self) != *(*other) - } -} - -impl RawOffsetArc { - /// Temporarily converts |self| into a bonafide Arc and exposes it to the - /// provided callback. The refcount is not modified. - #[inline] - pub fn with_arc(&self, f: F) -> U - where - F: FnOnce(&Arc) -> U, - { - // Synthesize transient Arc, which never touches the refcount of the ArcInner. - let transient = unsafe { NoDrop::new(Arc::from_raw(self.ptr.as_ptr())) }; - - // Expose the transient Arc to the callback, which may clone it if it wants. - let result = f(&transient); - - // Forget the transient Arc to leave the refcount untouched. - // XXXManishearth this can be removed when unions stabilize, - // since then NoDrop becomes zero overhead - mem::forget(transient); - - // Forward the result. - result - } - - /// If uniquely owned, provide a mutable reference - /// Else create a copy, and mutate that - #[inline] - pub fn make_mut(&mut self) -> &mut T - where - T: Clone, - { - unsafe { - // extract the RawOffsetArc as an owned variable - let this = ptr::read(self); - // treat it as a real Arc - let mut arc = Arc::from_raw_offset(this); - // obtain the mutable reference. Cast away the lifetime - // This may mutate `arc` - let ret = Arc::make_mut(&mut arc) as *mut _; - // Store the possibly-mutated arc back inside, after converting - // it to a RawOffsetArc again - ptr::write(self, Arc::into_raw_offset(arc)); - &mut *ret - } - } - - /// Clone it as an Arc - #[inline] - pub fn clone_arc(&self) -> Arc { - RawOffsetArc::with_arc(self, |a| a.clone()) - } - - /// Produce a pointer to the data that can be converted back - /// to an arc - #[inline] - pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> { - ArcBorrow(&**self) - } -} - -impl Arc { - /// Converts an Arc into a RawOffsetArc. This consumes the Arc, so the refcount - /// is not modified. - #[inline] - pub fn into_raw_offset(a: Self) -> RawOffsetArc { - RawOffsetArc { - // safety: as above - ptr: unsafe { NonNull::new_unchecked(Arc::into_raw(a) as *mut T) }, - } - } - - /// Converts a RawOffsetArc into an Arc. This consumes the RawOffsetArc, so the refcount - /// is not modified. - #[inline] - pub fn from_raw_offset(a: RawOffsetArc) -> Self { - let ptr = a.ptr.as_ptr(); - mem::forget(a); - unsafe { Arc::from_raw(ptr) } - } -} - -/// A "borrowed Arc". This is a pointer to -/// a T that is known to have been allocated within an -/// Arc. -/// -/// This is equivalent in guarantees to `&Arc`, however it is -/// a bit more flexible. To obtain an `&Arc` you must have -/// an Arc instance somewhere pinned down until we're done with it. -/// -/// However, Gecko hands us refcounted things as pointers to T directly, -/// so we have to conjure up a temporary Arc on the stack each time. The -/// same happens for when the object is managed by a RawOffsetArc. -/// -/// ArcBorrow lets us deal with borrows of known-refcounted objects -/// without needing to worry about how they're actually stored. -#[derive(Eq, PartialEq)] -pub struct ArcBorrow<'a, T: 'a>(&'a T); - -impl<'a, T> Copy for ArcBorrow<'a, T> {} -impl<'a, T> Clone for ArcBorrow<'a, T> { - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl<'a, T> ArcBorrow<'a, T> { - #[inline] - pub fn clone_arc(&self) -> Arc { - let arc = unsafe { Arc::from_raw(self.0) }; - // addref it! - mem::forget(arc.clone()); - arc - } - - /// For constructing from a reference known to be Arc-backed, - /// e.g. if we obtain such a reference over FFI - #[inline] - pub unsafe fn from_ref(r: &'a T) -> Self { - ArcBorrow(r) - } - - #[inline] - pub fn with_arc(&self, f: F) -> U - where - F: FnOnce(&Arc) -> U, - { - // Synthesize transient Arc, which never touches the refcount. - let transient = unsafe { NoDrop::new(Arc::from_raw(self.0)) }; - - // Expose the transient Arc to the callback, which may clone it if it wants. - let result = f(&transient); - - // Forget the transient Arc to leave the refcount untouched. - // XXXManishearth this can be removed when unions stabilize, - // since then NoDrop becomes zero overhead - mem::forget(transient); - - // Forward the result. - result - } -} - -impl<'a, T> Deref for ArcBorrow<'a, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - &*self.0 - } -} - -#[cfg(test)] -mod tests { - use super::{Arc, HeaderWithLength, ThinArc}; - use std::{ - clone::Clone, - ops::Drop, - sync::{ - atomic, - atomic::Ordering::{Acquire, SeqCst}, - }, - }; - - #[derive(PartialEq)] - struct Canary(*mut atomic::AtomicUsize); - - impl Drop for Canary { - fn drop(&mut self) { - unsafe { - (*self.0).fetch_add(1, SeqCst); - } - } - } - - #[test] - fn slices_and_thin() { - let mut canary = atomic::AtomicUsize::new(0); - let c = Canary(&mut canary as *mut atomic::AtomicUsize); - let v = vec![5, 6]; - let header = HeaderWithLength::new(c, v.len()); - { - let x = Arc::into_thin(Arc::from_header_and_iter(header, v.into_iter())); - let y = ThinArc::with_arc(&x, |q| q.clone()); - let _ = y.clone(); - let _ = x == x; - Arc::from_thin(x.clone()); - } - assert_eq!(canary.load(Acquire), 1); - } -} diff --git a/src/green/node.rs b/src/green/node.rs index 8a5af9d..1eab4d4 100644 --- a/src/green/node.rs +++ b/src/green/node.rs @@ -7,10 +7,10 @@ use std::{ use fxhash::FxHasher32; use crate::{ - arc::{Arc, HeaderWithLength, ThinArc}, green::{GreenElement, GreenElementRef, PackedGreenElement, SyntaxKind}, TextSize, }; +use triomphe::{Arc, HeaderWithLength, ThinArc}; #[repr(align(2))] //to use 1 bit for pointer tagging. NB: this is an at-least annotation #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/green/token.rs b/src/green/token.rs index 20e12b3..01c1cc5 100644 --- a/src/green/token.rs +++ b/src/green/token.rs @@ -1,7 +1,8 @@ use std::{fmt, hash, mem::ManuallyDrop, ptr}; -use crate::{arc::Arc, green::SyntaxKind, interning::Resolver, TextSize}; +use crate::{green::SyntaxKind, interning::Resolver, TextSize}; use lasso::Spur; +use triomphe::Arc; #[repr(align(2))] // to use 1 bit for pointer tagging. NB: this is an at-least annotation #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] @@ -87,7 +88,7 @@ impl Clone for GreenToken { let arc = ManuallyDrop::new(Arc::from_raw(ptr.as_ptr())); Arc::into_raw(Arc::clone(&arc)) }; - let ptr = ptr::NonNull::new(ptr as *mut _).unwrap(); + let ptr = unsafe { ptr::NonNull::new_unchecked(ptr as *mut _) }; GreenToken { ptr: Self::add_tag(ptr), } diff --git a/src/lib.rs b/src/lib.rs index d74450f..6e71cda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,8 +50,6 @@ )] #![deny(unsafe_code, missing_docs)] -#[allow(unsafe_code)] -mod arc; #[allow(unsafe_code)] mod green; #[allow(unsafe_code)] @@ -73,11 +71,11 @@ use std::fmt; pub use text_size::{TextLen, TextRange, TextSize}; pub use crate::{ - arc::Arc, green::{Checkpoint, Children, GreenNode, GreenNodeBuilder, GreenToken, NodeCache, SyntaxKind}, syntax::*, utility_types::{Direction, NodeOrToken, TokenAtOffset, WalkEvent}, }; +pub use triomphe::Arc; /// The `Language` trait is the bridge between the internal `cstree` representation and your language /// types. diff --git a/src/syntax/node.rs b/src/syntax/node.rs index 18c8d54..5c48cd3 100644 --- a/src/syntax/node.rs +++ b/src/syntax/node.rs @@ -2,7 +2,6 @@ use super::*; #[cfg(feature = "serde1")] use crate::serde_impls::{SerializeWithData, SerializeWithResolver}; use crate::{ - arc::Arc, green::{GreenElementRef, SyntaxKind}, interning::Resolver, *, @@ -18,6 +17,7 @@ use std::{ Arc as StdArc, }, }; +use triomphe::Arc; /// Inner syntax tree node. /// Syntax nodes can be shared between threads. diff --git a/tests/it/main.rs b/tests/it/main.rs index 7fff523..a1780f7 100644 --- a/tests/it/main.rs +++ b/tests/it/main.rs @@ -1,4 +1,5 @@ mod basic; +mod regressions; mod sendsync; #[cfg(feature = "serde1")] mod serde; diff --git a/tests/it/regressions.rs b/tests/it/regressions.rs new file mode 100644 index 0000000..bf180de --- /dev/null +++ b/tests/it/regressions.rs @@ -0,0 +1,34 @@ +#[test] +fn empty_tree_arc() { + // this test is not here for the test itself, but to run it through MIRI, who complained about out-of-bound + // `ThinArc` pointers for a root `GreenNode` with no children + + use cstree::*; + #[allow(non_camel_case_types)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[repr(u16)] + enum SyntaxKind { + Root, + } + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + enum Lang {} + impl cstree::Language for Lang { + // ... + type Kind = SyntaxKind; + + fn kind_from_raw(raw: cstree::SyntaxKind) -> Self::Kind { + assert!(raw.0 <= SyntaxKind::Root as u16); + unsafe { std::mem::transmute::(raw.0) } + } + + fn kind_to_raw(kind: Self::Kind) -> cstree::SyntaxKind { + cstree::SyntaxKind(kind as u16) + } + } + let mut builder = GreenNodeBuilder::new(); + builder.start_node(SyntaxKind(0)); + builder.finish_node(); + let (green, _) = builder.finish(); + let root: SyntaxNode = SyntaxNode::new_root(green); + assert_eq!(root.kind(), SyntaxKind::Root); +}