Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upUnify the task source and task canceller API #21804
Conversation
highfive
commented
Sep 24, 2018
|
Heads up! This PR modifies the following files:
|
highfive
commented
Sep 24, 2018
|
Thanks! I will look at this later this week! |
|
Is it just me, or is the Should we perhaps remove the cc @cbrewster @jdm |
|
Looks like a very good step in the right direction. I have one question on the overall design... |
| @@ -1207,6 +1214,8 @@ impl WindowMethods for Window { | |||
| } | |||
| } | |||
|
|
|||
| pub struct TaskManagement<T: TaskSource>(pub T, pub TaskCanceller); | |||
This comment has been minimized.
This comment has been minimized.
gterzian
Sep 26, 2018
•
Member
It'd be possible to unify *_task_source in such a way that we would
have only one method.
Have you considered making this a struct that would completely take over the task management from a Window?
Perhaps something like:
struct TaskManager {
task_cancellers: DomRefCell<HashMap<TaskSourceName, Arc<AtomicBool>>>,
task_sources: DomRefCell<HashMap<TaskSourceName, Box<TaskSource>>>
}
impl TaskManager {
fn new(task_sources: HashMap<TaskSourceName, Box<TaskSource>>) -> Self;
fn task_source_with_canceller(&self, name: TaskSourceName) -> (Box<TaskSource>, TaskCanceller);
fn task_source(&self, name: TaskSourceName)-> Box<TaskSource>;
fn task_canceller(&self, name: TaskSourceName)-> TaskCanceller;
}Perhaps it could be initialized when a new page loads, passed to Window::new, and basically replace all the task source related methods and fields on Window with one public task_manager field?
one of the
TaskSource implementations is special:
history_traversal_task_source. Not wanting to over complicate things,
I decided to leave the structure this way.
Perhaps if you were to implement TaskSource for HistoryTraversalTaskSource(or remove it if others confirm this is a good idea, see my other comment), it could work without requiring too much complication?
This comment has been minimized.
This comment has been minimized.
AgustinCB
Sep 26, 2018
Author
Contributor
I thought of that, but not knowing what to do with HistoryTraversalTaskSource made me chose something simpler. Truth being, I'd love to move responsibilities outside of Window (it's the most overwhelming file in the source code) :). Your proposal of adding a "fake" implementation for HistoryTraversalTaskSource sounds good to me and addresses the concern I had. Will address your feedback and push the new version in the next days.
Thanks for the suggestion.
This comment has been minimized.
This comment has been minimized.
gterzian
Sep 26, 2018
•
Member
Sounds good, you could also do an actually 'real' implementation, since we might actually need it later for #21815
I think basically a copy/paste from any of the other task-source, and adding a new ScriptThreadEventCategory, should do the trick...
This comment has been minimized.
This comment has been minimized.
AgustinCB
Sep 27, 2018
Author
Contributor
Unfortunately, TaskSource can't be made a trait object :(.
I do have the HistoryTraversalTaskSource implementation. Should I just do that? Or maybe we can somehow modify TaskSource to allow it to make a trait object?
This comment has been minimized.
This comment has been minimized.
AgustinCB
Sep 27, 2018
•
Author
Contributor
I guess I could also make a less elegant...
struct TaskManager {
task_cancellers: DomRefCell<HashMap<TaskSourceName, Arc<AtomicBool>>>,
*_task_sources: DomRefCell<*TaskSource>,
}
Although we would still have a lot of methods.
This comment has been minimized.
This comment has been minimized.
AgustinCB
Sep 28, 2018
Author
Contributor
Of course! Here it's:
error[E0038]: the trait `task_source::TaskSource` cannot be made into an object
--> components/script/task_manager.rs:119:5
|
119 | pub fn task_source(&self, name: TaskSourceName) -> Box<TaskSource> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `task_source::TaskSource` cannot be made into an object
|
= note: method `queue_with_canceller` has generic type parameters
= note: method `queue` has generic type parameters
= note: the trait cannot contain associated consts like `NAME
This comment has been minimized.
This comment has been minimized.
gterzian
Sep 28, 2018
Member
I see, so that's not going to work :)
I haven't found another way to avoid the repetition of methods, have you?
In any case, introducing a TaskManager that will lighten up Window on methods and fields is probably progress.
Thanks for pushing to additional commits, I will review them as is unless we come up with a better design...
This comment has been minimized.
This comment has been minimized.
AgustinCB
Sep 28, 2018
Author
Contributor
I tried to trick the compiler in a couple naive ways (i.e. using & mut instead of Box, wrapping the TaskSource in a structure that is object safe, etc.). As expected, the compiler outsmarted me.
Another thing I considered was just living with the duplication, but create a couple macros so it's less apparent. I didn't want to go through that route, because I didn't see any macro in the rest of the code and I know some projects are (rightfully) cautious about them. If this were a personal project, though, and the choice depended only on me, I'd probably do that.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
AgustinCB
Sep 29, 2018
Author
Contributor
Good to know! Will push (yet another) commit with how it would look like with macros. Thanks!
|
We don’t use the history task source, I tried using it so we would line up with the spec a bit more, but it ends up queuing a message on the task source to send a message to the constellation, which doesn’t make much sense. I would be okay with removing the history task source, but it really depends on how much we care about history spec compliance. |
|
@cbrewster Thanks for the info! I'll open a separate issue to discuss this... @AgustinCB I think in the context of this PR, we could implement a EDIT: I think an actual task source implementation similar to the others is probably better, since I just realized the history-traversal task queue and task source aren't completely related, see #21815 Could please do this implementation of |
ghost
commented
Sep 28, 2018
No Taskcluster jobs started for this pull requestThe `allowPullRequests` configuration for this repository (in `.taskcluster.yml` on the
default branch) does not allow starting tasks for this pull request. |
fe67621
to
abe1a44
ghost
commented
Sep 28, 2018
No Taskcluster jobs started for this pull requestThe `allowPullRequests` configuration for this repository (in `.taskcluster.yml` on the
default branch) does not allow starting tasks for this pull request. |
abe1a44
to
abdcefa
|
Looks great! Just a few nits really |
| @@ -137,7 +137,7 @@ impl AudioContextMethods for AudioContext { | |||
| if base_context.State() != AudioContextState::Suspended { | |||
| base_context.set_state_attribute(AudioContextState::Suspended); | |||
| let window = DomRoot::downcast::<Window>(context.global()).unwrap(); | |||
| window.dom_manipulation_task_source().queue_simple_event( | |||
| window.task_manager().dom_manipulation_task_source().queue_simple_event( | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| @@ -199,7 +199,7 @@ impl AudioContextMethods for AudioContext { | |||
| if base_context.State() != AudioContextState::Closed { | |||
| base_context.set_state_attribute(AudioContextState::Closed); | |||
| let window = DomRoot::downcast::<Window>(context.global()).unwrap(); | |||
| window.dom_manipulation_task_source().queue_simple_event( | |||
| window.task_manager().dom_manipulation_task_source().queue_simple_event( | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| let callback = OnEndedCallback::new(move || { | ||
| let _ = task_source.queue_with_canceller( | ||
| task!(ended: move || { | ||
| let this = this.root(); | ||
| let global = this.global(); | ||
| let window = global.as_window(); | ||
| window.dom_manipulation_task_source().queue_simple_event( | ||
| window.task_manager().dom_manipulation_task_source().queue_simple_event( |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| @@ -225,7 +225,7 @@ impl BaseAudioContext { | |||
| if this.state.get() != AudioContextState::Running { | |||
| this.state.set(AudioContextState::Running); | |||
| let window = DomRoot::downcast::<Window>(this.global()).unwrap(); | |||
| window.dom_manipulation_task_source().queue_simple_event( | |||
| window.task_manager().dom_manipulation_task_source().queue_simple_event( | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
AgustinCB
Sep 29, 2018
Author
Contributor
It's also borrowed before.
To be fair, this parts also bothered me. I suspect that there's some way in which this code can be refactor to call less clones. But I'm not sure it's immediately obvious.
Maybe I can offer you to open an issue to track the tech debt?
This comment has been minimized.
This comment has been minimized.
gterzian
Oct 1, 2018
•
Member
Ok, thanks for the info. I'm not sure at this point how to make a potential issue actionable, so I will not open one. If you happen to have a clearer idea, it could be useful if you were to open one indeed...
This comment has been minimized.
This comment has been minimized.
| win.dom_manipulation_task_source() | ||
| .queue_simple_event(target, atom!("close"), &win); | ||
| win.task_manager().dom_manipulation_task_source() | ||
| .queue_simple_event(target, atom!("close"), &win); |
This comment has been minimized.
This comment has been minimized.
gterzian
Sep 29, 2018
•
Member
nit: This is what rustfmt did with it(actually it appears the indent here is slightly different, the idea is 4 space ident I believe, starting at win.task_manager()):
// Step 5
win.task_manager()
.dom_manipulation_task_source()
.queue_simple_event(target, atom!("close"), &win);| @@ -588,11 +589,11 @@ impl HTMLMediaElement { | |||
|
|
|||
| let context = Arc::new(Mutex::new(HTMLMediaElementContext::new(self))); | |||
| let (action_sender, action_receiver) = ipc::channel().unwrap(); | |||
| let window = window_from_node(self); | |||
| let (task_source, canceller) = document.window().task_manager().networking_task_source_with_canceller(); | |||
This comment has been minimized.
This comment has been minimized.
gterzian
Sep 29, 2018
Member
nit: this one looks a little too long(you can check with ./mach test-tidy)
| self.task_canceller(TaskSourceName::DOMManipulation) | ||
| .wrap_task(task), | ||
| self.task_manager.task_canceller(TaskSourceName::DOMManipulation) | ||
| .wrap_task(task), |
This comment has been minimized.
This comment has been minimized.
| self.task_canceller(TaskSourceName::DOMManipulation) | ||
| .wrap_task(task), | ||
| self.task_manager.task_canceller(TaskSourceName::DOMManipulation) | ||
| .wrap_task(task), |
This comment has been minimized.
This comment has been minimized.
| @@ -0,0 +1,130 @@ | |||
| /* This Source Code Form is subject to the terms of the Mozilla Public | |||
This comment has been minimized.
This comment has been minimized.
gterzian
Sep 29, 2018
Member
Since this is a completely new file, you can safely run ./mach rustfmt -d components/script/task_manager.rs and apply the changes to the commit (requires installing first if you haven't, see https://github.com/rust-lang-nursery/rustfmt)
| T: TaskOnce + 'static, | ||
| { | ||
| let msg = MainThreadScriptMsg::Common(CommonScriptMsg::Task( | ||
| ScriptThreadEventCategory::DocumentEvent, |
This comment has been minimized.
This comment has been minimized.
gterzian
Sep 29, 2018
•
Member
I think you want to add a new ScriptThreadEventCategory::HistoryEvent(in the same commit)
4ebc260
to
c8268f6
|
Ok, looks good to me. I think you can squash it into two commits, one fro the history task source changes, and one for the rest. Thanks for tackling this! @jdm r? |
|
I agree with all of gterzian's earlier comments, so thank you for addressing them. Nice work on this! |
| @@ -173,21 +165,7 @@ pub struct Window { | |||
| #[ignore_malloc_size_of = "trait objects are hard"] | |||
| script_chan: MainThreadScriptChan, | |||
| #[ignore_malloc_size_of = "task sources are hard"] | |||
This comment has been minimized.
This comment has been minimized.
jdm
Oct 1, 2018
Member
Let's remove this annotation and add any required annotations inside of TaskManager instead.
This comment has been minimized.
This comment has been minimized.
AgustinCB
Oct 2, 2018
Author
Contributor
Sorry, I'm blocked in this I dunno how to get past: What do I have to do to have ignore_malloc_size_of in the TaskManager? I keep getting this error:
error[E0658]: The attribute `ignore_malloc_size_of` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
--> components/script/task_manager.rs:50:5
|
50 | #[ignore_malloc_size_of = "task sources are hard"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(custom_attribute)] to the crate attributes to enable
And I don't know how to solve it (I already tried the suggestion).
Thanks!
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
c8268f6
to
5954474
|
@bors-servo r+ |
|
|
|
I think the problem we've experienced where what was previously an intermittent problem suddenly failed consistently, with tasks not being cancelled when Fixing the above should put the situation back to the previous 'intermittent test failure'... |
| @@ -1227,14 +1169,6 @@ impl Window { | |||
| self.paint_worklet.or_init(|| self.new_paint_worklet()) | |||
| } | |||
|
|
|||
| pub fn task_canceller(&self, name: TaskSourceName) -> TaskCanceller { | |||
| let mut flags = self.ignore_further_async_events.borrow_mut(); | |||
This comment has been minimized.
This comment has been minimized.
gterzian
Nov 6, 2018
Member
The ignore_further_async_events field of Window is still used in a few places, such as ignore_all_events, this makes the task cancelling inside ignore_all_events not have effect on tasks that have been scheduled via the new TaskManager, since the Arc<AtomicBool> are not shared between ignore_further_async_events and TaskManager.task_cancellers.
So we need to remove ignore_further_async_events from Window, and make methods like Window.ignore_all_events used the TaskManager instead.
This comment has been minimized.
This comment has been minimized.
AgustinCB
Nov 6, 2018
Author
Contributor
Wow, there's no way I'd have seen that. Thank you so much.
One question, before I implement that, is there an (easy) way to properly fix this? I.e. no failure at all. Or would it fall outside the scope of this pr?
This comment has been minimized.
This comment has been minimized.
gterzian
Nov 6, 2018
•
Member
You're welcome.
I'm not sure if there is an easy way to fix the intermittent failure, although once the above is fixed in one commit, you could re-try, in a separate commit, the suggestion I made at #21804 (comment) (although since it is intermittent, you might have a hard time actually knowing for sure whether it helped or not).
|
|
And remove the method in window that returns it, because it isn't used so far.
5079c12
to
3fb8d4d
|
Arf, this is finally fixed. Sorry for taking so long with it. |
|
@bors-servo try=wpt |
…try> Unify the task source and task canceller API To do so, I created a struct `TaskManagement(TaskSource, TaskCanceller)` and made `*_task_source` return that instead of just the task source. Next, I refactored all places in which `task_canceller` by basically removing them in favour of a previously called `*_task_source`. I tried to make `task_canceller` a private method in `Window`, with the hope of enforcing the use of `*_task_source`. However, it's used in components/script/dom/globalscope.rs:575 in such a way that will make it harder to avoid. I decided to leave it that way. It'd be possible to unify `*_task_source` in such a way that we would have only one method. However, I decided not to do it because one of the `TaskSource` implementations is special: `history_traversal_task_source`. Not wanting to over complicate things, I decided to leave the structure this way. --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #21154 (github issue number if applicable). - [x] These changes do not require tests because it's refactoring code that should already be tested. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21804) <!-- Reviewable:end -->
|
|
|
Some |
I moved away from the `Window` struct all the logic to handle task sources, into a new struct called `TaskManager`. In a happy world, I'd be able to just have there two functions, of the types: ```rust fn task_source<T: TaskSource>(&self, name: TaskSourceName) -> Box<T> fn task_source_with_canceller<T: TaskSource>(&self, name: TaskSourceName) -> (Box<T>, TaskSourceCanceller) ``` And not so much duplicated code. However, because TaskSource can't be a trait object (because it has generic type parameters), that's not possible. Instead, I decided to reduce duplicated logic through macros. For reasons[1], I have to pass both the name of the function with canceller and the name of the function without, as I'm not able to concatenate them in the macro itself. I could probably use `concat_idents` to create both types already defined and reduce the amount of arguments by one, but that macro is nightly only. At the same time, not being able to declare macros inside `impl` forces me to pass `self` as an argument. All this makes this solution more verbose than it would be ideally. It does reduce duplication, but it doesn't reduce the size of the file. [1](rust-lang/rust#29599)
3fb8d4d
to
75eb94a
|
Thanks! I addressed both issues and rebased. |
|
@bors-servo r=gterzian |
|
|
…terzian Unify the task source and task canceller API To do so, I created a struct `TaskManagement(TaskSource, TaskCanceller)` and made `*_task_source` return that instead of just the task source. Next, I refactored all places in which `task_canceller` by basically removing them in favour of a previously called `*_task_source`. I tried to make `task_canceller` a private method in `Window`, with the hope of enforcing the use of `*_task_source`. However, it's used in components/script/dom/globalscope.rs:575 in such a way that will make it harder to avoid. I decided to leave it that way. It'd be possible to unify `*_task_source` in such a way that we would have only one method. However, I decided not to do it because one of the `TaskSource` implementations is special: `history_traversal_task_source`. Not wanting to over complicate things, I decided to leave the structure this way. --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #21154 (github issue number if applicable). - [x] These changes do not require tests because it's refactoring code that should already be tested. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21804) <!-- Reviewable:end -->
|
|
AgustinCB commentedSep 24, 2018
•
edited by SimonSapin
To do so, I created a struct
TaskManagement(TaskSource, TaskCanceller)and made*_task_sourcereturn that instead of justthe task source.
Next, I refactored all places in which
task_cancellerby basicallyremoving them in favour of a previously called
*_task_source.I tried to make
task_cancellera private method inWindow, with thehope of enforcing the use of
*_task_source. However, it's used incomponents/script/dom/globalscope.rs:575 in such a way that will make it
harder to avoid. I decided to leave it that way.
It'd be possible to unify
*_task_sourcein such a way that we wouldhave only one method. However, I decided not to do it because one of the
TaskSourceimplementations is special:history_traversal_task_source. Not wanting to over complicate things,I decided to leave the structure this way.
./mach build -ddoes not report any errors./mach test-tidydoes not report any errorsThese changes fix #21154 (github issue number if applicable).
These changes do not require tests because it's refactoring code that should already be tested.
This change is