mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
implement rcu using an amazing quantity of unsafe ops
This commit is contained in:
parent
731416b00c
commit
41bcc6a523
5 changed files with 315 additions and 77 deletions
|
@ -1 +1 @@
|
|||
Subproject commit d4575343780e925221e97d9ab21ebcdf37fe9c34
|
||||
Subproject commit 0e8a08bf861ae4d6271920e16bfd0022bd2aad77
|
|
@ -1,4 +1,4 @@
|
|||
import dom::rcu::methods;
|
||||
import dom::rcu::{scope, writer_methods};
|
||||
import gfx::geom::{au, size};
|
||||
import layout::base::box;
|
||||
import util::tree;
|
||||
|
@ -6,10 +6,6 @@ import util::tree;
|
|||
enum node_data = {
|
||||
tree: tree::fields<node>,
|
||||
kind: node_kind,
|
||||
|
||||
// Points to the primary box. Note that there may be multiple
|
||||
// boxes per DOM node.
|
||||
mut box: option<box>,
|
||||
};
|
||||
|
||||
enum node_kind {
|
||||
|
@ -17,17 +13,13 @@ enum node_kind {
|
|||
nk_img(size<au>)
|
||||
}
|
||||
|
||||
type node = rcu::handle<node_data>;
|
||||
// The rd_aux data is a (weak) pointer to the primary box. Note that
|
||||
// there may be multiple boxes per DOM node.
|
||||
type node = rcu::handle<node_data, box>;
|
||||
|
||||
impl of tree::tree for node {
|
||||
fn tree_fields() -> tree::fields<node> {
|
||||
ret self.get().tree;
|
||||
impl methods for scope<node_data, box> {
|
||||
fn new_node(+k: node_kind) -> node {
|
||||
self.handle(node_data({tree: tree::empty(),
|
||||
kind: k}))
|
||||
}
|
||||
}
|
||||
|
||||
fn new_node(+k: node_kind) -> node {
|
||||
rcu::handle(node_data({tree: tree::empty(),
|
||||
kind: k,
|
||||
mut box: none}))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,245 @@
|
|||
enum handle<T> {
|
||||
_handle(@T)
|
||||
}
|
||||
import ptr::extensions;
|
||||
|
||||
impl methods<T> for handle<T> {
|
||||
fn get() -> @T { *self }
|
||||
export handle;
|
||||
export reader_methods;
|
||||
export writer_methods;
|
||||
export scope;
|
||||
|
||||
fn with(f: fn(T)) {
|
||||
f(**self)
|
||||
type scope_data<T:send,A> = {
|
||||
mut layout_active: bool,
|
||||
mut free_list: [handle<T,A>],
|
||||
mut first_dirty: handle<T,A>
|
||||
};
|
||||
|
||||
resource scope_rsrc<T:send,A>(d: scope_data<T,A>) {
|
||||
unsafe {
|
||||
for d.free_list.each { |h| free_handle(h); }
|
||||
}
|
||||
}
|
||||
|
||||
fn handle<T:copy>(t: T) -> handle<T> {
|
||||
_handle(@t)
|
||||
type scope<T:send,A> = @scope_rsrc<T,A>;
|
||||
|
||||
type handle_data<T:send,A> = {mut rd_ptr: *T,
|
||||
mut wr_ptr: *T,
|
||||
mut rd_aux: *A,
|
||||
mut next_dirty: handle<T,A>};
|
||||
enum handle<T:send,A> {
|
||||
_handle(*handle_data<T,A>)
|
||||
}
|
||||
|
||||
impl private_methods<T:send,A> for handle<T,A> {
|
||||
fn rd_ptr() -> *T unsafe { (**self).rd_ptr }
|
||||
fn wr_ptr() -> *T unsafe { (**self).wr_ptr }
|
||||
fn rd_aux() -> *A unsafe { (**self).rd_aux }
|
||||
fn next_dirty() -> handle<T,A> unsafe { (**self).next_dirty }
|
||||
|
||||
fn set_rd_ptr(t: *T) unsafe { (**self).rd_ptr = t; }
|
||||
fn set_wr_ptr(t: *T) unsafe { (**self).wr_ptr = t; }
|
||||
fn set_rd_aux(t: *A) unsafe { (**self).rd_aux = t; }
|
||||
fn set_next_dirty(+h: handle<T,A>) unsafe { (**self).next_dirty = h; }
|
||||
|
||||
fn is_null() -> bool { (*self).is_null() }
|
||||
fn is_not_null() -> bool { (*self).is_not_null() }
|
||||
}
|
||||
|
||||
impl reader_methods<T:send,A> for handle<T,A> {
|
||||
fn rd<U>(f: fn(T) -> U) -> U unsafe {
|
||||
f(*self.rd_ptr())
|
||||
}
|
||||
|
||||
fn has_aux() -> bool unsafe {
|
||||
self.rd_aux().is_not_null()
|
||||
}
|
||||
|
||||
fn set_aux(p: @A) unsafe {
|
||||
(**self).rd_aux = ptr::addr_of(*p);
|
||||
}
|
||||
|
||||
fn aux<U>(f: fn(A) -> U) -> U unsafe {
|
||||
// warning: do not use if has_aux() is false!
|
||||
f(*self.rd_aux())
|
||||
}
|
||||
}
|
||||
|
||||
impl private_methods<T:send,A> for scope<T,A> {
|
||||
fn clone(v: *T) -> *T unsafe {
|
||||
let n: *mut T =
|
||||
unsafe::reinterpret_cast(
|
||||
libc::calloc(sys::size_of::<T>(), 1u));
|
||||
|
||||
// n.b.: this assignment will run the drop glue for <T,A>.
|
||||
// *Hopefully* the fact that everything is initialized to NULL
|
||||
// by calloc will make this ok. We may have to make the take
|
||||
// glue be tolerant.
|
||||
*n = unsafe{*v};
|
||||
|
||||
ret unsafe::reinterpret_cast(n);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn free<T:send>(t: *T) {
|
||||
let _x <- *unsafe::reinterpret_cast::<*T,*mut T>(t);
|
||||
libc::free(unsafe::reinterpret_cast(t));
|
||||
}
|
||||
|
||||
unsafe fn free_handle<T:send,A>(h: handle<T,A>) {
|
||||
free(h.rd_ptr());
|
||||
if h.wr_ptr() != h.rd_ptr() { free(h.wr_ptr()); }
|
||||
}
|
||||
|
||||
fn null_handle<T:send,A>() -> handle<T,A> {
|
||||
_handle(ptr::null())
|
||||
}
|
||||
|
||||
fn scope<T:send,A>() -> scope<T,A> {
|
||||
@scope_rsrc({mut layout_active: false,
|
||||
mut free_list: [],
|
||||
mut first_dirty: null_handle()})
|
||||
}
|
||||
|
||||
impl writer_methods<T:send,A> for scope<T,A> {
|
||||
fn reader_forked() {
|
||||
assert !self.layout_active;
|
||||
assert self.first_dirty.is_null();
|
||||
self.layout_active = true;
|
||||
}
|
||||
|
||||
fn reader_joined() unsafe {
|
||||
assert self.layout_active;
|
||||
|
||||
if self.first_dirty.is_not_null() {
|
||||
let mut handle = self.first_dirty;
|
||||
while (*handle).is_not_null() {
|
||||
free(handle.rd_ptr());
|
||||
|
||||
handle.set_rd_ptr(handle.wr_ptr());
|
||||
let next_handle = handle.next_dirty();
|
||||
handle.set_next_dirty(null_handle());
|
||||
handle = next_handle;
|
||||
}
|
||||
self.first_dirty = null_handle();
|
||||
}
|
||||
|
||||
self.layout_active = false;
|
||||
}
|
||||
|
||||
fn wr<U>(h: handle<T,A>, f: fn(T) -> U) -> U unsafe {
|
||||
if self.layout_active {
|
||||
if h.rd_ptr() == h.wr_ptr() {
|
||||
h.set_wr_ptr(self.clone(h.rd_ptr()));
|
||||
h.set_next_dirty(self.first_dirty);
|
||||
self.first_dirty = h;
|
||||
}
|
||||
}
|
||||
f(*h.wr_ptr())
|
||||
}
|
||||
|
||||
fn handle(v: T) -> handle<T,A> unsafe {
|
||||
let d: *handle_data<T,A> =
|
||||
unsafe::reinterpret_cast(
|
||||
libc::malloc(sys::size_of::<handle_data<T,A>>()));
|
||||
(*d).rd_ptr = self.clone(ptr::addr_of(v));
|
||||
(*d).wr_ptr = (*d).rd_ptr;
|
||||
(*d).rd_aux = ptr::null();
|
||||
(*d).next_dirty = null_handle();
|
||||
let h = _handle(d);
|
||||
self.free_list += [h];
|
||||
ret h;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
type animal = {name: str, species: species};
|
||||
enum species {
|
||||
chicken(~chicken),
|
||||
bull(~bull)
|
||||
}
|
||||
type chicken = {mut eggs_per_day:uint};
|
||||
type bull = {mut horns:uint};
|
||||
|
||||
type processed = {flag: bool};
|
||||
|
||||
type animal_scope = scope<animal, processed>;
|
||||
|
||||
#[test]
|
||||
fn handles_get_freed() {
|
||||
let s: animal_scope = scope();
|
||||
s.handle({name:"henrietta", species:chicken(~{mut eggs_per_day:22u})});
|
||||
s.handle({name:"ferdinand", species:bull(~{mut horns:3u})});
|
||||
}
|
||||
|
||||
fn describe(a: animal) -> str {
|
||||
let s = alt a.species {
|
||||
chicken(c) { #fmt["chicken who lays %u eggs per day",
|
||||
c.eggs_per_day] }
|
||||
bull(c) { #fmt["bull with %u horns", c.horns] }
|
||||
};
|
||||
#fmt["%s, the %s", a.name, s]
|
||||
}
|
||||
|
||||
fn mutate(a: animal) {
|
||||
alt a.species {
|
||||
chicken(c) { c.eggs_per_day += 1u; }
|
||||
bull(c) { c.horns += 1u; }
|
||||
}
|
||||
}
|
||||
|
||||
fn read_characteristic(a: animal) -> uint {
|
||||
alt a.species {
|
||||
chicken(c) { c.eggs_per_day }
|
||||
bull(c) { c.horns }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interspersed_execution() {
|
||||
let s: animal_scope = scope();
|
||||
let henrietta =
|
||||
s.handle({name:"henrietta",
|
||||
species:chicken(~{mut eggs_per_day:0u})});
|
||||
let ferdinand =
|
||||
s.handle({name:"ferdinand",
|
||||
species:bull(~{mut horns:0u})});
|
||||
|
||||
let iter1 = 3u;
|
||||
let iter2 = 22u;
|
||||
let read_port = comm::port();
|
||||
let read_chan = comm::chan(read_port);
|
||||
|
||||
// fire up a reader task
|
||||
uint::range(0u, iter1) { |i|
|
||||
s.reader_forked();
|
||||
let wait_chan = task::spawn_listener {|wait_port|
|
||||
uint::range(0u, iter2) { |_i|
|
||||
comm::send(read_chan, henrietta.rd(describe));
|
||||
comm::send(read_chan, ferdinand.rd(describe));
|
||||
comm::recv(wait_port);
|
||||
}
|
||||
};
|
||||
|
||||
let hrc = henrietta.rd(read_characteristic);
|
||||
assert hrc == (i * iter2);
|
||||
|
||||
let frc = ferdinand.rd(read_characteristic);
|
||||
assert frc == i * iter2;
|
||||
|
||||
let exp1 = henrietta.rd(describe);
|
||||
let exp2 = ferdinand.rd(describe);
|
||||
|
||||
uint::range(0u, iter2) { |_i|
|
||||
assert exp1 == comm::recv(read_port);
|
||||
s.wr(henrietta, mutate);
|
||||
assert exp2 == comm::recv(read_port);
|
||||
s.wr(ferdinand, mutate);
|
||||
comm::send(wait_chan, ());
|
||||
}
|
||||
s.reader_joined();
|
||||
}
|
||||
|
||||
assert henrietta.rd(read_characteristic) == iter1 * iter2;
|
||||
assert ferdinand.rd(read_characteristic) == iter1 * iter2;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,35 +1,41 @@
|
|||
import dom::base::{nk_img, node_data, node_kind, node, new_node};
|
||||
import dom::base::{nk_div, nk_img, node_data, node_kind, node};
|
||||
import dom::rcu;
|
||||
import dom::rcu::methods;
|
||||
import dom::rcu::reader_methods;
|
||||
import gfx::geom;
|
||||
import gfx::geom::{size, rect, point, au};
|
||||
import util::{tree};
|
||||
|
||||
enum box = @{
|
||||
tree: tree::fields<box>,
|
||||
enum box = {
|
||||
tree: tree::fields<@box>,
|
||||
node: node,
|
||||
mut bounds: geom::rect<au>
|
||||
};
|
||||
|
||||
impl of tree::tree for box {
|
||||
fn tree_fields() -> tree::fields<box> {
|
||||
ret self.tree;
|
||||
impl of tree::tree for node {
|
||||
fn with_tree_fields<R>(f: fn(tree::fields<node>) -> R) -> R {
|
||||
f(self.rd { |f| f.tree })
|
||||
}
|
||||
}
|
||||
|
||||
fn new_box(n: node) -> box {
|
||||
box(@{tree: tree::empty(),
|
||||
impl of tree::tree for @box {
|
||||
fn with_tree_fields<R>(f: fn(tree::fields<@box>) -> R) -> R {
|
||||
f(self.tree)
|
||||
}
|
||||
}
|
||||
|
||||
fn new_box(n: node) -> @box {
|
||||
@box({tree: tree::empty(),
|
||||
node: n,
|
||||
mut bounds: geom::zero_rect_au()})
|
||||
}
|
||||
|
||||
fn linked_box(n: node) -> box {
|
||||
fn linked_box(n: node) -> @box {
|
||||
let b = new_box(n);
|
||||
n.box = some(b);
|
||||
n.set_aux(b);
|
||||
ret b;
|
||||
}
|
||||
|
||||
fn reflow_block(root: box, available_width: au) {
|
||||
fn reflow_block(root: @box, available_width: au) {
|
||||
// Root here is the root of the reflow, not necessarily the doc as
|
||||
// a whole.
|
||||
//
|
||||
|
@ -38,7 +44,8 @@ fn reflow_block(root: box, available_width: au) {
|
|||
// - generates root.bounds.origin for each child
|
||||
// - and recursively computes the bounds for each child
|
||||
|
||||
alt root.node.get().kind {
|
||||
let k = root.node.rd() { |r| r.kind };
|
||||
alt k {
|
||||
nk_img(size) {
|
||||
root.bounds.size = size;
|
||||
ret;
|
||||
|
@ -62,6 +69,8 @@ fn reflow_block(root: box, available_width: au) {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
import dom::base::{nk_img, node_data, node_kind, node, methods};
|
||||
import dom::rcu::scope;
|
||||
|
||||
/*
|
||||
use sdl;
|
||||
|
@ -79,7 +88,7 @@ mod test {
|
|||
}
|
||||
*/
|
||||
|
||||
fn flat_bounds(root: box) -> [geom::rect<au>] {
|
||||
fn flat_bounds(root: @box) -> [geom::rect<au>] {
|
||||
let mut r = [];
|
||||
for tree::each_child(root) {|c|
|
||||
r += flat_bounds(c);
|
||||
|
@ -89,10 +98,12 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn do_layout() {
|
||||
let n0 = new_node(nk_img(size(au(10),au(10))));
|
||||
let n1 = new_node(nk_img(size(au(10),au(15))));
|
||||
let n2 = new_node(nk_img(size(au(10),au(20))));
|
||||
let n3 = new_node(nk_div);
|
||||
let s = scope();
|
||||
|
||||
let n0 = s.new_node(nk_img(size(au(10),au(10))));
|
||||
let n1 = s.new_node(nk_img(size(au(10),au(15))));
|
||||
let n2 = s.new_node(nk_img(size(au(10),au(20))));
|
||||
let n3 = s.new_node(nk_div);
|
||||
|
||||
tree::add_child(n3, n0);
|
||||
tree::add_child(n3, n1);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
type fields<T> = @{
|
||||
type fields<T> = {
|
||||
mut parent: option<T>,
|
||||
mut first_child: option<T>,
|
||||
mut last_child: option<T>,
|
||||
|
@ -7,59 +7,62 @@ type fields<T> = @{
|
|||
};
|
||||
|
||||
iface tree {
|
||||
fn tree_fields() -> fields<self>;
|
||||
fn with_tree_fields<R>(f: fn(fields<self>) -> R) -> R;
|
||||
}
|
||||
|
||||
fn each_child<T:copy tree>(
|
||||
node: T, f: fn(T) -> bool) {
|
||||
|
||||
let mut p = node.tree_fields().first_child;
|
||||
let mut p = node.with_tree_fields { |f| f.first_child };
|
||||
loop {
|
||||
alt p {
|
||||
none { ret; }
|
||||
some(c) {
|
||||
if !f(c) { ret; }
|
||||
p = c.tree_fields().next_sibling;
|
||||
p = c.with_tree_fields { |f| f.next_sibling };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn empty<T>() -> fields<T> {
|
||||
@{mut parent: none,
|
||||
mut first_child: none,
|
||||
mut last_child: none,
|
||||
mut prev_sibling: none,
|
||||
mut next_sibling: none}
|
||||
{mut parent: none,
|
||||
mut first_child: none,
|
||||
mut last_child: none,
|
||||
mut prev_sibling: none,
|
||||
mut next_sibling: none}
|
||||
}
|
||||
|
||||
fn add_child<T:copy tree>(
|
||||
node: T, child: T) {
|
||||
|
||||
let child_tf = child.tree_fields();
|
||||
alt child_tf.parent {
|
||||
some(_) { fail "Already has a parent"; }
|
||||
none { child_tf.parent = some(node); }
|
||||
child.with_tree_fields { |child_tf|
|
||||
alt child_tf.parent {
|
||||
some(_) { fail "Already has a parent"; }
|
||||
none { child_tf.parent = some(node); }
|
||||
}
|
||||
|
||||
assert child_tf.prev_sibling == none;
|
||||
assert child_tf.next_sibling == none;
|
||||
|
||||
node.with_tree_fields { |node_tf|
|
||||
alt node_tf.last_child {
|
||||
none {
|
||||
node_tf.first_child = some(child);
|
||||
}
|
||||
|
||||
some(lc) {
|
||||
lc.with_tree_fields { |lc_tf|
|
||||
assert lc_tf.next_sibling == none;
|
||||
lc_tf.next_sibling = some(child);
|
||||
}
|
||||
child_tf.prev_sibling = some(lc);
|
||||
}
|
||||
}
|
||||
|
||||
node_tf.last_child = some(child);
|
||||
}
|
||||
}
|
||||
|
||||
assert child_tf.prev_sibling == none;
|
||||
assert child_tf.next_sibling == none;
|
||||
|
||||
let node_tf = node.tree_fields();
|
||||
alt node_tf.last_child {
|
||||
none {
|
||||
node_tf.first_child = some(child);
|
||||
}
|
||||
|
||||
some(lc) {
|
||||
let lc_tf = lc.tree_fields();
|
||||
assert lc_tf.next_sibling == none;
|
||||
lc_tf.next_sibling = some(child);
|
||||
child_tf.prev_sibling = some(lc);
|
||||
}
|
||||
}
|
||||
|
||||
node_tf.last_child = some(child);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -70,7 +73,9 @@ mod test {
|
|||
};
|
||||
|
||||
impl of tree for dummy {
|
||||
fn tree_fields() -> fields<dummy> { self.fields }
|
||||
fn with_tree_fields<R>(f: fn(fields<dummy>) -> R) -> R {
|
||||
f(self.fields)
|
||||
}
|
||||
}
|
||||
|
||||
fn new_dummy(v: uint) -> dummy {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue