aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout/flow_ref.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout/flow_ref.rs')
-rw-r--r--components/layout/flow_ref.rs197
1 files changed, 12 insertions, 185 deletions
diff --git a/components/layout/flow_ref.rs b/components/layout/flow_ref.rs
index 65c07f81f19..d1717ef2000 100644
--- a/components/layout/flow_ref.rs
+++ b/components/layout/flow_ref.rs
@@ -8,194 +8,21 @@
//! be superfluous. This design is largely duplicating logic of Arc<T> and
//! Weak<T>; please see comments there for details.
-#![allow(unsafe_code)]
-use flow;
-use flow::{Flow, BaseFlow};
+use flow::Flow;
+use std::sync::{Arc, Weak};
-use std::mem;
-use std::ops::{Deref, DerefMut};
-use std::ptr;
-use std::raw;
-use std::rt::heap;
-use std::sync::atomic::{self, Ordering};
+pub type FlowRef = Arc<Flow>;
+pub type WeakFlowRef = Weak<Flow>;
-#[unsafe_no_drop_flag]
-pub struct FlowRef {
- object: raw::TraitObject,
-}
-
-unsafe impl Send for FlowRef {}
-unsafe impl Sync for FlowRef {}
-
-#[unsafe_no_drop_flag]
-pub struct WeakFlowRef {
- object: raw::TraitObject,
-}
-
-unsafe impl Send for WeakFlowRef {}
-unsafe impl Sync for WeakFlowRef {}
-
-impl FlowRef {
- pub fn new(mut flow: Box<Flow>) -> FlowRef {
- unsafe {
- let result = {
- let flow_ref: &mut Flow = &mut *flow;
- let object = mem::transmute::<&mut Flow, raw::TraitObject>(flow_ref);
- FlowRef { object: object }
- };
- mem::forget(flow);
- result
- }
- }
-
- /// Downgrades the FlowRef to a WeakFlowRef.
- pub fn downgrade(&self) -> WeakFlowRef {
- unsafe {
- flow::base(&**self).weak_ref_count().fetch_add(1, Ordering::Relaxed);
- }
- WeakFlowRef { object: self.object }
- }
-}
-
-impl<'a> Deref for FlowRef {
- type Target = Flow + 'a;
- fn deref(&self) -> &(Flow + 'a) {
- unsafe {
- mem::transmute_copy::<raw::TraitObject, &(Flow + 'a)>(&self.object)
- }
- }
-}
-
-impl DerefMut for FlowRef {
- fn deref_mut<'a>(&mut self) -> &mut (Flow + 'a) {
- unsafe {
- mem::transmute_copy::<raw::TraitObject, &mut (Flow + 'a)>(&self.object)
- }
- }
-}
-
-impl Drop for FlowRef {
- fn drop(&mut self) {
- unsafe {
- if self.object.vtable.is_null() ||
- self.object.vtable as usize == mem::POST_DROP_USIZE {
- return
- }
- if flow::base(&**self).strong_ref_count().fetch_sub(1, Ordering::Release) != 1 {
- return
- }
- atomic::fence(Ordering::Acquire);
-
- // Normally we'd call the underlying Drop logic but not free the
- // allocation, but this is almost impossible without DST in
- // Rust. Instead we make a fake trait object to run the drop logic
- // on.
- let flow_ref: FlowRef = mem::replace(self, FlowRef {
- object: raw::TraitObject {
- vtable: ptr::null_mut(),
- data: ptr::null_mut(),
- }
- });
-
- let vtable: &[usize; 3] = mem::transmute::<*mut (), &[usize; 3]>(flow_ref.object.vtable);
- let object_size = vtable[1];
- let object_align = vtable[2];
-
- let fake_data = heap::allocate(object_size, object_align);
- ptr::copy(flow_ref.object.data as *const u8, fake_data, object_size);
-
- let fake_box = raw::TraitObject { vtable: flow_ref.object.vtable, data: fake_data as *mut () };
- let fake_flow = mem::transmute::<raw::TraitObject, Box<Flow>>(fake_box);
- drop(fake_flow);
-
- if flow::base(&*flow_ref).weak_ref_count().fetch_sub(1, Ordering::Release) == 1 {
- atomic::fence(Ordering::Acquire);
- heap::deallocate(flow_ref.object.data as *mut u8, object_size, object_align);
- }
-
- mem::forget(flow_ref);
- }
- }
-}
-
-impl Clone for FlowRef {
- fn clone(&self) -> FlowRef {
- unsafe {
- let _ = flow::base(&**self).strong_ref_count().fetch_add(1, Ordering::Relaxed);
- FlowRef {
- object: raw::TraitObject {
- vtable: self.object.vtable,
- data: self.object.data,
- }
- }
- }
- }
-}
-
-fn base<'a>(r: &WeakFlowRef) -> &'a BaseFlow {
- let data = r.object.data;
- debug_assert!(!data.is_null());
+/// WARNING: This should only be used when there is no aliasing:
+/// when the traversal ensures that no other threads accesses the same flow at the same time.
+/// See https://github.com/servo/servo/issues/6503
+/// Use Arc::get_mut instead when possible (e.g. on an Arc that was just created).
+#[allow(unsafe_code)]
+pub fn deref_mut<'a>(r: &'a mut FlowRef) -> &'a mut Flow {
+ let ptr: *const Flow = &**r;
unsafe {
- mem::transmute::<*mut (), &'a BaseFlow>(data)
- }
-}
-
-impl WeakFlowRef {
- /// Upgrades a WeakFlowRef to a FlowRef.
- pub fn upgrade(&self) -> Option<FlowRef> {
- unsafe {
- let object = base(self);
- // We use a CAS loop to increment the strong count instead of a
- // fetch_add because once the count hits 0 is must never be above
- // 0.
- loop {
- let n = object.strong_ref_count().load(Ordering::SeqCst);
- if n == 0 { return None }
- let old = object.strong_ref_count().compare_and_swap(n, n + 1, Ordering::SeqCst);
- if old == n {
- return Some(FlowRef { object: self.object })
- }
- }
- }
- }
-}
-
-impl Clone for WeakFlowRef {
- fn clone(&self) -> WeakFlowRef {
- unsafe {
- base(self).weak_ref_count().fetch_add(1, Ordering::Relaxed);
- }
- WeakFlowRef { object: self. object }
- }
-}
-
-impl Drop for WeakFlowRef {
- fn drop(&mut self) {
- unsafe {
- if self.object.vtable.is_null() ||
- self.object.vtable as usize == mem::POST_DROP_USIZE {
- return
- }
-
- if base(self).weak_ref_count().fetch_sub(1, Ordering::Release) == 1 {
- atomic::fence(Ordering::Acquire);
-
- // This dance deallocates the Box<Flow> without running its
- // drop glue. The drop glue is run when the last strong
- // reference is released.
- let weak_ref: WeakFlowRef = mem::replace(self, WeakFlowRef {
- object: raw::TraitObject {
- vtable: ptr::null_mut(),
- data: ptr::null_mut(),
- }
- });
- let vtable: &[usize; 3] = mem::transmute::<*mut (), &[usize; 3]>(weak_ref.object.vtable);
- let object_size = vtable[1];
- let object_align = vtable[2];
- heap::deallocate(weak_ref.object.data as *mut u8, object_size, object_align);
- mem::forget(weak_ref);
- }
- }
+ &mut *(ptr as *mut Flow)
}
}