diff options
Diffstat (limited to 'components/script/dom/bindings/refcounted.rs')
-rw-r--r-- | components/script/dom/bindings/refcounted.rs | 149 |
1 files changed, 99 insertions, 50 deletions
diff --git a/components/script/dom/bindings/refcounted.rs b/components/script/dom/bindings/refcounted.rs index dcf11626543..ab7aea3e922 100644 --- a/components/script/dom/bindings/refcounted.rs +++ b/components/script/dom/bindings/refcounted.rs @@ -1,6 +1,6 @@ /* 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/. */ + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! A generic, safe mechanism by which DOM objects can be pinned and transferred //! between threads (or intra-thread for asynchronous events). Akin to Gecko's @@ -11,7 +11,7 @@ //! To guarantee the lifetime of a DOM object when performing asynchronous operations, //! obtain a `Trusted<T>` from that object and pass it along with each operation. //! A usable pointer to the original DOM object can be obtained on the script thread -//! from a `Trusted<T>` via the `to_temporary` method. +//! from a `Trusted<T>` via the `root` method. //! //! The implementation of `Trusted<T>` is as follows: //! The `Trusted<T>` object contains an atomic reference counted pointer to the Rust DOM object. @@ -22,41 +22,43 @@ //! its hash table during the next GC. During GC, the entries of the hash table are counted //! as JS roots. -use core::nonzero::NonZero; -use dom::bindings::js::Root; -use dom::bindings::reflector::{DomObject, Reflector}; -use dom::bindings::trace::trace_reflector; -use dom::promise::Promise; +use crate::dom::bindings::conversions::ToJSValConvertible; +use crate::dom::bindings::error::Error; +use crate::dom::bindings::reflector::{DomObject, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::trace::trace_reflector; +use crate::dom::promise::Promise; +use crate::task::TaskOnce; use js::jsapi::JSTracer; -use libc; use std::cell::RefCell; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_map::HashMap; use std::hash::Hash; use std::marker::PhantomData; -use std::os; use std::rc::Rc; use std::sync::{Arc, Weak}; - -#[allow(missing_docs)] // FIXME -mod dummy { // Attributes don’t apply through the macro. +#[allow(missing_docs)] // FIXME +mod dummy { + // Attributes don’t apply through the macro. + use super::LiveDOMReferences; use std::cell::RefCell; use std::rc::Rc; - use super::LiveDOMReferences; thread_local!(pub static LIVE_REFERENCES: Rc<RefCell<Option<LiveDOMReferences>>> = Rc::new(RefCell::new(None))); } pub use self::dummy::LIVE_REFERENCES; - /// A pointer to a Rust DOM object that needs to be destroyed. -pub struct TrustedReference(*const libc::c_void); +struct TrustedReference(*const libc::c_void); unsafe impl Send for TrustedReference {} impl TrustedReference { - fn new<T: DomObject>(ptr: *const T) -> TrustedReference { - TrustedReference(ptr as *const libc::c_void) + /// Creates a new TrustedReference from a pointer to a value that impements DOMObject. + /// This is not enforced by the type system to reduce duplicated generic code, + /// which is acceptable since this method is internal to this module. + unsafe fn new(ptr: *const libc::c_void) -> TrustedReference { + TrustedReference(ptr) } } @@ -92,41 +94,72 @@ impl TrustedPromise { /// Obtain a usable DOM Promise from a pinned `TrustedPromise` value. Fails if used on /// a different thread than the original value from which this `TrustedPromise` was /// obtained. - #[allow(unrooted_must_root)] pub fn root(self) -> Rc<Promise> { LIVE_REFERENCES.with(|ref r| { let r = r.borrow(); let live_references = r.as_ref().unwrap(); - assert!(self.owner_thread == (&*live_references) as *const _ as *const libc::c_void); + assert_eq!( + self.owner_thread, + (&*live_references) as *const _ as *const libc::c_void + ); // Borrow-check error requires the redundant `let promise = ...; promise` here. - let promise = match live_references.promise_table.borrow_mut().entry(self.dom_object) { + let promise = match live_references + .promise_table + .borrow_mut() + .entry(self.dom_object) + { Occupied(mut entry) => { let promise = { let promises = entry.get_mut(); - promises.pop().expect("rooted promise list unexpectedly empty") + promises + .pop() + .expect("rooted promise list unexpectedly empty") }; if entry.get().is_empty() { entry.remove(); } promise - } + }, Vacant(_) => unreachable!(), }; promise }) } + + /// A task which will reject the promise. + #[allow(unrooted_must_root)] + pub fn reject_task(self, error: Error) -> impl TaskOnce { + let this = self; + task!(reject_promise: move || { + debug!("Rejecting promise."); + this.root().reject_error(error); + }) + } + + /// A task which will resolve the promise. + #[allow(unrooted_must_root)] + pub fn resolve_task<T>(self, value: T) -> impl TaskOnce + where + T: ToJSValConvertible + Send, + { + let this = self; + task!(resolve_promise: move || { + debug!("Resolving promise."); + this.root().resolve_native(&value); + }) + } } /// A safe wrapper around a raw pointer to a DOM object that can be /// shared among threads for use in asynchronous operations. The underlying /// DOM object is guaranteed to live at least as long as the last outstanding /// `Trusted<T>` instance. -#[allow_unrooted_interior] +#[unrooted_must_root_lint::allow_unrooted_interior] pub struct Trusted<T: DomObject> { /// A pointer to the Rust DOM object of type T, but void to allow /// sending `Trusted<T>` between threads, regardless of T's sendability. refcount: Arc<TrustedReference>, - owner_thread: *const libc::c_void, + owner_thread: *const LiveDOMReferences, phantom: PhantomData<T>, } @@ -137,30 +170,38 @@ impl<T: DomObject> Trusted<T> { /// be prevented from being GCed for the duration of the resulting `Trusted<T>` object's /// lifetime. pub fn new(ptr: &T) -> Trusted<T> { - LIVE_REFERENCES.with(|ref r| { - let r = r.borrow(); - let live_references = r.as_ref().unwrap(); - let refcount = live_references.addref(&*ptr as *const T); - Trusted { - refcount: refcount, - owner_thread: (&*live_references) as *const _ as *const libc::c_void, - phantom: PhantomData, - } - }) + fn add_live_reference( + ptr: *const libc::c_void, + ) -> (Arc<TrustedReference>, *const LiveDOMReferences) { + LIVE_REFERENCES.with(|ref r| { + let r = r.borrow(); + let live_references = r.as_ref().unwrap(); + let refcount = unsafe { live_references.addref(ptr) }; + (refcount, live_references as *const _) + }) + } + + let (refcount, owner_thread) = add_live_reference(ptr as *const T as *const _); + Trusted { + refcount, + owner_thread, + phantom: PhantomData, + } } /// Obtain a usable DOM pointer from a pinned `Trusted<T>` value. Fails if used on /// a different thread than the original value from which this `Trusted<T>` was /// obtained. - pub fn root(&self) -> Root<T> { - assert!(LIVE_REFERENCES.with(|ref r| { - let r = r.borrow(); - let live_references = r.as_ref().unwrap(); - self.owner_thread == (&*live_references) as *const _ as *const libc::c_void - })); - unsafe { - Root::new(NonZero::new(self.refcount.0 as *const T)) + pub fn root(&self) -> DomRoot<T> { + fn validate(owner_thread: *const LiveDOMReferences) { + assert!(LIVE_REFERENCES.with(|ref r| { + let r = r.borrow(); + let live_references = r.as_ref().unwrap(); + owner_thread == live_references + })); } + validate(self.owner_thread); + unsafe { DomRoot::from_ref(&*(self.refcount.0 as *const T)) } } } @@ -194,13 +235,22 @@ impl LiveDOMReferences { }); } + pub fn destruct() { + LIVE_REFERENCES.with(|ref r| { + *r.borrow_mut() = None; + }); + } + #[allow(unrooted_must_root)] fn addref_promise(&self, promise: Rc<Promise>) { let mut table = self.promise_table.borrow_mut(); table.entry(&*promise).or_insert(vec![]).push(promise) } - fn addref<T: DomObject>(&self, ptr: *const T) -> Arc<TrustedReference> { + /// ptr must be a pointer to a type that implements DOMObject. + /// This is not enforced by the type system to reduce duplicated generic code, + /// which is acceptable since this method is internal to this module. + unsafe fn addref(&self, ptr: *const libc::c_void) -> Arc<TrustedReference> { let mut table = self.reflectable_table.borrow_mut(); let capacity = table.capacity(); let len = table.len(); @@ -209,7 +259,7 @@ impl LiveDOMReferences { remove_nulls(&mut table); table.reserve(len); } - match table.entry(ptr as *const libc::c_void) { + match table.entry(ptr) { Occupied(mut entry) => match entry.get().upgrade() { Some(refcount) => refcount, None => { @@ -222,15 +272,15 @@ impl LiveDOMReferences { let refcount = Arc::new(TrustedReference::new(ptr)); entry.insert(Arc::downgrade(&refcount)); refcount - } + }, } } } /// Remove null entries from the live references table -fn remove_nulls<K: Eq + Hash + Clone, V> (table: &mut HashMap<K, Weak<V>>) { - let to_remove: Vec<K> = - table.iter() +fn remove_nulls<K: Eq + Hash + Clone, V>(table: &mut HashMap<K, Weak<V>>) { + let to_remove: Vec<K> = table + .iter() .filter(|&(_, value)| Weak::upgrade(value).is_none()) .map(|(key, _)| key.clone()) .collect(); @@ -242,8 +292,7 @@ fn remove_nulls<K: Eq + Hash + Clone, V> (table: &mut HashMap<K, Weak<V>>) { /// A JSTraceDataOp for tracing reflectors held in LIVE_REFERENCES #[allow(unrooted_must_root)] -pub unsafe extern "C" fn trace_refcounted_objects(tracer: *mut JSTracer, - _data: *mut os::raw::c_void) { +pub unsafe fn trace_refcounted_objects(tracer: *mut JSTracer) { info!("tracing live refcounted references"); LIVE_REFERENCES.with(|ref r| { let r = r.borrow(); |