diff options
author | Josh Matthews <josh@joshmatthews.net> | 2014-04-21 16:10:28 -0400 |
---|---|---|
committer | Josh Matthews <josh@joshmatthews.net> | 2014-04-21 16:10:28 -0400 |
commit | 3078f580550870156a985fb002f429b6dc35b381 (patch) | |
tree | 7f72e7fdd2bd33857a90bc4d0dbfb03e0682fd0e /src/components/script/script_task.rs | |
parent | 7d9fa356717839fdf1ab01ccbc83fa71eefaf426 (diff) | |
download | servo-3078f580550870156a985fb002f429b6dc35b381.tar.gz servo-3078f580550870156a985fb002f429b6dc35b381.zip |
Ensure JS-owned memory is not leaked if a script task fails. Fixes #2201.
Diffstat (limited to 'src/components/script/script_task.rs')
-rw-r--r-- | src/components/script/script_task.rs | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index 854b060513d..a993a75d0f6 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -493,6 +493,45 @@ pub struct ScriptTask { mouse_over_targets: RefCell<Option<~[JS<Node>]>> } +/// In the event of task failure, all data on the stack runs its destructor. However, there +/// are no reachable, owning pointers to the DOM memory, so it never gets freed by default +/// when the script task fails. The ScriptMemoryFailsafe uses the destructor bomb pattern +/// to forcibly tear down the JS compartments for pages associated with the failing ScriptTask. +struct ScriptMemoryFailsafe<'a> { + owner: Option<&'a ScriptTask>, +} + +impl<'a> ScriptMemoryFailsafe<'a> { + fn neuter(&mut self) { + self.owner = None; + } + + fn new(owner: &'a ScriptTask) -> ScriptMemoryFailsafe<'a> { + ScriptMemoryFailsafe { + owner: Some(owner), + } + } +} + +#[unsafe_destructor] +impl<'a> Drop for ScriptMemoryFailsafe<'a> { + fn drop(&mut self) { + match self.owner { + Some(owner) => { + let mut page_tree = owner.page_tree.borrow_mut(); + for page in page_tree.iter() { + let mut js_info = page.mut_js_info(); + unsafe { + JS_AllowGC(js_info.get_ref().js_context.deref().deref().ptr); + } + *js_info = None; + } + } + None => (), + } + } +} + impl ScriptTask { /// Creates a new script task. pub fn new(id: PipelineId, @@ -555,7 +594,11 @@ impl ScriptTask { resource_task, image_cache_task, window_size); + let mut failsafe = ScriptMemoryFailsafe::new(&*script_task); script_task.start(); + + // This must always be the very last operation performed before the task completes + failsafe.neuter(); }); } |