mirror of
https://github.com/servo/servo.git
synced 2025-07-18 21:03:45 +01:00
script: Reduce usage of Trusted in Node::insert. (#37762)
These changes introduce a new kind of task that uses a variation of the `task!` syntax. Existing `task!` usages create task structs that have a `Send` bound, which requires the use of `Trusted<T>` to reference a DOM object T inside of the task closure. The new syntax replaces the `Send` bound with a `JSTraceable` bound, which requires explicit capture clauses with types. This looks like: ```rust task!(ScriptPrepare: {script: DomRoot<HTMLScriptElement>} |script| { script.prepare(CanGc::note()); }), ``` The capture clauses must list every value that will be referenced from the closure's environment—these values are moved into fields in the generated task structure, which allows them to be traced by the GC as part of a generated JSTraceable implementation. Since the closure itself is not a `move` closure, any attempts to reference values not explicitly captured will generate a borrow checker error since the closure requires the `'static` lifetime. Testing: Existing WPT tests exercise these code paths. I also attempted to write incorrect tasks that capture references or use values not explicitly captured, and the compiler correctly errors out. Fixes: part of #35517 Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
parent
f5b0165e54
commit
9e2ee0029a
5 changed files with 74 additions and 27 deletions
|
@ -9,6 +9,36 @@ use std::sync::Arc;
|
|||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
macro_rules! task {
|
||||
($name:ident: |$($field:ident: $field_type:ty$(,)*)*| $body:tt) => {{
|
||||
#[allow(non_camel_case_types)]
|
||||
struct $name<F> {
|
||||
$($field: $field_type,)*
|
||||
task: F,
|
||||
}
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl<F> crate::JSTraceable for $name<F> {
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn trace(&self, tracer: *mut ::js::jsapi::JSTracer) {
|
||||
$(self.$field.trace(tracer);)*
|
||||
// We cannot trace the actual task closure. This is safe because
|
||||
// all referenced values from within the closure are either borrowed
|
||||
// or moved into fields in the struct (and therefore traced).
|
||||
}
|
||||
}
|
||||
impl<F> crate::task::NonSendTaskOnce for $name<F>
|
||||
where
|
||||
F: ::std::ops::FnOnce($($field_type,)*),
|
||||
{
|
||||
fn run_once(self) {
|
||||
(self.task)($(self.$field,)*);
|
||||
}
|
||||
}
|
||||
$name {
|
||||
$($field,)*
|
||||
task: |$($field: $field_type,)*| $body,
|
||||
}
|
||||
}};
|
||||
|
||||
($name:ident: move || $body:tt) => {{
|
||||
#[allow(non_camel_case_types)]
|
||||
struct $name<F>(F);
|
||||
|
@ -28,9 +58,9 @@ macro_rules! task {
|
|||
}};
|
||||
}
|
||||
|
||||
/// A task that can be run. The name method is for profiling purposes.
|
||||
/// A task that can be sent between threads and run.
|
||||
/// The name method is for profiling purposes.
|
||||
pub(crate) trait TaskOnce: Send {
|
||||
#[allow(unsafe_code)]
|
||||
fn name(&self) -> &'static str {
|
||||
::std::any::type_name::<Self>()
|
||||
}
|
||||
|
@ -38,6 +68,11 @@ pub(crate) trait TaskOnce: Send {
|
|||
fn run_once(self);
|
||||
}
|
||||
|
||||
/// A task that must be run on the same thread it originated in.
|
||||
pub(crate) trait NonSendTaskOnce: crate::JSTraceable {
|
||||
fn run_once(self);
|
||||
}
|
||||
|
||||
/// A boxed version of `TaskOnce`.
|
||||
pub(crate) trait TaskBox: Send {
|
||||
fn name(&self) -> &'static str;
|
||||
|
@ -45,6 +80,20 @@ pub(crate) trait TaskBox: Send {
|
|||
fn run_box(self: Box<Self>);
|
||||
}
|
||||
|
||||
/// A boxed version of `NonSendTaskOnce`.
|
||||
pub(crate) trait NonSendTaskBox: crate::JSTraceable {
|
||||
fn run_box(self: Box<Self>);
|
||||
}
|
||||
|
||||
impl<T> NonSendTaskBox for T
|
||||
where
|
||||
T: NonSendTaskOnce,
|
||||
{
|
||||
fn run_box(self: Box<Self>) {
|
||||
self.run_once()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TaskBox for T
|
||||
where
|
||||
T: TaskOnce,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue