mirror of
https://github.com/servo/servo.git
synced 2025-06-19 14:48:59 +01:00
layout: Port parallel layout over to a generic "work queue"
infrastructure. The work queue accepts abstract generic "work units", which in this case are layout operations. The same speedups have been observed.
This commit is contained in:
parent
86c29d253a
commit
18a2050a64
12 changed files with 518 additions and 351 deletions
|
@ -198,6 +198,9 @@ impl IOCompositor {
|
||||||
// Drain compositor port, sometimes messages contain channels that are blocking
|
// Drain compositor port, sometimes messages contain channels that are blocking
|
||||||
// another task from finishing (i.e. SetIds)
|
// another task from finishing (i.e. SetIds)
|
||||||
while self.port.try_recv().is_some() {}
|
while self.port.try_recv().is_some() {}
|
||||||
|
|
||||||
|
// Tell the profiler to shut down.
|
||||||
|
self.profiler_chan.send(time::ExitMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_message(&mut self) {
|
fn handle_message(&mut self) {
|
||||||
|
|
|
@ -329,7 +329,7 @@ impl BlockFlow {
|
||||||
// top or bottom borders nor top or bottom padding, and it has a 'height' of either 0 or 'auto',
|
// top or bottom borders nor top or bottom padding, and it has a 'height' of either 0 or 'auto',
|
||||||
// and it does not contain a line box, and all of its in-flow children's margins (if any) collapse.
|
// and it does not contain a line box, and all of its in-flow children's margins (if any) collapse.
|
||||||
|
|
||||||
let screen_height = ctx.shared.screen_size.height;
|
let screen_height = ctx.screen_size.height;
|
||||||
|
|
||||||
let mut height = if self.is_root {
|
let mut height = if self.is_root {
|
||||||
// FIXME(pcwalton): The max is taken here so that you can scroll the page, but this is
|
// FIXME(pcwalton): The max is taken here so that you can scroll the page, but this is
|
||||||
|
@ -627,7 +627,7 @@ impl Flow for BlockFlow {
|
||||||
if self.is_root {
|
if self.is_root {
|
||||||
debug!("Setting root position");
|
debug!("Setting root position");
|
||||||
self.base.position.origin = Au::zero_point();
|
self.base.position.origin = Au::zero_point();
|
||||||
self.base.position.size.width = ctx.shared.screen_size.width;
|
self.base.position.size.width = ctx.screen_size.width;
|
||||||
self.base.floats_in = FloatContext::new(self.base.num_floats);
|
self.base.floats_in = FloatContext::new(self.base.num_floats);
|
||||||
self.base.flags_info.flags.set_inorder(false);
|
self.base.flags_info.flags.set_inorder(false);
|
||||||
}
|
}
|
||||||
|
@ -672,7 +672,7 @@ impl Flow for BlockFlow {
|
||||||
margin_bottom,
|
margin_bottom,
|
||||||
margin_left));
|
margin_left));
|
||||||
|
|
||||||
let screen_size = ctx.shared.screen_size;
|
let screen_size = ctx.screen_size;
|
||||||
let (x, w) = box_.get_x_coord_and_new_width_if_fixed(screen_size.width,
|
let (x, w) = box_.get_x_coord_and_new_width_if_fixed(screen_size.width,
|
||||||
screen_size.height,
|
screen_size.height,
|
||||||
width,
|
width,
|
||||||
|
|
|
@ -1338,7 +1338,7 @@ impl Box {
|
||||||
iframe_box.pipeline_id,
|
iframe_box.pipeline_id,
|
||||||
iframe_box.subpage_id);
|
iframe_box.subpage_id);
|
||||||
let msg = FrameRectMsg(iframe_box.pipeline_id, iframe_box.subpage_id, rect);
|
let msg = FrameRectMsg(iframe_box.pipeline_id, iframe_box.subpage_id, rect);
|
||||||
layout_context.shared.constellation_chan.send(msg)
|
layout_context.constellation_chan.send(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ use layout::text::TextRunScanner;
|
||||||
use layout::util::LayoutDataAccess;
|
use layout::util::LayoutDataAccess;
|
||||||
use layout::wrapper::{LayoutNode, PostorderNodeMutTraversal};
|
use layout::wrapper::{LayoutNode, PostorderNodeMutTraversal};
|
||||||
|
|
||||||
|
use gfx::font_context::FontContext;
|
||||||
use script::dom::element::{HTMLIframeElementTypeId, HTMLImageElementTypeId};
|
use script::dom::element::{HTMLIframeElementTypeId, HTMLImageElementTypeId};
|
||||||
use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId};
|
use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId};
|
||||||
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, TextNodeTypeId};
|
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, TextNodeTypeId};
|
||||||
|
@ -209,14 +210,19 @@ pub struct FlowConstructor<'a> {
|
||||||
///
|
///
|
||||||
/// FIXME(pcwalton): This is going to have to be atomic; can't we do something better?
|
/// FIXME(pcwalton): This is going to have to be atomic; can't we do something better?
|
||||||
next_flow_id: RefCell<int>,
|
next_flow_id: RefCell<int>,
|
||||||
|
|
||||||
|
/// The font context.
|
||||||
|
font_context: ~FontContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'fc> FlowConstructor<'fc> {
|
impl<'fc> FlowConstructor<'fc> {
|
||||||
/// Creates a new flow constructor.
|
/// Creates a new flow constructor.
|
||||||
pub fn init<'a>(layout_context: &'a mut LayoutContext) -> FlowConstructor<'a> {
|
pub fn init<'a>(layout_context: &'a mut LayoutContext) -> FlowConstructor<'a> {
|
||||||
|
let font_context = ~FontContext::new(layout_context.font_context_info.clone());
|
||||||
FlowConstructor {
|
FlowConstructor {
|
||||||
layout_context: layout_context,
|
layout_context: layout_context,
|
||||||
next_flow_id: RefCell::new(0),
|
next_flow_id: RefCell::new(0),
|
||||||
|
font_context: font_context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +241,7 @@ impl<'fc> FlowConstructor<'fc> {
|
||||||
Some(url) => {
|
Some(url) => {
|
||||||
// FIXME(pcwalton): The fact that image boxes store the cache within them makes
|
// FIXME(pcwalton): The fact that image boxes store the cache within them makes
|
||||||
// little sense to me.
|
// little sense to me.
|
||||||
Some(ImageBoxInfo::new(&node, url, self.layout_context.shared.image_cache.clone()))
|
Some(ImageBoxInfo::new(&node, url, self.layout_context.image_cache.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,20 +268,19 @@ impl<'fc> FlowConstructor<'fc> {
|
||||||
/// otherwise.
|
/// otherwise.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn flush_inline_boxes_to_flow(&mut self, boxes: ~[Box], flow: &mut ~Flow, node: LayoutNode) {
|
fn flush_inline_boxes_to_flow(&mut self, boxes: ~[Box], flow: &mut ~Flow, node: LayoutNode) {
|
||||||
if boxes.len() > 0 {
|
if boxes.len() == 0 {
|
||||||
let inline_base = BaseFlow::new(self.next_flow_id(), node);
|
return
|
||||||
|
|
||||||
let mut inline_flow = ~InlineFlow::from_boxes(inline_base, boxes) as ~Flow;
|
|
||||||
|
|
||||||
self.layout_context.shared.leaf_set.access(|leaf_set| leaf_set.insert(&inline_flow));
|
|
||||||
|
|
||||||
TextRunScanner::new().scan_for_runs(self.layout_context, inline_flow);
|
|
||||||
let mut inline_flow = Some(inline_flow);
|
|
||||||
|
|
||||||
self.layout_context.shared.leaf_set.access(|leaf_set| {
|
|
||||||
flow.add_new_child(inline_flow.take_unwrap(), leaf_set)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let inline_base = BaseFlow::new(self.next_flow_id(), node);
|
||||||
|
let mut inline_flow = ~InlineFlow::from_boxes(inline_base, boxes) as ~Flow;
|
||||||
|
self.layout_context.leaf_set.access(|leaf_set| leaf_set.insert(&inline_flow));
|
||||||
|
TextRunScanner::new().scan_for_runs(self.font_context, inline_flow);
|
||||||
|
|
||||||
|
let mut inline_flow = Some(inline_flow);
|
||||||
|
self.layout_context.leaf_set.access(|leaf_set| {
|
||||||
|
flow.add_new_child(inline_flow.take_unwrap(), leaf_set)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an inline flow from a set of inline boxes, if present, and adds it as a child of
|
/// Creates an inline flow from a set of inline boxes, if present, and adds it as a child of
|
||||||
|
@ -319,7 +324,7 @@ impl<'fc> FlowConstructor<'fc> {
|
||||||
flow,
|
flow,
|
||||||
node);
|
node);
|
||||||
let mut kid_flow = Some(kid_flow);
|
let mut kid_flow = Some(kid_flow);
|
||||||
self.layout_context.shared.leaf_set.access(|leaf_set| {
|
self.layout_context.leaf_set.access(|leaf_set| {
|
||||||
flow.add_new_child(kid_flow.take_unwrap(), leaf_set)
|
flow.add_new_child(kid_flow.take_unwrap(), leaf_set)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -362,7 +367,7 @@ impl<'fc> FlowConstructor<'fc> {
|
||||||
// Push the flow generated by the {ib} split onto our list of
|
// Push the flow generated by the {ib} split onto our list of
|
||||||
// flows.
|
// flows.
|
||||||
let mut kid_flow = Some(kid_flow);
|
let mut kid_flow = Some(kid_flow);
|
||||||
self.layout_context.shared.leaf_set.access(|leaf_set| {
|
self.layout_context.leaf_set.access(|leaf_set| {
|
||||||
flow.add_new_child(kid_flow.take_unwrap(), leaf_set)
|
flow.add_new_child(kid_flow.take_unwrap(), leaf_set)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -391,7 +396,7 @@ impl<'fc> FlowConstructor<'fc> {
|
||||||
let box_ = self.build_box_for_node(node);
|
let box_ = self.build_box_for_node(node);
|
||||||
let mut flow = ~BlockFlow::from_box(base, box_, is_fixed) as ~Flow;
|
let mut flow = ~BlockFlow::from_box(base, box_, is_fixed) as ~Flow;
|
||||||
|
|
||||||
self.layout_context.shared.leaf_set.access(|leaf_set| leaf_set.insert(&flow));
|
self.layout_context.leaf_set.access(|leaf_set| leaf_set.insert(&flow));
|
||||||
|
|
||||||
self.build_children_of_block_flow(&mut flow, node);
|
self.build_children_of_block_flow(&mut flow, node);
|
||||||
flow
|
flow
|
||||||
|
@ -406,7 +411,7 @@ impl<'fc> FlowConstructor<'fc> {
|
||||||
|
|
||||||
let mut flow = ~BlockFlow::float_from_box(base, float_type, box_) as ~Flow;
|
let mut flow = ~BlockFlow::float_from_box(base, float_type, box_) as ~Flow;
|
||||||
|
|
||||||
self.layout_context.shared.leaf_set.access(|leaf_set| leaf_set.insert(&flow));
|
self.layout_context.leaf_set.access(|leaf_set| leaf_set.insert(&flow));
|
||||||
|
|
||||||
self.build_children_of_block_flow(&mut flow, node);
|
self.build_children_of_block_flow(&mut flow, node);
|
||||||
flow
|
flow
|
||||||
|
@ -484,7 +489,7 @@ impl<'fc> FlowConstructor<'fc> {
|
||||||
fn set_inline_info_for_inline_child(&mut self, boxes: &mut ~[Box], parent_node: LayoutNode) {
|
fn set_inline_info_for_inline_child(&mut self, boxes: &mut ~[Box], parent_node: LayoutNode) {
|
||||||
let parent_box = self.build_box_for_node(parent_node);
|
let parent_box = self.build_box_for_node(parent_node);
|
||||||
let font_style = parent_box.font_style();
|
let font_style = parent_box.font_style();
|
||||||
let font_group = self.layout_context.font_ctx.get_resolved_font_for_style(&font_style);
|
let font_group = self.font_context.get_resolved_font_for_style(&font_style);
|
||||||
let (font_ascent,font_descent) = font_group.borrow().with_mut( |fg| {
|
let (font_ascent,font_descent) = font_group.borrow().with_mut( |fg| {
|
||||||
fg.fonts[0].borrow().with_mut( |font| {
|
fg.fonts[0].borrow().with_mut( |font| {
|
||||||
(font.metrics.ascent,font.metrics.descent)
|
(font.metrics.ascent,font.metrics.descent)
|
||||||
|
@ -569,7 +574,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
|
||||||
// `display: none` contributes no flow construction result. Nuke the flow construction
|
// `display: none` contributes no flow construction result. Nuke the flow construction
|
||||||
// results of children.
|
// results of children.
|
||||||
(display::none, _, _) => {
|
(display::none, _, _) => {
|
||||||
self.layout_context.shared.leaf_set.access(|leaf_set| {
|
self.layout_context.leaf_set.access(|leaf_set| {
|
||||||
for child in node.children() {
|
for child in node.children() {
|
||||||
let mut old_result = child.swap_out_construction_result();
|
let mut old_result = child.swap_out_construction_result();
|
||||||
old_result.destroy(leaf_set)
|
old_result.destroy(leaf_set)
|
||||||
|
|
|
@ -5,17 +5,26 @@
|
||||||
//! Data needed by the layout task.
|
//! Data needed by the layout task.
|
||||||
|
|
||||||
use extra::arc::MutexArc;
|
use extra::arc::MutexArc;
|
||||||
|
use green::task::GreenTask;
|
||||||
use layout::flow::LeafSet;
|
use layout::flow::LeafSet;
|
||||||
|
use std::cast;
|
||||||
|
use std::ptr;
|
||||||
|
use std::rt::Runtime;
|
||||||
|
use std::rt::local::Local;
|
||||||
|
use std::rt::task::Task;
|
||||||
|
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::{FontContext, FontContextInfo};
|
||||||
use servo_msg::constellation_msg::ConstellationChan;
|
use servo_msg::constellation_msg::ConstellationChan;
|
||||||
use servo_net::local_image_cache::LocalImageCache;
|
use servo_net::local_image_cache::LocalImageCache;
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
|
||||||
|
#[thread_local]
|
||||||
|
static mut FONT_CONTEXT: *mut FontContext = 0 as *mut FontContext;
|
||||||
|
|
||||||
/// Data shared by all layout workers.
|
/// Data shared by all layout workers.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct SharedLayoutInfo {
|
pub struct LayoutContext {
|
||||||
/// The local image cache.
|
/// The local image cache.
|
||||||
image_cache: MutexArc<LocalImageCache>,
|
image_cache: MutexArc<LocalImageCache>,
|
||||||
|
|
||||||
|
@ -27,14 +36,30 @@ pub struct SharedLayoutInfo {
|
||||||
|
|
||||||
/// The set of leaf flows.
|
/// The set of leaf flows.
|
||||||
leaf_set: MutexArc<LeafSet>,
|
leaf_set: MutexArc<LeafSet>,
|
||||||
|
|
||||||
|
/// Information needed to construct a font context.
|
||||||
|
font_context_info: FontContextInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data specific to a layout worker.
|
impl LayoutContext {
|
||||||
pub struct LayoutContext {
|
pub fn font_context<'a>(&'a mut self) -> &'a mut FontContext {
|
||||||
/// Shared layout info.
|
// Sanity check.
|
||||||
shared: SharedLayoutInfo,
|
let mut task = Local::borrow(None::<Task>);
|
||||||
|
match task.get().maybe_take_runtime::<GreenTask>() {
|
||||||
|
Some(green) => {
|
||||||
|
task.get().put_runtime(green as ~Runtime);
|
||||||
|
fail!("can't call this on a green task!")
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
/// The current font context.
|
unsafe {
|
||||||
font_ctx: ~FontContext,
|
if FONT_CONTEXT == ptr::mut_null() {
|
||||||
|
let context = ~FontContext::new(self.font_context_info.clone());
|
||||||
|
FONT_CONTEXT = cast::transmute(context)
|
||||||
|
}
|
||||||
|
cast::transmute(FONT_CONTEXT)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use css::matching::MatchMethods;
|
||||||
use css::select::new_stylist;
|
use css::select::new_stylist;
|
||||||
use css::node_style::StyledNode;
|
use css::node_style::StyledNode;
|
||||||
use layout::construct::{FlowConstructionResult, FlowConstructor, NoConstructionResult};
|
use layout::construct::{FlowConstructionResult, FlowConstructor, NoConstructionResult};
|
||||||
use layout::context::{LayoutContext, SharedLayoutInfo};
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ToGfxColor};
|
use layout::display_list_builder::{DisplayListBuilder, ToGfxColor};
|
||||||
use layout::extra::LayoutAuxMethods;
|
use layout::extra::LayoutAuxMethods;
|
||||||
use layout::flow::{Flow, ImmutableFlowUtils, LeafSet, MutableFlowUtils, MutableOwnedFlowUtils};
|
use layout::flow::{Flow, ImmutableFlowUtils, LeafSet, MutableFlowUtils, MutableOwnedFlowUtils};
|
||||||
|
@ -17,7 +17,8 @@ use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal};
|
||||||
use layout::flow;
|
use layout::flow;
|
||||||
use layout::incremental::RestyleDamage;
|
use layout::incremental::RestyleDamage;
|
||||||
use layout::parallel::{AssignHeightsAndStoreOverflowTraversalKind, BubbleWidthsTraversalKind};
|
use layout::parallel::{AssignHeightsAndStoreOverflowTraversalKind, BubbleWidthsTraversalKind};
|
||||||
use layout::parallel::{ParallelPostorderFlowTraversal};
|
use layout::parallel::{UnsafeFlow};
|
||||||
|
use layout::parallel;
|
||||||
use layout::util::{LayoutDataAccess, OpaqueNode, LayoutDataWrapper};
|
use layout::util::{LayoutDataAccess, OpaqueNode, LayoutDataWrapper};
|
||||||
use layout::wrapper::LayoutNode;
|
use layout::wrapper::LayoutNode;
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ use extra::arc::{Arc, MutexArc, RWArc};
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList};
|
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList};
|
||||||
use gfx::font_context::{FontContext, FontContextInfo};
|
use gfx::font_context::FontContextInfo;
|
||||||
use gfx::opts::Opts;
|
use gfx::opts::Opts;
|
||||||
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
|
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
|
||||||
use gfx::{render_task, color};
|
use gfx::{render_task, color};
|
||||||
|
@ -46,10 +47,12 @@ use servo_util::geometry::Au;
|
||||||
use servo_util::time::{ProfilerChan, profile};
|
use servo_util::time::{ProfilerChan, profile};
|
||||||
use servo_util::time;
|
use servo_util::time;
|
||||||
use servo_util::task::spawn_named;
|
use servo_util::task::spawn_named;
|
||||||
|
use servo_util::workqueue::WorkQueue;
|
||||||
use std::cast::transmute;
|
use std::cast::transmute;
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::comm::Port;
|
use std::comm::Port;
|
||||||
|
use std::ptr;
|
||||||
use std::util;
|
use std::util;
|
||||||
use style::{AuthorOrigin, Stylesheet, Stylist};
|
use style::{AuthorOrigin, Stylesheet, Stylist};
|
||||||
|
|
||||||
|
@ -91,7 +94,7 @@ pub struct LayoutTask {
|
||||||
stylist: RWArc<Stylist>,
|
stylist: RWArc<Stylist>,
|
||||||
|
|
||||||
/// The workers that we use for parallel operation.
|
/// The workers that we use for parallel operation.
|
||||||
parallel_traversal: Option<ParallelPostorderFlowTraversal>,
|
parallel_traversal: Option<WorkQueue<*mut LayoutContext,UnsafeFlow>>,
|
||||||
|
|
||||||
/// The channel on which messages can be sent to the profiler.
|
/// The channel on which messages can be sent to the profiler.
|
||||||
profiler_chan: ProfilerChan,
|
profiler_chan: ProfilerChan,
|
||||||
|
@ -142,12 +145,14 @@ impl PreorderFlowTraversal for PropagateDamageTraversal {
|
||||||
|
|
||||||
/// The bubble-widths traversal, the first part of layout computation. This computes preferred
|
/// The bubble-widths traversal, the first part of layout computation. This computes preferred
|
||||||
/// and intrinsic widths and bubbles them up the tree.
|
/// and intrinsic widths and bubbles them up the tree.
|
||||||
pub struct BubbleWidthsTraversal<'a>(&'a mut LayoutContext);
|
pub struct BubbleWidthsTraversal<'a> {
|
||||||
|
layout_context: &'a mut LayoutContext,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> PostorderFlowTraversal for BubbleWidthsTraversal<'a> {
|
impl<'a> PostorderFlowTraversal for BubbleWidthsTraversal<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn process(&mut self, flow: &mut Flow) -> bool {
|
fn process(&mut self, flow: &mut Flow) -> bool {
|
||||||
flow.bubble_widths(**self);
|
flow.bubble_widths(self.layout_context);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,13 +179,15 @@ impl<'a> PreorderFlowTraversal for AssignWidthsTraversal<'a> {
|
||||||
/// The assign-heights-and-store-overflow traversal, the last (and most expensive) part of layout
|
/// The assign-heights-and-store-overflow traversal, the last (and most expensive) part of layout
|
||||||
/// computation. Determines the final heights for all layout objects, computes positions, and
|
/// computation. Determines the final heights for all layout objects, computes positions, and
|
||||||
/// computes overflow regions. In Gecko this corresponds to `FinishAndStoreOverflow`.
|
/// computes overflow regions. In Gecko this corresponds to `FinishAndStoreOverflow`.
|
||||||
pub struct AssignHeightsAndStoreOverflowTraversal<'a>(&'a mut LayoutContext);
|
pub struct AssignHeightsAndStoreOverflowTraversal<'a> {
|
||||||
|
layout_context: &'a mut LayoutContext,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {
|
impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn process(&mut self, flow: &mut Flow) -> bool {
|
fn process(&mut self, flow: &mut Flow) -> bool {
|
||||||
flow.assign_height(**self);
|
flow.assign_height(self.layout_context);
|
||||||
flow.store_overflow(**self);
|
flow.store_overflow(self.layout_context);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,13 +255,8 @@ impl LayoutTask {
|
||||||
-> LayoutTask {
|
-> LayoutTask {
|
||||||
let local_image_cache = MutexArc::new(LocalImageCache(image_cache_task.clone()));
|
let local_image_cache = MutexArc::new(LocalImageCache(image_cache_task.clone()));
|
||||||
let screen_size = Size2D(Au(0), Au(0));
|
let screen_size = Size2D(Au(0), Au(0));
|
||||||
let font_context_info = FontContextInfo {
|
|
||||||
backend: opts.render_backend,
|
|
||||||
needs_font_list: true,
|
|
||||||
profiler_chan: profiler_chan.clone(),
|
|
||||||
};
|
|
||||||
let parallel_traversal = if opts.layout_threads != 1 {
|
let parallel_traversal = if opts.layout_threads != 1 {
|
||||||
Some(ParallelPostorderFlowTraversal::new(font_context_info, opts.layout_threads))
|
Some(WorkQueue::new(opts.layout_threads, ptr::mut_null()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -288,20 +290,18 @@ impl LayoutTask {
|
||||||
|
|
||||||
// Create a layout context for use in building display lists, hit testing, &c.
|
// Create a layout context for use in building display lists, hit testing, &c.
|
||||||
fn build_layout_context(&self) -> LayoutContext {
|
fn build_layout_context(&self) -> LayoutContext {
|
||||||
let font_ctx = ~FontContext::new(FontContextInfo {
|
let font_context_info = FontContextInfo {
|
||||||
backend: self.opts.render_backend,
|
backend: self.opts.render_backend,
|
||||||
needs_font_list: true,
|
needs_font_list: true,
|
||||||
profiler_chan: self.profiler_chan.clone(),
|
profiler_chan: self.profiler_chan.clone(),
|
||||||
});
|
};
|
||||||
|
|
||||||
LayoutContext {
|
LayoutContext {
|
||||||
shared: SharedLayoutInfo {
|
image_cache: self.local_image_cache.clone(),
|
||||||
image_cache: self.local_image_cache.clone(),
|
screen_size: self.screen_size.clone(),
|
||||||
screen_size: self.screen_size.clone(),
|
constellation_chan: self.constellation_chan.clone(),
|
||||||
constellation_chan: self.constellation_chan.clone(),
|
leaf_set: self.leaf_set.clone(),
|
||||||
leaf_set: self.leaf_set.clone(),
|
font_context_info: font_context_info,
|
||||||
},
|
|
||||||
font_ctx: font_ctx,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,7 +418,12 @@ impl LayoutTask {
|
||||||
fn solve_constraints(&mut self,
|
fn solve_constraints(&mut self,
|
||||||
layout_root: &mut Flow,
|
layout_root: &mut Flow,
|
||||||
layout_context: &mut LayoutContext) {
|
layout_context: &mut LayoutContext) {
|
||||||
layout_root.traverse_postorder(&mut BubbleWidthsTraversal(layout_context));
|
{
|
||||||
|
let mut traversal = BubbleWidthsTraversal {
|
||||||
|
layout_context: layout_context,
|
||||||
|
};
|
||||||
|
layout_root.traverse_postorder(&mut traversal);
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME(kmc): We want to prune nodes without the Reflow restyle damage
|
// FIXME(kmc): We want to prune nodes without the Reflow restyle damage
|
||||||
// bit, but FloatContext values can't be reused, so we need to
|
// bit, but FloatContext values can't be reused, so we need to
|
||||||
|
@ -428,8 +433,12 @@ impl LayoutTask {
|
||||||
layout_root.traverse_preorder(&mut AssignWidthsTraversal(layout_context));
|
layout_root.traverse_preorder(&mut AssignWidthsTraversal(layout_context));
|
||||||
|
|
||||||
// FIXME(pcwalton): Prune this pass as well.
|
// FIXME(pcwalton): Prune this pass as well.
|
||||||
layout_root.traverse_postorder(&mut AssignHeightsAndStoreOverflowTraversal(
|
{
|
||||||
layout_context));
|
let mut traversal = AssignHeightsAndStoreOverflowTraversal {
|
||||||
|
layout_context: layout_context,
|
||||||
|
};
|
||||||
|
layout_root.traverse_postorder(&mut traversal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs layout constraint solving in parallel.
|
/// Performs layout constraint solving in parallel.
|
||||||
|
@ -443,9 +452,11 @@ impl LayoutTask {
|
||||||
match self.parallel_traversal {
|
match self.parallel_traversal {
|
||||||
None => fail!("solve_contraints_parallel() called with no parallel traversal ready"),
|
None => fail!("solve_contraints_parallel() called with no parallel traversal ready"),
|
||||||
Some(ref mut traversal) => {
|
Some(ref mut traversal) => {
|
||||||
traversal.start(BubbleWidthsTraversalKind,
|
parallel::traverse_flow_tree(BubbleWidthsTraversalKind,
|
||||||
layout_context,
|
&self.leaf_set,
|
||||||
self.profiler_chan.clone());
|
self.profiler_chan.clone(),
|
||||||
|
layout_context,
|
||||||
|
traversal);
|
||||||
|
|
||||||
// NOTE: this currently computes borders, so any pruning should separate that
|
// NOTE: this currently computes borders, so any pruning should separate that
|
||||||
// operation out.
|
// operation out.
|
||||||
|
@ -453,9 +464,11 @@ impl LayoutTask {
|
||||||
// because this is a top-down traversal, unlike the others.
|
// because this is a top-down traversal, unlike the others.
|
||||||
layout_root.traverse_preorder(&mut AssignWidthsTraversal(layout_context));
|
layout_root.traverse_preorder(&mut AssignWidthsTraversal(layout_context));
|
||||||
|
|
||||||
traversal.start(AssignHeightsAndStoreOverflowTraversalKind,
|
parallel::traverse_flow_tree(AssignHeightsAndStoreOverflowTraversalKind,
|
||||||
layout_context,
|
&self.leaf_set,
|
||||||
self.profiler_chan.clone());
|
self.profiler_chan.clone(),
|
||||||
|
layout_context,
|
||||||
|
traversal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,44 +4,29 @@
|
||||||
|
|
||||||
//! Implements parallel traversals over the flow tree.
|
//! Implements parallel traversals over the flow tree.
|
||||||
|
|
||||||
use layout::context::{LayoutContext, SharedLayoutInfo};
|
use layout::context::LayoutContext;
|
||||||
use layout::flow::{Flow, PostorderFlowTraversal};
|
use layout::flow::{Flow, LeafSet, PostorderFlowTraversal};
|
||||||
use layout::flow;
|
use layout::flow;
|
||||||
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, BubbleWidthsTraversal};
|
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, BubbleWidthsTraversal};
|
||||||
|
|
||||||
use gfx::font_context::{FontContext, FontContextInfo};
|
use extra::arc::MutexArc;
|
||||||
use native;
|
|
||||||
use servo_util::time::{ProfilerChan, profile};
|
use servo_util::time::{ProfilerChan, profile};
|
||||||
use servo_util::time;
|
use servo_util::time;
|
||||||
|
use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use std::comm::SharedChan;
|
|
||||||
use std::libc::c_void;
|
|
||||||
use std::ptr;
|
|
||||||
use std::sync::atomics::{AtomicInt, Relaxed, SeqCst};
|
use std::sync::atomics::{AtomicInt, Relaxed, SeqCst};
|
||||||
use std::sync::deque::{Abort, BufferPool, Data, Empty, Stealer, Worker};
|
|
||||||
use std::util;
|
|
||||||
|
|
||||||
enum WorkerMsg {
|
pub enum TraversalKind {
|
||||||
/// Tells the worker to start a traversal.
|
BubbleWidthsTraversalKind,
|
||||||
StartMsg(Worker<UnsafeFlow>, TraversalKind, SharedLayoutInfo),
|
AssignHeightsAndStoreOverflowTraversalKind,
|
||||||
|
|
||||||
/// Tells the worker to stop. It can be restarted again with a `StartMsg`.
|
|
||||||
StopMsg,
|
|
||||||
|
|
||||||
/// Tells the worker thread to terminate.
|
|
||||||
ExitMsg,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SupervisorMsg {
|
pub type UnsafeFlow = (uint, uint);
|
||||||
/// Sent once the last flow is processed.
|
|
||||||
FinishedMsg,
|
|
||||||
|
|
||||||
/// Returns the deque to the supervisor.
|
fn null_unsafe_flow() -> UnsafeFlow {
|
||||||
ReturnDequeMsg(uint, Worker<UnsafeFlow>),
|
(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type UnsafeFlow = (*c_void, *c_void);
|
|
||||||
|
|
||||||
pub fn owned_flow_to_unsafe_flow(flow: *~Flow) -> UnsafeFlow {
|
pub fn owned_flow_to_unsafe_flow(flow: *~Flow) -> UnsafeFlow {
|
||||||
unsafe {
|
unsafe {
|
||||||
cast::transmute_copy(&*flow)
|
cast::transmute_copy(&*flow)
|
||||||
|
@ -54,10 +39,6 @@ pub fn mut_owned_flow_to_unsafe_flow(flow: *mut ~Flow) -> UnsafeFlow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn null_unsafe_flow() -> UnsafeFlow {
|
|
||||||
(ptr::null(), ptr::null())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information that we need stored in each flow.
|
/// Information that we need stored in each flow.
|
||||||
pub struct FlowParallelInfo {
|
pub struct FlowParallelInfo {
|
||||||
/// The number of children that still need work done.
|
/// The number of children that still need work done.
|
||||||
|
@ -75,270 +56,97 @@ impl FlowParallelInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information that the supervisor thread keeps about the worker threads.
|
/// A parallel bottom-up flow traversal.
|
||||||
struct WorkerInfo {
|
trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
|
||||||
/// The communication channel to the workers.
|
fn run_parallel(&mut self, mut unsafe_flow: UnsafeFlow) {
|
||||||
chan: Chan<WorkerMsg>,
|
|
||||||
/// The buffer pool for this deque.
|
|
||||||
pool: BufferPool<UnsafeFlow>,
|
|
||||||
/// The worker end of the deque, if we have it.
|
|
||||||
deque: Option<Worker<UnsafeFlow>>,
|
|
||||||
/// The thief end of the work-stealing deque.
|
|
||||||
thief: Stealer<UnsafeFlow>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information that each worker needs to do its job.
|
|
||||||
struct PostorderWorker {
|
|
||||||
/// The font context.
|
|
||||||
font_context: Option<~FontContext>,
|
|
||||||
/// Communications for the worker.
|
|
||||||
comm: PostorderWorkerComm,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Communication channels for postorder workers.
|
|
||||||
struct PostorderWorkerComm {
|
|
||||||
/// The index of this worker.
|
|
||||||
index: uint,
|
|
||||||
/// The communication port from the supervisor.
|
|
||||||
port: Port<WorkerMsg>,
|
|
||||||
/// The communication channel to the supervisor.
|
|
||||||
chan: SharedChan<SupervisorMsg>,
|
|
||||||
/// The thief end of the work-stealing deque for all other workers.
|
|
||||||
other_deques: ~[Stealer<UnsafeFlow>],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The type of traversal we're performing.
|
|
||||||
pub enum TraversalKind {
|
|
||||||
BubbleWidthsTraversalKind,
|
|
||||||
AssignHeightsAndStoreOverflowTraversalKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PostorderWorker {
|
|
||||||
/// Starts up the worker and listens for messages.
|
|
||||||
pub fn start(&mut self) {
|
|
||||||
loop {
|
loop {
|
||||||
// Wait for a start message.
|
unsafe {
|
||||||
let (mut deque, kind, shared_layout_info) = match self.comm.port.recv() {
|
// Get a real flow.
|
||||||
StopMsg => fail!("unexpected stop message"),
|
let flow: &mut ~Flow = cast::transmute(&unsafe_flow);
|
||||||
StartMsg(deque, kind, shared_layout_info) => (deque, kind, shared_layout_info),
|
|
||||||
ExitMsg => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set up our traversal context.
|
// Perform the appropriate traversal.
|
||||||
let mut traversal = LayoutContext {
|
if self.should_process(*flow) {
|
||||||
shared: shared_layout_info,
|
self.process(*flow);
|
||||||
font_ctx: self.font_context.take_unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// And we're off!
|
|
||||||
'outer: loop {
|
|
||||||
let unsafe_flow;
|
|
||||||
match deque.pop() {
|
|
||||||
Some(the_flow) => unsafe_flow = the_flow,
|
|
||||||
None => {
|
|
||||||
// Become a thief.
|
|
||||||
let mut i = 0;
|
|
||||||
loop {
|
|
||||||
if self.comm.other_deques.len() != 0 {
|
|
||||||
match self.comm.other_deques[i].steal() {
|
|
||||||
Empty => {
|
|
||||||
// Try the next one.
|
|
||||||
i += 1;
|
|
||||||
if i >= self.comm.other_deques.len() {
|
|
||||||
i = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Abort => {
|
|
||||||
// Continue.
|
|
||||||
}
|
|
||||||
Data(the_flow) => {
|
|
||||||
unsafe_flow = the_flow;
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == 0 {
|
|
||||||
match self.comm.port.try_recv() {
|
|
||||||
Some(StopMsg) => break 'outer,
|
|
||||||
Some(ExitMsg) => return,
|
|
||||||
Some(_) => fail!("unexpected message!"),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OK, we've got some data. The rest of this is unsafe code.
|
let base = flow::mut_base(*flow);
|
||||||
unsafe {
|
|
||||||
// Get a real flow.
|
|
||||||
let flow: &mut ~Flow = cast::transmute(&unsafe_flow);
|
|
||||||
|
|
||||||
// Perform the appropriate traversal.
|
// Reset the count of children for the next layout traversal.
|
||||||
match kind {
|
base.parallel.children_count.store(base.children.len() as int, Relaxed);
|
||||||
BubbleWidthsTraversalKind => {
|
|
||||||
let mut traversal = BubbleWidthsTraversal(&mut traversal);
|
|
||||||
if traversal.should_process(*flow) {
|
|
||||||
traversal.process(*flow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AssignHeightsAndStoreOverflowTraversalKind => {
|
|
||||||
let mut traversal =
|
|
||||||
AssignHeightsAndStoreOverflowTraversal(&mut traversal);
|
|
||||||
if traversal.should_process(*flow) {
|
|
||||||
traversal.process(*flow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let base = flow::mut_base(*flow);
|
// Possibly enqueue the parent.
|
||||||
|
let unsafe_parent = base.parallel.parent;
|
||||||
|
if unsafe_parent == null_unsafe_flow() {
|
||||||
|
// We're done!
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
// Reset the count of children for the next layout traversal.
|
// No, we're not at the root yet. Then are we the last sibling of our parent? If
|
||||||
base.parallel.children_count.store(base.children.len() as int, Relaxed);
|
// so, we can continue on with our parent; otherwise, we've gotta wait.
|
||||||
|
let parent: &mut ~Flow = cast::transmute(&unsafe_parent);
|
||||||
// Possibly enqueue the parent.
|
let parent_base = flow::mut_base(*parent);
|
||||||
let unsafe_parent = base.parallel.parent;
|
if parent_base.parallel.children_count.fetch_sub(1, SeqCst) == 1 {
|
||||||
if unsafe_parent == null_unsafe_flow() {
|
// We were the last child of our parent. Reflow our parent.
|
||||||
// We're done!
|
unsafe_flow = unsafe_parent
|
||||||
self.comm.chan.send(FinishedMsg);
|
} else {
|
||||||
} else {
|
// Stop.
|
||||||
// No, we're not at the root yet. Then are we the last sibling of our
|
break
|
||||||
// parent? If so, we can enqueue our parent; otherwise, we've gotta wait.
|
|
||||||
let parent: &mut ~Flow = cast::transmute(&unsafe_parent);
|
|
||||||
let parent_base = flow::mut_base(*parent);
|
|
||||||
if parent_base.parallel.children_count.fetch_sub(1, SeqCst) == 1 {
|
|
||||||
// We were the last child of our parent. Enqueue the parent.
|
|
||||||
deque.push(unsafe_parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy the traversal and save the font context.
|
|
||||||
let LayoutContext {
|
|
||||||
font_ctx: font_context,
|
|
||||||
..
|
|
||||||
} = traversal;
|
|
||||||
self.font_context = Some(font_context);
|
|
||||||
|
|
||||||
// Give the deque back to the supervisor.
|
|
||||||
self.comm.chan.send(ReturnDequeMsg(self.comm.index, deque))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A parallel bottom-up traversal.
|
impl<'a> ParallelPostorderFlowTraversal for BubbleWidthsTraversal<'a> {}
|
||||||
pub struct ParallelPostorderFlowTraversal {
|
|
||||||
/// Information about each of the workers.
|
impl<'a> ParallelPostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {}
|
||||||
workers: ~[WorkerInfo],
|
|
||||||
/// A port on which information can be received from the workers.
|
fn bubble_widths(unsafe_flow: UnsafeFlow, proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
|
||||||
port: Port<SupervisorMsg>,
|
let layout_context: &mut LayoutContext = unsafe {
|
||||||
|
cast::transmute(*proxy.user_data())
|
||||||
|
};
|
||||||
|
let mut bubble_widths_traversal = BubbleWidthsTraversal {
|
||||||
|
layout_context: layout_context,
|
||||||
|
};
|
||||||
|
bubble_widths_traversal.run_parallel(unsafe_flow)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParallelPostorderFlowTraversal {
|
fn assign_heights_and_store_overflow(unsafe_flow: UnsafeFlow,
|
||||||
pub fn new(font_context_info: FontContextInfo, thread_count: uint)
|
proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
|
||||||
-> ParallelPostorderFlowTraversal {
|
let layout_context: &mut LayoutContext = unsafe {
|
||||||
let (supervisor_port, supervisor_chan) = SharedChan::new();
|
cast::transmute(*proxy.user_data())
|
||||||
let (mut infos, mut comms) = (~[], ~[]);
|
};
|
||||||
for i in range(0, thread_count) {
|
let mut assign_heights_traversal = AssignHeightsAndStoreOverflowTraversal {
|
||||||
let (worker_port, worker_chan) = Chan::new();
|
layout_context: layout_context,
|
||||||
let mut pool = BufferPool::new();
|
};
|
||||||
let (worker, thief) = pool.deque();
|
assign_heights_traversal.run_parallel(unsafe_flow)
|
||||||
infos.push(WorkerInfo {
|
}
|
||||||
chan: worker_chan,
|
|
||||||
pool: pool,
|
|
||||||
deque: Some(worker),
|
|
||||||
thief: thief,
|
|
||||||
});
|
|
||||||
comms.push(PostorderWorkerComm {
|
|
||||||
index: i,
|
|
||||||
port: worker_port,
|
|
||||||
chan: supervisor_chan.clone(),
|
|
||||||
other_deques: ~[],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in range(0, thread_count) {
|
pub fn traverse_flow_tree(kind: TraversalKind,
|
||||||
for j in range(0, thread_count) {
|
leaf_set: &MutexArc<LeafSet>,
|
||||||
if i != j {
|
profiler_chan: ProfilerChan,
|
||||||
comms[i].other_deques.push(infos[j].thief.clone())
|
layout_context: &mut LayoutContext,
|
||||||
}
|
queue: &mut WorkQueue<*mut LayoutContext,UnsafeFlow>) {
|
||||||
}
|
unsafe {
|
||||||
assert!(comms[i].other_deques.len() == thread_count - 1)
|
queue.data = cast::transmute(layout_context)
|
||||||
}
|
|
||||||
|
|
||||||
for comm in comms.move_iter() {
|
|
||||||
let font_context_info = font_context_info.clone();
|
|
||||||
native::task::spawn(proc() {
|
|
||||||
let mut worker = PostorderWorker {
|
|
||||||
font_context: Some(~FontContext::new(font_context_info)),
|
|
||||||
comm: comm,
|
|
||||||
};
|
|
||||||
worker.start()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ParallelPostorderFlowTraversal {
|
|
||||||
workers: infos,
|
|
||||||
port: supervisor_port,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO(pcwalton): This could be parallelized.
|
let fun = match kind {
|
||||||
fn warmup(&mut self, layout_context: &mut LayoutContext) {
|
BubbleWidthsTraversalKind => bubble_widths,
|
||||||
layout_context.shared.leaf_set.access(|leaf_set| {
|
AssignHeightsAndStoreOverflowTraversalKind => assign_heights_and_store_overflow,
|
||||||
|
};
|
||||||
|
|
||||||
|
profile(time::LayoutParallelWarmupCategory, profiler_chan, || {
|
||||||
|
leaf_set.access(|leaf_set| {
|
||||||
for &flow in leaf_set.iter() {
|
for &flow in leaf_set.iter() {
|
||||||
match self.workers[0].deque {
|
queue.push(WorkUnit {
|
||||||
None => fail!("no deque!"),
|
fun: fun,
|
||||||
Some(ref mut deque) => {
|
data: flow,
|
||||||
deque.push(flow);
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
});
|
||||||
|
|
||||||
/// Traverses the given flow tree in parallel.
|
queue.run()
|
||||||
pub fn start(&mut self,
|
|
||||||
kind: TraversalKind,
|
|
||||||
layout_context: &mut LayoutContext,
|
|
||||||
profiler_chan: ProfilerChan) {
|
|
||||||
profile(time::LayoutParallelWarmupCategory, profiler_chan, || self.warmup(layout_context));
|
|
||||||
|
|
||||||
for worker in self.workers.mut_iter() {
|
|
||||||
worker.chan.send(StartMsg(util::replace(&mut worker.deque, None).unwrap(),
|
|
||||||
kind,
|
|
||||||
layout_context.shared.clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for them to finish.
|
|
||||||
let _ = self.port.recv();
|
|
||||||
|
|
||||||
// Tell everyone to stop.
|
|
||||||
for worker in self.workers.iter() {
|
|
||||||
worker.chan.send(StopMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get our deques back.
|
|
||||||
//
|
|
||||||
// TODO(pcwalton): Might be able to get a little parallelism over multiple traversals by
|
|
||||||
// doing this lazily.
|
|
||||||
for _ in range(0, self.workers.len()) {
|
|
||||||
match self.port.recv() {
|
|
||||||
ReturnDequeMsg(returned_deque_index, returned_deque) => {
|
|
||||||
self.workers[returned_deque_index].deque = Some(returned_deque)
|
|
||||||
}
|
|
||||||
_ => fail!("unexpected message received during return queue phase"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shuts down all the worker threads.
|
|
||||||
pub fn shutdown(&mut self) {
|
|
||||||
for worker in self.workers.iter() {
|
|
||||||
worker.chan.send(ExitMsg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
//! Text layout.
|
//! Text layout.
|
||||||
|
|
||||||
use layout::box_::{Box, ScannedTextBox, ScannedTextBoxInfo, UnscannedTextBox};
|
use layout::box_::{Box, ScannedTextBox, ScannedTextBoxInfo, UnscannedTextBox};
|
||||||
use layout::context::LayoutContext;
|
|
||||||
use layout::flow::Flow;
|
use layout::flow::Flow;
|
||||||
|
|
||||||
use extra::arc::Arc;
|
use extra::arc::Arc;
|
||||||
|
use gfx::font_context::FontContext;
|
||||||
use gfx::text::text_run::TextRun;
|
use gfx::text::text_run::TextRun;
|
||||||
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
|
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
|
@ -27,7 +27,7 @@ impl TextRunScanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scan_for_runs(&mut self, ctx: &mut LayoutContext, flow: &mut Flow) {
|
pub fn scan_for_runs(&mut self, font_context: &mut FontContext, flow: &mut Flow) {
|
||||||
{
|
{
|
||||||
let inline = flow.as_immutable_inline();
|
let inline = flow.as_immutable_inline();
|
||||||
debug!("TextRunScanner: scanning {:u} boxes for text runs...", inline.boxes.len());
|
debug!("TextRunScanner: scanning {:u} boxes for text runs...", inline.boxes.len());
|
||||||
|
@ -40,13 +40,16 @@ impl TextRunScanner {
|
||||||
if box_i > 0 && !can_coalesce_text_nodes(flow.as_immutable_inline().boxes,
|
if box_i > 0 && !can_coalesce_text_nodes(flow.as_immutable_inline().boxes,
|
||||||
box_i - 1,
|
box_i - 1,
|
||||||
box_i) {
|
box_i) {
|
||||||
last_whitespace = self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
|
last_whitespace = self.flush_clump_to_list(font_context,
|
||||||
|
flow,
|
||||||
|
last_whitespace,
|
||||||
|
&mut out_boxes);
|
||||||
}
|
}
|
||||||
self.clump.extend_by(1);
|
self.clump.extend_by(1);
|
||||||
}
|
}
|
||||||
// handle remaining clumps
|
// handle remaining clumps
|
||||||
if self.clump.length() > 0 {
|
if self.clump.length() > 0 {
|
||||||
self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
|
self.flush_clump_to_list(font_context, flow, last_whitespace, &mut out_boxes);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("TextRunScanner: swapping out boxes.");
|
debug!("TextRunScanner: swapping out boxes.");
|
||||||
|
@ -73,7 +76,7 @@ impl TextRunScanner {
|
||||||
/// FIXME(pcwalton): Stop cloning boxes. Instead we will need to consume the `in_box`es as we
|
/// FIXME(pcwalton): Stop cloning boxes. Instead we will need to consume the `in_box`es as we
|
||||||
/// iterate over them.
|
/// iterate over them.
|
||||||
pub fn flush_clump_to_list(&mut self,
|
pub fn flush_clump_to_list(&mut self,
|
||||||
ctx: &mut LayoutContext,
|
font_context: &mut FontContext,
|
||||||
flow: &mut Flow,
|
flow: &mut Flow,
|
||||||
last_whitespace: bool,
|
last_whitespace: bool,
|
||||||
out_boxes: &mut ~[Box])
|
out_boxes: &mut ~[Box])
|
||||||
|
@ -130,7 +133,7 @@ impl TextRunScanner {
|
||||||
// TODO(#177): Text run creation must account for the renderability of text by
|
// TODO(#177): Text run creation must account for the renderability of text by
|
||||||
// font group fonts. This is probably achieved by creating the font group above
|
// font group fonts. This is probably achieved by creating the font group above
|
||||||
// and then letting `FontGroup` decide which `Font` to stick into the text run.
|
// and then letting `FontGroup` decide which `Font` to stick into the text run.
|
||||||
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
|
let fontgroup = font_context.get_resolved_font_for_style(&font_style);
|
||||||
let run = ~fontgroup.borrow().with(|fg| fg.create_textrun(transformed_text.clone(), decoration));
|
let run = ~fontgroup.borrow().with(|fg| fg.create_textrun(transformed_text.clone(), decoration));
|
||||||
|
|
||||||
debug!("TextRunScanner: pushing single text box in range: {} ({})",
|
debug!("TextRunScanner: pushing single text box in range: {} ({})",
|
||||||
|
@ -151,7 +154,7 @@ impl TextRunScanner {
|
||||||
// and then letting `FontGroup` decide which `Font` to stick into the text run.
|
// and then letting `FontGroup` decide which `Font` to stick into the text run.
|
||||||
let in_box = &in_boxes[self.clump.begin()];
|
let in_box = &in_boxes[self.clump.begin()];
|
||||||
let font_style = in_box.font_style();
|
let font_style = in_box.font_style();
|
||||||
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
|
let fontgroup = font_context.get_resolved_font_for_style(&font_style);
|
||||||
let decoration = in_box.text_decoration();
|
let decoration = in_box.text_decoration();
|
||||||
|
|
||||||
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#[comment = "The Servo Parallel Browser Project"];
|
#[comment = "The Servo Parallel Browser Project"];
|
||||||
#[license = "MPL"];
|
#[license = "MPL"];
|
||||||
|
|
||||||
#[feature(globs, macro_rules, managed_boxes)];
|
#[feature(globs, macro_rules, managed_boxes, thread_local)];
|
||||||
|
|
||||||
extern mod alert;
|
extern mod alert;
|
||||||
extern mod azure;
|
extern mod azure;
|
||||||
|
|
|
@ -37,10 +37,12 @@ impl ProfilerChan {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ProfilerMsg {
|
pub enum ProfilerMsg {
|
||||||
// Normal message used for reporting time
|
/// Normal message used for reporting time
|
||||||
TimeMsg(ProfilerCategory, f64),
|
TimeMsg(ProfilerCategory, f64),
|
||||||
// Message used to force print the profiling metrics
|
/// Message used to force print the profiling metrics
|
||||||
PrintMsg,
|
PrintMsg,
|
||||||
|
/// Tells the profiler to shut down.
|
||||||
|
ExitMsg,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deriving(Eq, Clone, TotalEq, TotalOrd)]
|
#[deriving(Eq, Clone, TotalEq, TotalOrd)]
|
||||||
|
@ -136,7 +138,12 @@ impl Profiler {
|
||||||
None => {
|
None => {
|
||||||
// no-op to handle profiler messages when the profiler is inactive
|
// no-op to handle profiler messages when the profiler is inactive
|
||||||
spawn_named("Profiler", proc() {
|
spawn_named("Profiler", proc() {
|
||||||
while port.recv_opt().is_some() {}
|
loop {
|
||||||
|
match port.recv_opt() {
|
||||||
|
None | Some(ExitMsg) => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,13 +163,17 @@ impl Profiler {
|
||||||
loop {
|
loop {
|
||||||
let msg = self.port.recv_opt();
|
let msg = self.port.recv_opt();
|
||||||
match msg {
|
match msg {
|
||||||
Some (msg) => self.handle_msg(msg),
|
Some(msg) => {
|
||||||
|
if !self.handle_msg(msg) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
None => break
|
None => break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_msg(&mut self, msg: ProfilerMsg) {
|
fn handle_msg(&mut self, msg: ProfilerMsg) -> bool {
|
||||||
match msg {
|
match msg {
|
||||||
TimeMsg(category, t) => self.buckets.find_mut(&category).unwrap().push(t),
|
TimeMsg(category, t) => self.buckets.find_mut(&category).unwrap().push(t),
|
||||||
PrintMsg => match self.last_msg {
|
PrintMsg => match self.last_msg {
|
||||||
|
@ -170,8 +181,10 @@ impl Profiler {
|
||||||
Some(TimeMsg(..)) => self.print_buckets(),
|
Some(TimeMsg(..)) => self.print_buckets(),
|
||||||
_ => ()
|
_ => ()
|
||||||
},
|
},
|
||||||
|
ExitMsg => return false,
|
||||||
};
|
};
|
||||||
self.last_msg = Some(msg);
|
self.last_msg = Some(msg);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_buckets(&mut self) {
|
fn print_buckets(&mut self) {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
extern mod extra;
|
extern mod extra;
|
||||||
extern mod geom;
|
extern mod geom;
|
||||||
|
extern mod native;
|
||||||
|
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
pub mod geometry;
|
pub mod geometry;
|
||||||
|
@ -19,4 +20,5 @@ pub mod vec;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod task;
|
pub mod task;
|
||||||
|
pub mod workqueue;
|
||||||
|
|
||||||
|
|
295
src/components/util/workqueue.rs
Normal file
295
src/components/util/workqueue.rs
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
//! A work queue for scheduling units of work across threads in a fork-join fashion.
|
||||||
|
//!
|
||||||
|
//! Data associated with queues is simply a pair of unsigned integers. It is expected that a
|
||||||
|
//! higher-level API on top of this could allow safe fork-join parallelism.
|
||||||
|
|
||||||
|
use native;
|
||||||
|
use std::cast;
|
||||||
|
use std::rand::{Rng, XorShiftRng};
|
||||||
|
use std::rand;
|
||||||
|
use std::sync::atomics::{AtomicUint, SeqCst};
|
||||||
|
use std::sync::deque::{Abort, BufferPool, Data, Empty, Stealer, Worker};
|
||||||
|
use std::unstable::intrinsics;
|
||||||
|
|
||||||
|
/// A unit of work.
|
||||||
|
///
|
||||||
|
/// The type parameter `QUD` stands for "queue user data" and represents global custom data for the
|
||||||
|
/// entire work queue, and the type parameter `WUD` stands for "work user data" and represents
|
||||||
|
/// custom data specific to each unit of work.
|
||||||
|
pub struct WorkUnit<QUD,WUD> {
|
||||||
|
/// The function to execute.
|
||||||
|
fun: extern "Rust" fn(WUD, &mut WorkerProxy<QUD,WUD>),
|
||||||
|
/// Arbitrary data.
|
||||||
|
data: WUD,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Messages from the supervisor to the worker.
|
||||||
|
enum WorkerMsg<QUD,WUD> {
|
||||||
|
/// Tells the worker to start work.
|
||||||
|
StartMsg(Worker<WorkUnit<QUD,WUD>>, *mut AtomicUint, *QUD),
|
||||||
|
|
||||||
|
/// Tells the worker to stop. It can be restarted again with a `StartMsg`.
|
||||||
|
StopMsg,
|
||||||
|
|
||||||
|
/// Tells the worker thread to terminate.
|
||||||
|
ExitMsg,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Messages to the supervisor.
|
||||||
|
enum SupervisorMsg<QUD,WUD> {
|
||||||
|
FinishedMsg,
|
||||||
|
ReturnDequeMsg(uint, Worker<WorkUnit<QUD,WUD>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information that the supervisor thread keeps about the worker threads.
|
||||||
|
struct WorkerInfo<QUD,WUD> {
|
||||||
|
/// The communication channel to the workers.
|
||||||
|
chan: Chan<WorkerMsg<QUD,WUD>>,
|
||||||
|
/// The buffer pool for this deque.
|
||||||
|
pool: BufferPool<WorkUnit<QUD,WUD>>,
|
||||||
|
/// The worker end of the deque, if we have it.
|
||||||
|
deque: Option<Worker<WorkUnit<QUD,WUD>>>,
|
||||||
|
/// The thief end of the work-stealing deque.
|
||||||
|
thief: Stealer<WorkUnit<QUD,WUD>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information specific to each worker thread that the thread keeps.
|
||||||
|
struct WorkerThread<QUD,WUD> {
|
||||||
|
/// The index of this worker.
|
||||||
|
index: uint,
|
||||||
|
/// The communication port from the supervisor.
|
||||||
|
port: Port<WorkerMsg<QUD,WUD>>,
|
||||||
|
/// The communication channel on which messages are sent to the supervisor.
|
||||||
|
chan: SharedChan<SupervisorMsg<QUD,WUD>>,
|
||||||
|
/// The thief end of the work-stealing deque for all other workers.
|
||||||
|
other_deques: ~[Stealer<WorkUnit<QUD,WUD>>],
|
||||||
|
/// The random number generator for this worker.
|
||||||
|
rng: XorShiftRng,
|
||||||
|
}
|
||||||
|
|
||||||
|
static SPIN_COUNT: uint = 1000;
|
||||||
|
|
||||||
|
impl<QUD:Send,WUD:Send> WorkerThread<QUD,WUD> {
|
||||||
|
/// The main logic. This function starts up the worker and listens for
|
||||||
|
/// messages.
|
||||||
|
pub fn start(&mut self) {
|
||||||
|
loop {
|
||||||
|
// Wait for a start message.
|
||||||
|
let (mut deque, ref_count, queue_data) = match self.port.recv() {
|
||||||
|
StartMsg(deque, ref_count, queue_data) => (deque, ref_count, queue_data),
|
||||||
|
StopMsg => fail!("unexpected stop message"),
|
||||||
|
ExitMsg => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We're off!
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton): Can't use labeled break or continue cross-crate due to a Rust bug.
|
||||||
|
loop {
|
||||||
|
// FIXME(pcwalton): Nasty workaround for the lack of labeled break/continue
|
||||||
|
// cross-crate.
|
||||||
|
let mut work_unit = unsafe {
|
||||||
|
intrinsics::uninit()
|
||||||
|
};
|
||||||
|
match deque.pop() {
|
||||||
|
Some(work) => work_unit = work,
|
||||||
|
None => {
|
||||||
|
// Become a thief.
|
||||||
|
let mut i = 0;
|
||||||
|
let mut should_continue = true;
|
||||||
|
loop {
|
||||||
|
let victim = (self.rng.next_u32() as uint) % self.other_deques.len();
|
||||||
|
match self.other_deques[victim].steal() {
|
||||||
|
Empty | Abort => {
|
||||||
|
// Continue.
|
||||||
|
}
|
||||||
|
Data(work) => {
|
||||||
|
work_unit = work;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == SPIN_COUNT {
|
||||||
|
match self.port.try_recv() {
|
||||||
|
Some(StopMsg) => {
|
||||||
|
should_continue = false;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
Some(ExitMsg) => return,
|
||||||
|
Some(_) => fail!("unexpected message"),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
} else {
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !should_continue {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we have some work. Perform it.
|
||||||
|
let mut proxy = WorkerProxy {
|
||||||
|
worker: &mut deque,
|
||||||
|
ref_count: ref_count,
|
||||||
|
queue_data: queue_data,
|
||||||
|
};
|
||||||
|
(work_unit.fun)(work_unit.data, &mut proxy);
|
||||||
|
|
||||||
|
// The work is done. Now decrement the count of outstanding work items. If this was
|
||||||
|
// the last work unit in the queue, then send a message on the channel.
|
||||||
|
unsafe {
|
||||||
|
if (*ref_count).fetch_sub(1, SeqCst) == 1 {
|
||||||
|
self.chan.send(FinishedMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give the deque back to the supervisor.
|
||||||
|
self.chan.send(ReturnDequeMsg(self.index, deque))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A handle to the work queue that individual work units have.
|
||||||
|
pub struct WorkerProxy<'a,QUD,WUD> {
|
||||||
|
priv worker: &'a mut Worker<WorkUnit<QUD,WUD>>,
|
||||||
|
priv ref_count: *mut AtomicUint,
|
||||||
|
priv queue_data: *QUD,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,QUD,WUD:Send> WorkerProxy<'a,QUD,WUD> {
|
||||||
|
/// Enqueues a block into the work queue.
|
||||||
|
#[inline]
|
||||||
|
pub fn push(&mut self, work_unit: WorkUnit<QUD,WUD>) {
|
||||||
|
unsafe {
|
||||||
|
drop((*self.ref_count).fetch_add(1, SeqCst));
|
||||||
|
}
|
||||||
|
self.worker.push(work_unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the queue user data.
|
||||||
|
#[inline]
|
||||||
|
pub fn user_data<'a>(&'a self) -> &'a QUD {
|
||||||
|
unsafe {
|
||||||
|
cast::transmute(self.queue_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A work queue on which units of work can be submitted.
|
||||||
|
pub struct WorkQueue<QUD,WUD> {
|
||||||
|
/// Information about each of the workers.
|
||||||
|
priv workers: ~[WorkerInfo<QUD,WUD>],
|
||||||
|
/// A port on which deques can be received from the workers.
|
||||||
|
priv port: Port<SupervisorMsg<QUD,WUD>>,
|
||||||
|
/// The amount of work that has been enqueued.
|
||||||
|
priv work_count: uint,
|
||||||
|
/// Arbitrary user data.
|
||||||
|
data: QUD,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<QUD:Send,WUD:Send> WorkQueue<QUD,WUD> {
|
||||||
|
/// Creates a new work queue and spawns all the threads associated with
|
||||||
|
/// it.
|
||||||
|
pub fn new(thread_count: uint, user_data: QUD) -> WorkQueue<QUD,WUD> {
|
||||||
|
// Set up data structures.
|
||||||
|
let (supervisor_port, supervisor_chan) = SharedChan::new();
|
||||||
|
let (mut infos, mut threads) = (~[], ~[]);
|
||||||
|
for i in range(0, thread_count) {
|
||||||
|
let (worker_port, worker_chan) = Chan::new();
|
||||||
|
let mut pool = BufferPool::new();
|
||||||
|
let (worker, thief) = pool.deque();
|
||||||
|
infos.push(WorkerInfo {
|
||||||
|
chan: worker_chan,
|
||||||
|
pool: pool,
|
||||||
|
deque: Some(worker),
|
||||||
|
thief: thief,
|
||||||
|
});
|
||||||
|
threads.push(WorkerThread {
|
||||||
|
index: i,
|
||||||
|
port: worker_port,
|
||||||
|
chan: supervisor_chan.clone(),
|
||||||
|
other_deques: ~[],
|
||||||
|
rng: rand::weak_rng(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect workers to one another.
|
||||||
|
for i in range(0, thread_count) {
|
||||||
|
for j in range(0, thread_count) {
|
||||||
|
if i != j {
|
||||||
|
threads[i].other_deques.push(infos[j].thief.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(threads[i].other_deques.len() == thread_count - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn threads.
|
||||||
|
for thread in threads.move_iter() {
|
||||||
|
native::task::spawn(proc() {
|
||||||
|
let mut thread = thread;
|
||||||
|
thread.start()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkQueue {
|
||||||
|
workers: infos,
|
||||||
|
port: supervisor_port,
|
||||||
|
work_count: 0,
|
||||||
|
data: user_data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enqueues a block into the work queue.
|
||||||
|
#[inline]
|
||||||
|
pub fn push(&mut self, work_unit: WorkUnit<QUD,WUD>) {
|
||||||
|
match self.workers[0].deque {
|
||||||
|
None => {
|
||||||
|
fail!("tried to push a block but we don't have the deque?!")
|
||||||
|
}
|
||||||
|
Some(ref mut deque) => deque.push(work_unit),
|
||||||
|
}
|
||||||
|
self.work_count += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Synchronously runs all the enqueued tasks and waits for them to complete.
|
||||||
|
pub fn run(&mut self) {
|
||||||
|
// Tell the workers to start.
|
||||||
|
let mut work_count = AtomicUint::new(self.work_count);
|
||||||
|
for worker in self.workers.mut_iter() {
|
||||||
|
worker.chan.send(StartMsg(worker.deque.take_unwrap(), &mut work_count, &self.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the work to finish.
|
||||||
|
drop(self.port.recv());
|
||||||
|
self.work_count = 0;
|
||||||
|
|
||||||
|
// Tell everyone to stop.
|
||||||
|
for worker in self.workers.iter() {
|
||||||
|
worker.chan.send(StopMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get our deques back.
|
||||||
|
for _ in range(0, self.workers.len()) {
|
||||||
|
match self.port.recv() {
|
||||||
|
ReturnDequeMsg(index, deque) => self.workers[index].deque = Some(deque),
|
||||||
|
FinishedMsg => fail!("unexpected finished message!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown(&mut self) {
|
||||||
|
for worker in self.workers.iter() {
|
||||||
|
worker.chan.send(ExitMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue