diff options
author | Bobby Holley <bobbyholley@gmail.com> | 2017-06-04 19:14:14 -0700 |
---|---|---|
committer | Bobby Holley <bobbyholley@gmail.com> | 2017-06-05 19:44:09 -0700 |
commit | ce8d2a920857fd85cce3480ffb1b44da1bda8010 (patch) | |
tree | 0d6e07e7b9b741b0b4928e9b771597da4614d042 | |
parent | e8a56e969f38b48f535a97cfad57008f57c0f4aa (diff) | |
download | servo-ce8d2a920857fd85cce3480ffb1b44da1bda8010.tar.gz servo-ce8d2a920857fd85cce3480ffb1b44da1bda8010.zip |
Implement ThinArc.
MozReview-Commit-ID: LB3bs9ed2WC
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | components/servo_arc/Cargo.toml | 1 | ||||
-rw-r--r-- | components/servo_arc/lib.rs | 164 |
3 files changed, 166 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock index 8ed951c806f..b5981a9cf6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2694,6 +2694,7 @@ name = "servo_arc" version = "0.0.1" dependencies = [ "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/components/servo_arc/Cargo.toml b/components/servo_arc/Cargo.toml index 578b845da53..ffb743bd20a 100644 --- a/components/servo_arc/Cargo.toml +++ b/components/servo_arc/Cargo.toml @@ -15,3 +15,4 @@ servo = ["serde", "heapsize"] [dependencies] heapsize = {version = "0.4.0", optional = true} serde = {version = "0.9", optional = true} +nodrop = {version = "0.1.8"} diff --git a/components/servo_arc/lib.rs b/components/servo_arc/lib.rs index 41ebc7f1bf1..5d91c9bc44f 100644 --- a/components/servo_arc/lib.rs +++ b/components/servo_arc/lib.rs @@ -14,6 +14,7 @@ //! * 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 @@ -22,9 +23,11 @@ #![allow(missing_docs)] #[cfg(feature = "servo")] extern crate serde; +extern crate nodrop; #[cfg(feature = "servo")] use heapsize::HeapSizeOf; +use nodrop::NoDrop; #[cfg(feature = "servo")] use serde::{Deserialize, Serialize}; use std::{isize, usize}; @@ -511,3 +514,164 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> { Arc { ptr: ptr } } } + +/// 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<H> { + /// The fixed-sized data. + pub header: H, + + /// The slice length. + length: usize, +} + +impl<H> HeaderWithLength<H> { + /// Creates a new HeaderWithLength. + pub fn new(header: H, length: usize) -> Self { + HeaderWithLength { + header: header, + length: length, + } + } +} + +pub struct ThinArc<H, T> { + ptr: *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T; 1]>>, +} + +unsafe impl<H: Sync + Send, T: Sync + Send> Send for ThinArc<H, T> {} +unsafe impl<H: Sync + Send, T: Sync + Send> Sync for ThinArc<H, T> {} + +// Synthesize a fat pointer from a thin pointer. +// +// See the comment around the analogous operation in from_header_and_iter. +fn thin_to_thick<H, T>(thin: *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T; 1]>>) + -> *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T]>> +{ + 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<HeaderSlice<HeaderWithLength<H>, [T]>> +} + +impl<H, T> ThinArc<H, T> { + /// Temporarily converts |self| into a bonafide Arc and exposes it to the + /// provided callback. The refcount is not modified. + #[inline(always)] + pub fn with_arc<F, U>(&self, f: F) -> U + where F: FnOnce(&Arc<HeaderSlice<HeaderWithLength<H>, [T]>>) -> U + { + // Synthesize transient Arc, which never touches the refcount of the ArcInner. + let transient = NoDrop::new(Arc { + ptr: 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. + mem::forget(transient); + + // Forward the result. + result + } +} + +impl<H, T> Deref for ThinArc<H, T> { + type Target = HeaderSlice<HeaderWithLength<H>, [T]>; + fn deref(&self) -> &Self::Target { + unsafe { &(*thin_to_thick(self.ptr)).data } + } +} + +impl<H, T> Clone for ThinArc<H, T> { + fn clone(&self) -> Self { + ThinArc::with_arc(self, |a| Arc::into_thin(a.clone())) + } +} + +impl<H, T> Drop for ThinArc<H, T> { + fn drop(&mut self) { + let _ = Arc::from_thin(ThinArc { ptr: self.ptr }); + } +} + +impl<H, T> Arc<HeaderSlice<HeaderWithLength<H>, [T]>> { + /// Converts an Arc into a ThinArc. This consumes the Arc, so the refcount + /// is not modified. + pub fn into_thin(a: Self) -> ThinArc<H, T> { + assert!(a.header.length == a.slice.len(), + "Length needs to be correct for ThinArc to work"); + let fat_ptr: *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T]>> = a.ptr; + mem::forget(a); + let thin_ptr = fat_ptr as *mut [usize] as *mut usize; + ThinArc { + ptr: thin_ptr as *mut ArcInner<HeaderSlice<HeaderWithLength<H>, [T; 1]>> + } + } + + /// Converts a ThinArc into an Arc. This consumes the ThinArc, so the refcount + /// is not modified. + pub fn from_thin(a: ThinArc<H, T>) -> Self { + let ptr = thin_to_thick(a.ptr); + mem::forget(a); + Arc { + ptr: ptr + } + } +} + +impl<H: PartialEq, T: PartialEq> PartialEq for ThinArc<H, T> { + fn eq(&self, other: &ThinArc<H, T>) -> bool { + ThinArc::with_arc(self, |a| { + ThinArc::with_arc(other, |b| { + *a == *b + }) + }) + } +} + +impl<H: Eq, T: Eq> Eq for ThinArc<H, T> {} + +#[cfg(test)] +mod tests { + use std::clone::Clone; + use std::ops::Drop; + use std::sync::atomic; + use std::sync::atomic::Ordering::{Acquire, SeqCst}; + use super::{Arc, HeaderWithLength, ThinArc}; + + #[derive(PartialEq)] + struct Canary(*mut atomic::AtomicUsize); + + impl Drop for Canary { + fn drop(&mut self) { + unsafe { + match *self { + Canary(c) => { + (*c).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!(canary.load(Acquire) == 1); + } +} |