Refactor RCU to use the CamelCase naming convention

This commit is contained in:
Patrick Walton 2012-06-14 19:20:08 -07:00
parent 6c6f7f99e4
commit 0347e4408c
11 changed files with 155 additions and 160 deletions

View file

@ -6,14 +6,15 @@
export ControlMsg, PingMsg; export ControlMsg, PingMsg;
export content; export content;
import result::extensions;
import dom::base::NodeScope; import dom::base::NodeScope;
import dom::rcu::writer_methods; import dom::rcu::WriterMethods;
import dom::style; import dom::style;
import dom = dom::base;
import layout::layout_task; import layout::layout_task;
import js::rust::methods; import js::rust::methods;
import result::extensions;
enum ControlMsg { enum ControlMsg {
ParseMsg(~str), ParseMsg(~str),
ExecuteMsg(~str), ExecuteMsg(~str),
@ -40,7 +41,7 @@ fn join_layout(scope: NodeScope, to_layout: chan<layout_task::Msg>) {
fn content(to_layout: chan<layout_task::Msg>) -> chan<ControlMsg> { fn content(to_layout: chan<layout_task::Msg>) -> chan<ControlMsg> {
task::spawn_listener::<ControlMsg> { task::spawn_listener::<ControlMsg> {
|from_master| |from_master|
let scope = dom::NodeScope(); let scope = NodeScope();
let rt = js::rust::rt(); let rt = js::rust::rt();
loop { loop {
alt from_master.recv() { alt from_master.recv() {

View file

@ -1,8 +1,11 @@
import dom::rcu::{writer_methods}; #[doc="The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements."]
import dom::rcu::WriterMethods;
import gfx::geometry::au; import gfx::geometry::au;
import geom::size::Size2D; import geom::size::Size2D;
import layout::base::layout_data; import layout::base::layout_data;
import util::tree; import util::tree;
import dvec::{dvec, extensions}; import dvec::{dvec, extensions};
enum NodeData = { enum NodeData = {
@ -61,12 +64,12 @@ enum ElementKind {
the primary box. Note that there may be multiple boxes per DOM node. the primary box. Note that there may be multiple boxes per DOM node.
"] "]
type Node = rcu::handle<NodeData, layout_data>; type Node = rcu::Handle<NodeData, layout_data>;
type NodeScope = rcu::scope<NodeData, layout_data>; type NodeScope = rcu::Scope<NodeData, layout_data>;
fn NodeScope() -> NodeScope { fn NodeScope() -> NodeScope {
rcu::scope() rcu::Scope()
} }
impl NodeScope for NodeScope { impl NodeScope for NodeScope {
@ -85,7 +88,7 @@ impl of tree::rd_tree_ops<Node> for NodeScope {
} }
fn with_tree_fields<R>(node: Node, f: fn(tree::fields<Node>) -> R) -> R { fn with_tree_fields<R>(node: Node, f: fn(tree::fields<Node>) -> R) -> R {
self.rd(node) { |n| f(n.tree) } self.read(node) { |n| f(n.tree) }
} }
} }
@ -95,7 +98,7 @@ impl of tree::wr_tree_ops<Node> for NodeScope {
} }
fn with_tree_fields<R>(node: Node, f: fn(tree::fields<Node>) -> R) -> R { fn with_tree_fields<R>(node: Node, f: fn(tree::fields<Node>) -> R) -> R {
self.wr(node) { |n| f(n.tree) } self.write(node) { |n| f(n.tree) }
} }
} }

View file

@ -1,115 +1,108 @@
#[doc(str = " #[doc(str = "
Implements the RCU dom-sharing model. This model allows for a single Implements the RCU DOM-sharing model. This model allows for a single writer and any number of
writer and any number of readers, but the writer must be able to readers, but the writer must be able to control and manage the lifetimes of the reader(s). For
control and manage the lifetimes of the reader(s). For simplicity I simplicity I will describe the implementation as though there were a single reader.
will describe the impl as though there were a single reader.
The basic idea is that every object in the RCU pool has both a reader The basic idea is that every object in the RCU pool has both a reader view and a writer view. The
view and a writer view. The writer always sees the writer view, which writer always sees the writer view, which contains the most up-to-date values. The reader uses the
contains the most up-to-date values. The reader uses the reader view, reader view, which contains the values as of the point where the reader was forked. When the
which contains the values as of the point where the reader was forked. writer joins the reader, the reader view will be synchronized with the writer view.
When the writer joins the reader, the reader view will be synchronized
with the writer view.
Internally, the way this works is using a copy-on-write scheme. Each Internally, the way this works is using a copy-on-write scheme. Each RCU node maintains two
RCU node maintains two pointers (`rd_ptr` and `wr_ptr`). Assuming pointers (`read_ptr` and `write_ptr`). Assuming that readers are active, when a writer wants to
that readers are active, when a writer wants to modify a node, it modify a node, it first copies the reader's data into a new pointer. Any writes that occur after
first copies the reader's data into a new pointer. Any writes that that point (but before the reader is joined) will operate on this same copy. When the reader is
occur after that point (but before the reader is joined) will operate joined, any nodes which the writer modified will free the stale reader data and update the reader
on this same copy. When the reader is joined, any nodes which the
writer modified will free the stale reader data and update the reader
pointer to be the same as the writer pointer. pointer to be the same as the writer pointer.
# Using the RCU APIs as a writer # Using the RCU APIs as a writer
You must first create a `scope` object. The scope object manages the You must first create a `scope` object. The scope object manages the memory and the RCU
memory and the RCU operations. RCU'd objects of some sendable type operations. RCU'd objects of some sendable type `T` are not referenced directly but rather through
`T` are not referenced directly but rather through a `handle<T>`. To a `handle<T>`. To create a new RCU object, you use `scope.handle(t)` where `t` is some initial
create a new RCU object, you use `scope.handle(t)` where `t` is some value of type `T`. To write to an RCU object, use `scope.write()` and to read from it use
initial value of type `T`. To write to an RCU object, use `scope.read()`. Be sure not to use the various `ReaderMethods`.
`scope.wr()` and to read from it use `scope.rd()`. Be sure not to use
the various `reader_methods`.
Handles can be freely sent between tasks but the RCU scope cannot. It Handles can be freely sent between tasks but the RCU scope cannot. It must stay with the writer
must stay with the writer task. You are responsible for correctly task. You are responsible for correctly invoking `reader_forked()` and `reader_joined()` to keep
invoking `reader_forked()` and `reader_joined()` to keep the RCU scope the RCU scope abreast of when the reader is active. Failure to do so will lead to race conditions
abreast of when the reader is active. Failure to do so will lead to or worse.
race conditions or worse.
# Using the RCU APIs as a reader # Using the RCU APIs as a reader
Import the `reader_methods` impl. When you receive a handle, you can Import the `ReaderMethods` impl. When you receive a handle, you can invoke `h.read { |v| ... }`
invoke `h.rd { |v| ... }` and so forth. There is also a piece of and so forth. There is also a piece of auxiliary data that can be optionally associated with each
auxiliary data that can be optionally associated with each handle. handle.
Note: if the type `T` contains mutable fields, then there is nothing Note: if the type `T` contains mutable fields, then there is nothing to stop the reader from
to stop the reader from mutating those fields in the `rd()` method. mutating those fields in the `read()` method. Do not do this. It will lead to race conditions.
Do not do this. It will lead to race conditions.
FIXME: We can enforce that this is not done by ensuring that the type `T` contains no mutable
fields.
# Auxiliary data # Auxiliary data
Readers can associate a piece of auxiliary data of type `A` along with Readers can associate a piece of auxiliary data of type `A` along with main nodes. This is
main nodes. This is convenient but dangerous: it is the reader's job convenient but dangerous: it is the reader's job to ensure that this data remains live independent
to ensure that this data remains live independent of the RCU nodes of the RCU nodes themselves.
themselves.
")]; ")];
import ptr::extensions; import ptr::extensions;
import core::libc::types::os::arch::c95::size_t; import core::libc::types::os::arch::c95::size_t;
export handle; export Handle;
export reader_methods; export ReaderMethods;
export writer_methods; export WriterMethods;
export scope; export Scope;
type scope_data<T:send,A> = { type ScopeData<T:send,A> = {
mut layout_active: bool, mut layout_active: bool,
mut free_list: [handle<T,A>], mut free_list: [Handle<T,A>],
mut first_dirty: handle<T,A> mut first_dirty: Handle<T,A>
}; };
resource scope_rsrc<T:send,A>(d: scope_data<T,A>) { resource ScopeResource<T:send,A>(d: ScopeData<T,A>) {
unsafe { unsafe {
for d.free_list.each { |h| free_handle(h); } for d.free_list.each { |h| free_handle(h); }
} }
} }
type scope<T:send,A> = @scope_rsrc<T,A>; type Scope<T:send,A> = @ScopeResource<T,A>;
type handle_data<T:send,A> = {mut rd_ptr: *T, type HandleData<T:send,A> = {mut read_ptr: *T,
mut wr_ptr: *T, mut write_ptr: *mut T,
mut rd_aux: *A, mut read_aux: *A,
mut next_dirty: handle<T,A>}; mut next_dirty: Handle<T,A>};
enum handle<T:send,A> { enum Handle<T:send,A> {
_handle(*handle_data<T,A>) _Handle(*HandleData<T,A>)
} }
impl private_methods<T:send,A> for handle<T,A> { impl HandlePrivate<T:send,A> for Handle<T,A> {
fn rd_ptr() -> *T unsafe { (**self).rd_ptr } fn read_ptr() -> *T unsafe { (**self).read_ptr }
fn wr_ptr() -> *T unsafe { (**self).wr_ptr } fn write_ptr() -> *mut T unsafe { (**self).write_ptr }
fn rd_aux() -> *A unsafe { (**self).rd_aux } fn read_aux() -> *A unsafe { (**self).read_aux }
fn next_dirty() -> handle<T,A> unsafe { (**self).next_dirty } fn next_dirty() -> Handle<T,A> unsafe { (**self).next_dirty }
fn set_rd_ptr(t: *T) unsafe { (**self).rd_ptr = t; } fn set_read_ptr(t: *T) unsafe { (**self).read_ptr = t; }
fn set_wr_ptr(t: *T) unsafe { (**self).wr_ptr = t; } fn set_write_ptr(t: *mut T) unsafe { (**self).write_ptr = t; }
fn set_rd_aux(t: *A) unsafe { (**self).rd_aux = t; } fn set_read_aux(t: *A) unsafe { (**self).read_aux = t; }
fn set_next_dirty(+h: handle<T,A>) unsafe { (**self).next_dirty = h; } fn set_next_dirty(+h: Handle<T,A>) unsafe { (**self).next_dirty = h; }
pure fn is_null() -> bool { (*self).is_null() } pure fn is_null() -> bool { (*self).is_null() }
fn is_not_null() -> bool { (*self).is_not_null() } fn is_not_null() -> bool { (*self).is_not_null() }
} }
impl reader_methods<T:send,A> for handle<T,A> { impl ReaderMethods<T:send,A> for Handle<T,A> {
#[doc(str = "access the reader's view of the handle's data")] #[doc(str = "Access the reader's view of the handle's data.")]
fn rd<U>(f: fn(T) -> U) -> U unsafe { fn read<U>(f: fn(T) -> U) -> U unsafe {
f(*self.rd_ptr()) f(*self.read_ptr())
} }
#[doc(str = "true if auxiliary data is associated with this handle")] #[doc(str = "True if auxiliary data is associated with this handle.")]
fn has_aux() -> bool unsafe { fn has_aux() -> bool unsafe {
self.rd_aux().is_not_null() self.read_aux().is_not_null()
} }
#[doc(str = "set the auxiliary data associated with this handle. #[doc(str = "set the auxiliary data associated with this handle.
@ -120,26 +113,24 @@ impl reader_methods<T:send,A> for handle<T,A> {
let p2 = p; let p2 = p;
unsafe::forget(p2); // Bump the reference count. unsafe::forget(p2); // Bump the reference count.
(**self).rd_aux = ptr::addr_of(*p); (**self).read_aux = ptr::addr_of(*p);
} }
#[doc(str = "access the auxiliary data associated with this handle.")] #[doc(str = "access the auxiliary data associated with this handle.")]
fn aux<U>(f: fn(A) -> U) -> U unsafe { fn aux<U>(f: fn(A) -> U) -> U unsafe {
assert self.has_aux(); assert self.has_aux();
f(*self.rd_aux()) f(*self.read_aux())
} }
} }
impl private_methods<T: copy send,A> for scope<T,A> { impl ScopePrivate<T: copy send,A> for Scope<T,A> {
fn clone(v: *T) -> *T unsafe { fn clone(v: *T) -> *T unsafe {
let n: *mut T = let n: *mut T =
unsafe::reinterpret_cast( unsafe::reinterpret_cast(libc::calloc(sys::size_of::<T>() as size_t, 1u as size_t));
libc::calloc(sys::size_of::<T>() as size_t, 1u as size_t));
// n.b.: this assignment will run the drop glue for <T,A>. // n.b.: this assignment will run the drop glue for <T,A>. *Hopefully* the fact that
// *Hopefully* the fact that everything is initialized to NULL // everything is initialized to NULL by calloc will make this ok. We may have to make the
// by calloc will make this ok. We may have to make the take // take glue be tolerant of this.
// glue be tolerant.
*n = unsafe{*v}; *n = unsafe{*v};
ret unsafe::reinterpret_cast(n); ret unsafe::reinterpret_cast(n);
@ -151,22 +142,24 @@ unsafe fn free<T:send>(t: *T) {
libc::free(unsafe::reinterpret_cast(t)); libc::free(unsafe::reinterpret_cast(t));
} }
unsafe fn free_handle<T:send,A>(h: handle<T,A>) { unsafe fn free_handle<T:send,A>(h: Handle<T,A>) {
free(h.rd_ptr()); free(h.read_ptr());
if h.wr_ptr() != h.rd_ptr() { free(h.wr_ptr()); } if h.write_ptr() != unsafe::reinterpret_cast(h.read_ptr()) {
free(unsafe::reinterpret_cast::<*mut T,*T>(h.write_ptr()));
}
} }
fn null_handle<T:send,A>() -> handle<T,A> { fn null_handle<T:send,A>() -> Handle<T,A> {
_handle(ptr::null()) _Handle(ptr::null())
} }
fn scope<T:send,A>() -> scope<T,A> { fn Scope<T:send,A>() -> Scope<T,A> {
@scope_rsrc({mut layout_active: false, @ScopeResource({mut layout_active: false,
mut free_list: [], mut free_list: [],
mut first_dirty: null_handle()}) mut first_dirty: null_handle()})
} }
impl writer_methods<T:copy send,A> for scope<T,A> { impl WriterMethods<T:copy send,A> for Scope<T,A> {
fn is_reader_forked() -> bool { fn is_reader_forked() -> bool {
self.layout_active self.layout_active
} }
@ -183,9 +176,9 @@ impl writer_methods<T:copy send,A> for scope<T,A> {
if self.first_dirty.is_not_null() { if self.first_dirty.is_not_null() {
let mut handle = self.first_dirty; let mut handle = self.first_dirty;
while (*handle).is_not_null() { while (*handle).is_not_null() {
free(handle.rd_ptr()); free(handle.read_ptr());
handle.set_rd_ptr(handle.wr_ptr()); handle.set_read_ptr(unsafe::reinterpret_cast(handle.write_ptr()));
let next_handle = handle.next_dirty(); let next_handle = handle.next_dirty();
handle.set_next_dirty(null_handle()); handle.set_next_dirty(null_handle());
handle = next_handle; handle = next_handle;
@ -197,31 +190,30 @@ impl writer_methods<T:copy send,A> for scope<T,A> {
self.layout_active = false; self.layout_active = false;
} }
fn rd<U>(h: handle<T,A>, f: fn(T) -> U) -> U unsafe { fn read<U>(h: Handle<T,A>, f: fn(T) -> U) -> U unsafe {
// Use the wr_ptr, which may be more up to date than the // Use the write_ptr, which may be more up to date than the read_ptr or may not
// rd_ptr or may not f(*h.write_ptr())
f(*h.wr_ptr())
} }
fn wr<U>(h: handle<T,A>, f: fn(T) -> U) -> U unsafe { fn write<U>(h: Handle<T,A>, f: fn(T) -> U) -> U unsafe {
if self.layout_active && h.rd_ptr() == h.wr_ptr() { if self.layout_active && h.read_ptr() == h.write_ptr() {
#debug["marking handle %? as dirty", h]; #debug["marking handle %? as dirty", h];
h.set_wr_ptr(self.clone(h.rd_ptr())); h.set_write_ptr(unsafe::reinterpret_cast(self.clone(h.read_ptr())));
h.set_next_dirty(self.first_dirty); h.set_next_dirty(self.first_dirty);
self.first_dirty = h; self.first_dirty = h;
} }
f(*h.wr_ptr()) f(*h.write_ptr())
} }
fn handle(v: T) -> handle<T,A> unsafe { fn handle(v: T) -> Handle<T,A> unsafe {
let d: *handle_data<T,A> = let d: *HandleData<T,A> =
unsafe::reinterpret_cast( unsafe::reinterpret_cast(
libc::malloc(sys::size_of::<handle_data<T,A>>() as size_t)); libc::malloc(sys::size_of::<HandleData<T,A>>() as size_t));
(*d).rd_ptr = self.clone(ptr::addr_of(v)); (*d).read_ptr = self.clone(ptr::addr_of(v));
(*d).wr_ptr = (*d).rd_ptr; (*d).write_ptr = unsafe::reinterpret_cast((*d).read_ptr);
(*d).rd_aux = ptr::null(); (*d).read_aux = ptr::null();
(*d).next_dirty = null_handle(); (*d).next_dirty = null_handle();
let h = _handle(d); let h = _Handle(d);
self.free_list += [h]; self.free_list += [h];
ret h; ret h;
} }
@ -240,11 +232,11 @@ mod test {
type processed = {flag: bool}; type processed = {flag: bool};
type animal_scope = scope<animal, processed>; type animal_scope = Scope<animal, processed>;
#[test] #[test]
fn handles_get_freed() { fn handles_get_freed() {
let s: animal_scope = scope(); let s: animal_scope = Scope();
s.handle({name:"henrietta", species:chicken(~{mut eggs_per_day:22u})}); s.handle({name:"henrietta", species:chicken(~{mut eggs_per_day:22u})});
s.handle({name:"ferdinand", species:bull(~{mut horns:3u})}); s.handle({name:"ferdinand", species:bull(~{mut horns:3u})});
} }
@ -265,7 +257,7 @@ mod test {
#[test] #[test]
fn interspersed_execution() { fn interspersed_execution() {
let s: animal_scope = scope(); let s: animal_scope = Scope();
let henrietta = let henrietta =
s.handle({name:"henrietta", s.handle({name:"henrietta",
species:chicken(~{mut eggs_per_day:0u})}); species:chicken(~{mut eggs_per_day:0u})});
@ -283,30 +275,30 @@ mod test {
s.reader_forked(); s.reader_forked();
let wait_chan = task::spawn_listener {|wait_port| let wait_chan = task::spawn_listener {|wait_port|
for uint::range(0u, iter2) { |_i| for uint::range(0u, iter2) { |_i|
comm::send(read_chan, henrietta.rd(read_characteristic)); comm::send(read_chan, henrietta.read(read_characteristic));
comm::send(read_chan, ferdinand.rd(read_characteristic)); comm::send(read_chan, ferdinand.read(read_characteristic));
comm::recv(wait_port); comm::recv(wait_port);
} }
}; };
let hrc = henrietta.rd(read_characteristic); let hrc = henrietta.read(read_characteristic);
assert hrc == (i * iter2); assert hrc == (i * iter2);
let frc = ferdinand.rd(read_characteristic); let frc = ferdinand.read(read_characteristic);
assert frc == i * iter2; assert frc == i * iter2;
for uint::range(0u, iter2) { |_i| for uint::range(0u, iter2) { |_i|
assert hrc == comm::recv(read_port); assert hrc == comm::recv(read_port);
s.wr(henrietta, mutate); s.write(henrietta, mutate);
assert frc == comm::recv(read_port); assert frc == comm::recv(read_port);
s.wr(ferdinand, mutate); s.write(ferdinand, mutate);
comm::send(wait_chan, ()); comm::send(wait_chan, ());
} }
s.reader_joined(); s.reader_joined();
} }
assert henrietta.rd(read_characteristic) == iter1 * iter2; assert henrietta.read(read_characteristic) == iter1 * iter2;
assert ferdinand.rd(read_characteristic) == iter1 * iter2; assert ferdinand.read(read_characteristic) == iter1 * iter2;
} }
} }

View file

@ -3,7 +3,7 @@
import dom::base::{Element, ElementKind, HTMLDivElement, HTMLImageElement, Node, NodeData}; import dom::base::{Element, ElementKind, HTMLDivElement, HTMLImageElement, Node, NodeData};
import dom::base::{NodeKind}; import dom::base::{NodeKind};
import dom::rcu; import dom::rcu;
import dom::rcu::reader_methods; import dom::rcu::ReaderMethods;
import gfx::geometry; import gfx::geometry;
import gfx::geometry::{au, zero_size_au}; import gfx::geometry::{au, zero_size_au};
import geom::point::Point2D; import geom::point::Point2D;
@ -62,7 +62,7 @@ impl of tree::rd_tree_ops<Node> for ntree {
} }
fn with_tree_fields<R>(&&n: Node, f: fn(tree::fields<Node>) -> R) -> R { fn with_tree_fields<R>(&&n: Node, f: fn(tree::fields<Node>) -> R) -> R {
n.rd { |n| f(n.tree) } n.read { |n| f(n.tree) }
} }
} }
@ -138,7 +138,7 @@ impl PrivateNodeMethods for Node {
s += " "; s += " ";
} }
s += #fmt("%?", self.rd({ |n| copy n.kind })); s += #fmt("%?", self.read({ |n| copy n.kind }));
#debug["%s", s]; #debug["%s", s];
for ntree.each_child(self) { |kid| kid.dump_indent(indent + 1u) } for ntree.each_child(self) { |kid| kid.dump_indent(indent + 1u) }
@ -156,7 +156,7 @@ impl NodeMethods for Node {
mod test { mod test {
import dom::base::{Element, ElementData, HTMLDivElement, HTMLImageElement, Node, NodeKind}; import dom::base::{Element, ElementData, HTMLDivElement, HTMLImageElement, Node, NodeKind};
import dom::base::{NodeScope, wr_tree_ops}; import dom::base::{NodeScope, wr_tree_ops};
import dom::rcu::scope; import dom::rcu::Scope;
import box_builder::{box_builder_methods}; import box_builder::{box_builder_methods};
/* /*
@ -186,7 +186,7 @@ mod test {
#[test] #[test]
#[ignore(reason = "busted")] #[ignore(reason = "busted")]
fn do_layout() { fn do_layout() {
let s = scope(); let s = Scope();
fn mk_img(size: Size2D<au>) -> ~ElementKind { fn mk_img(size: Size2D<au>) -> ~ElementKind {
~HTMLImageElement({mut size: size}) ~HTMLImageElement({mut size: size})

View file

@ -2,7 +2,7 @@
import dom::base::{ElementData, HTMLDivElement, HTMLImageElement, Element, Text, Node}; import dom::base::{ElementData, HTMLDivElement, HTMLImageElement, Element, Text, Node};
import dom::style::{display_type, di_block, di_inline, di_none}; import dom::style::{display_type, di_block, di_inline, di_none};
import dom::rcu::reader_methods; import dom::rcu::ReaderMethods;
import gfx::geometry; import gfx::geometry;
import layout::base::{BlockBox, Box, BoxKind, InlineBox, IntrinsicBox, NodeMethods, TextBox}; import layout::base::{BlockBox, Box, BoxKind, InlineBox, IntrinsicBox, NodeMethods, TextBox};
import layout::base::{appearance, btree, ntree, rd_tree_ops, wr_tree_ops}; import layout::base::{appearance, btree, ntree, rd_tree_ops, wr_tree_ops};
@ -153,7 +153,7 @@ impl box_builder_priv for Node {
size. size.
"] "]
fn determine_box_kind() -> BoxKind { fn determine_box_kind() -> BoxKind {
alt self.rd({ |n| copy n.kind }) { alt self.read({ |n| copy n.kind }) {
~Text(string) { ~Text(string) {
TextBox(@text_box(string)) TextBox(@text_box(string))
} }

View file

@ -1,7 +1,7 @@
#[doc="Inline layout."] #[doc="Inline layout."]
import dom::rcu; import dom::rcu;
import dom::rcu::reader_methods; import dom::rcu::ReaderMethods;
import geom::point::Point2D; import geom::point::Point2D;
import geom::size::Size2D; import geom::size::Size2D;
import gfx::geometry::au; import gfx::geometry::au;

View file

@ -1,14 +1,12 @@
#[doc = " #[doc = "
The layout task. Performs layout on the DOM, builds display lists and sends them to be
The layout task. Performs layout on the dom, builds display lists and sends rendered.
them to be rendered
"]; "];
import box_builder::box_builder_methods; import box_builder::box_builder_methods;
import dl = display_list; import dl = display_list;
import dom::base::Node; import dom::base::Node;
import dom::rcu::scope; import dom::rcu::Scope;
import dom::style::stylesheet; import dom::style::stylesheet;
import gfx::geometry::{au, au_to_px, box, px_to_au}; import gfx::geometry::{au, au_to_px, box, px_to_au};
import gfx::renderer; import gfx::renderer;

View file

@ -1,6 +1,7 @@
#[doc="Applies style to boxes."] #[doc="Applies the appropriate CSS style to boxes."]
import dom::base::{HTMLImageElement, Element, Node};
import dom::rcu::reader_methods; import dom::base::{Element, HTMLImageElement, Node};
import dom::rcu::ReaderMethods;
import image::base::load; import image::base::load;
import layout::base::*; import layout::base::*;
import style::style_methods; import style::style_methods;
@ -17,7 +18,7 @@ impl ApplyStyleBoxMethods for @Box {
#[doc="Applies CSS style."] #[doc="Applies CSS style."]
fn apply_style() { fn apply_style() {
// Right now, we only handle images. // Right now, we only handle images.
self.node.rd { self.node.read {
|node| |node|
alt node.kind { alt node.kind {
~Element(element) { ~Element(element) {

View file

@ -1,12 +1,12 @@
#[doc="Perform css selector matching"] #[doc="Performs CSS selector matching."]
import base::{layout_data};
import dom::base::{Element, ElementData, Node, Text}; import dom::base::{Element, ElementData, Node, Text};
import dom::style::{selector, style_decl, font_size, display, text_color, background_color, import dom::style::{selector, style_decl, font_size, display, text_color, background_color,
stylesheet, element, child, descendant, sibling, attr, exact, exists, includes, stylesheet, element, child, descendant, sibling, attr, exact, exists, includes,
starts_with}; starts_with};
import dom::rcu::{reader_methods}; import dom::rcu::ReaderMethods;
import style::{computed_style, default_style_for_node_kind}; import style::{computed_style, default_style_for_node_kind};
import base::{layout_data};
export matching_methods; export matching_methods;
@ -72,7 +72,7 @@ impl priv_matching_methods for Node {
alt *sel { alt *sel {
child(_, _) | descendant(_, _) | sibling(_, _) { ret false; } child(_, _) | descendant(_, _) | sibling(_, _) { ret false; }
element(tag, attrs) { element(tag, attrs) {
alt self.rd { |n| copy *n.kind } { alt self.read { |n| copy *n.kind } {
Element(elmt) { Element(elmt) {
if !(tag == "*" || tag == elmt.tag_name) { if !(tag == "*" || tag == elmt.tag_name) {
ret false; ret false;
@ -100,7 +100,7 @@ impl priv_matching_methods for Node {
alt *sel { alt *sel {
element(str, atts) { ret self.matches_element(sel); } element(str, atts) { ret self.matches_element(sel); }
child(sel1, sel2) { child(sel1, sel2) {
alt self.rd { |n| n.tree.parent } { alt self.read { |n| n.tree.parent } {
some(parent) { some(parent) {
ret self.matches_element(sel2) && ret self.matches_element(sel2) &&
parent.matches_selector(sel1); parent.matches_selector(sel1);
@ -115,7 +115,7 @@ impl priv_matching_methods for Node {
//loop over all ancestors to check if they are the person //loop over all ancestors to check if they are the person
//we should be descended from. //we should be descended from.
let mut cur_parent = alt self.rd { |n| n.tree.parent } { let mut cur_parent = alt self.read { |n| n.tree.parent } {
some(parent) { parent } some(parent) { parent }
none { ret false; } none { ret false; }
}; };
@ -123,7 +123,7 @@ impl priv_matching_methods for Node {
loop { loop {
if cur_parent.matches_selector(sel1) { ret true; } if cur_parent.matches_selector(sel1) { ret true; }
cur_parent = alt cur_parent.rd { |n| n.tree.parent } { cur_parent = alt cur_parent.read { |n| n.tree.parent } {
some(parent) { parent } some(parent) { parent }
none { ret false; } none { ret false; }
}; };
@ -133,13 +133,13 @@ impl priv_matching_methods for Node {
if !self.matches_element(sel2) { ret false; } if !self.matches_element(sel2) { ret false; }
// Loop over this node's previous siblings to see if they match. // Loop over this node's previous siblings to see if they match.
alt self.rd { |n| n.tree.prev_sibling } { alt self.read { |n| n.tree.prev_sibling } {
some(sib) { some(sib) {
let mut cur_sib = sib; let mut cur_sib = sib;
loop { loop {
if cur_sib.matches_selector(sel1) { ret true; } if cur_sib.matches_selector(sel1) { ret true; }
cur_sib = alt cur_sib.rd { |n| n.tree.prev_sibling } { cur_sib = alt cur_sib.read { |n| n.tree.prev_sibling } {
some(sib) { sib } some(sib) { sib }
none { break; } none { break; }
}; };
@ -149,13 +149,13 @@ impl priv_matching_methods for Node {
} }
// check the rest of the siblings // check the rest of the siblings
alt self.rd { |n| n.tree.next_sibling } { alt self.read { |n| n.tree.next_sibling } {
some(sib) { some(sib) {
let mut cur_sib = sib; let mut cur_sib = sib;
loop { loop {
if cur_sib.matches_selector(sel1) { ret true; } if cur_sib.matches_selector(sel1) { ret true; }
cur_sib = alt cur_sib.rd { |n| n.tree.next_sibling } { cur_sib = alt cur_sib.read { |n| n.tree.next_sibling } {
some(sib) { sib } some(sib) { sib }
none { break; } none { break; }
}; };
@ -174,7 +174,7 @@ impl matching_methods for Node {
#[doc="Compare an html element to a list of css rules and update its #[doc="Compare an html element to a list of css rules and update its
style according to the rules matching it."] style according to the rules matching it."]
fn match_css_style(styles : stylesheet) -> computed_style { fn match_css_style(styles : stylesheet) -> computed_style {
let node_kind = self.rd { |n| copy *n.kind }; let node_kind = self.read { |n| copy *n.kind };
let style = let style =
@default_style_for_node_kind(node_kind); @default_style_for_node_kind(node_kind);

View file

@ -3,7 +3,7 @@
import dom::style::{display_type, di_block, di_inline, di_none, stylesheet}; import dom::style::{display_type, di_block, di_inline, di_none, stylesheet};
import dom::base::{Element, HTMLDivElement, HTMLHeadElement, HTMLImageElement, Node, NodeKind}; import dom::base::{Element, HTMLDivElement, HTMLHeadElement, HTMLImageElement, Node, NodeKind};
import dom::base::{Text}; import dom::base::{Text};
import dom::rcu::reader_methods; import dom::rcu::ReaderMethods;
import layout::base::*; // FIXME: resolve bug requires * import layout::base::*; // FIXME: resolve bug requires *
import matching::matching_methods; import matching::matching_methods;
import util::color::{Color, rgb}; import util::color::{Color, rgb};

View file

@ -1,20 +1,20 @@
#[doc="Constructs a DOM tree from an incoming token stream."] #[doc="Constructs a DOM tree from an incoming token stream."]
import dom::rcu::writer_methods;
import dom::base::{Attr, Element, ElementData, ElementKind, HTMLDivElement, HTMLHeadElement}; import dom::base::{Attr, Element, ElementData, ElementKind, HTMLDivElement, HTMLHeadElement};
import dom::base::{HTMLImageElement, Node, NodeScope, Text, UnknownElement, rd_tree_ops}; import dom::base::{HTMLImageElement, Node, NodeScope, Text, UnknownElement, rd_tree_ops};
import dom::base::{wr_tree_ops}; import dom::base::{wr_tree_ops};
import dom = dom::base; import dom::rcu::WriterMethods;
import dvec::extensions;
import geom::size::Size2D; import geom::size::Size2D;
import gfx::geometry; import gfx::geometry;
import gfx::geometry::au; import gfx::geometry::au;
import parser = parser::lexer::html; import parser = parser::lexer::html;
import parser::token; import parser::token;
import dvec::extensions;
fn link_up_attribute(scope: NodeScope, node: Node, -key: str, -value: str) { fn link_up_attribute(scope: NodeScope, node: Node, -key: str, -value: str) {
// TODO: Implement atoms so that we don't always perform string comparisons. // TODO: Implement atoms so that we don't always perform string comparisons.
scope.rd(node) { scope.read(node) {
|node_contents| |node_contents|
alt *node_contents.kind { alt *node_contents.kind {
Element(element) { Element(element) {