|
|
#pike __REAL_VERSION__ |
|
inherit .Scheduler; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Consumer |
{ |
inherit ::this_program; |
|
|
Consumer parent; |
|
|
array(Consumer) children = ({}); |
|
|
float children_weight = 0.0; |
|
|
|
|
int(0..) depth; |
|
|
|
|
|
void update_quanta() |
{ |
if (!parent) { |
quanta = 1.0; |
return; |
} |
float old_quanta = quanta; |
|
quanta = parent->children_weight * parent->quanta / weight; |
children->update_quanta(); |
pri += (quanta - old_quanta)/2.0; |
adjust(); |
} |
|
void `weight=(float|int new_weight) |
{ |
if (new_weight == weight_) return; |
parent->children_weight += new_weight - weight_; |
weight_ = new_weight; |
parent->children->update_quanta(); |
} |
|
float|int `weight() |
{ |
return weight_; |
} |
|
|
protected void create(int|float weight, mixed v, Consumer|void parent) |
{ |
weight_ = weight; |
value = v; |
parent = this_program::parent = parent || root; |
if (parent) { |
parent->children += ({ this }); |
parent->children_weight += weight; |
depth = parent->depth + 1; |
pri = parent->pri - parent->quanta/2.0; |
offset = parent->offset; |
|
parent->children->update_quanta(); |
} else { |
|
quanta = 1.0; |
pri = 0.5; |
} |
} |
|
void set_depth(int new_depth) |
{ |
if (depth == new_depth) return; |
depth = new_depth; |
foreach(children, Consumer c) { |
c->set_depth(depth + 1); |
} |
adjust(); |
} |
|
void consume_down(float delta) |
{ |
|
foreach(children, Consumer c) { |
c->consume_down(delta * c->weight / children_weight); |
} |
::consume(delta); |
} |
|
void consume_up(float delta) |
{ |
::consume(delta); |
if (parent) { |
parent->consume_up(delta); |
} |
} |
|
void consume(float delta) |
{ |
consume_down(delta); |
if (parent) { |
|
|
parent->consume_up(delta); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void set_parent(Consumer new_parent, int|float weight) |
{ |
if (!new_parent) new_parent = root; |
|
if (new_parent == parent) { |
this_program::weight = weight; |
return; |
} |
|
|
|
Consumer npp = new_parent; |
while (npp) { |
if (npp == this) { |
|
|
new_parent->set_parent(parent, weight_); |
break; |
} |
npp = npp->parent; |
} |
|
|
parent->children_weight -= weight_; |
parent->children -= ({ this }); |
parent->children->update_quanta(); |
|
|
parent = new_parent; |
weight_ = weight; |
parent->children += ({ this }); |
parent->children_weight += weight; |
|
|
set_depth(parent->depth + 1); |
|
parent->children->update_quanta(); |
|
|
|
|
float parent_pri = parent->pri + parent->offset - offset; |
if (parent_pri > pri) { |
|
|
consume_down((parent_pri - pri) / quanta); |
} |
} |
|
|
|
|
|
|
void reparent_siblings() |
{ |
|
if (sizeof(parent->children) == 1) return; |
|
float weight_factor = children_weight/weight; |
foreach(parent->children, Consumer c) { |
if (c == this) continue; |
c->set_parent(this, c->weight * weight_factor); |
} |
} |
|
|
|
|
|
|
|
|
void detach() |
{ |
if (state & STATE_ACTIVE) { |
remove(this); |
} |
Consumer parent = this_program::parent; |
this_program::parent = UNDEFINED; |
|
parent->children -= ({ this }); |
|
if (sizeof(children)) { |
|
|
|
parent->children += children; |
foreach(children, Consumer c) { |
c->parent = parent; |
c->weight *= weight/children_weight; |
parent->children_weight += c->weight; |
} |
children->set_depth(parent->depth + 1); |
children = ({}); |
} |
parent->children_weight -= weight; |
parent->children->update_quanta(); |
} |
|
protected void _destruct() |
{ |
if (parent) { |
detach(); |
} else { |
|
foreach(children, Consumer c) { |
c->parent = UNDEFINED; |
c->set_depth(depth); |
} |
} |
} |
|
protected int `<(object o) |
{ |
if (pri == o->pri) { |
|
return depth < o->depth; |
} |
return pri<o->pri; |
} |
protected int `>(object o) |
{ |
if (pri == o->pri) { |
|
return depth > o->depth; |
} |
return pri>o->pri; |
} |
} |
|
|
|
variant Consumer add(int|float weight, mixed val, Consumer parent) |
{ |
return add(Consumer(weight, val, parent)); |
} |
|
|
|
|
|
|
|
Consumer root = Consumer(1.0, "root"); |
|
protected string _sprintf(int c) |
{ |
if (c != 'O') return UNDEFINED; |
Stdio.Buffer buf = Stdio.Buffer("ADT.TreeScheduler(\n"); |
.Stack todo = .Stack(); |
todo->push(0); |
todo->push(root); |
Consumer child; |
while (child = todo->pop()) { |
buf->sprintf(" %*s%O\n", child->depth * 2, "", child); |
foreach(reverse(child->children), Consumer cc) { |
todo->push(cc); |
} |
} |
buf->add(")"); |
return (string)buf; |
} |
|
protected void _destruct() |
{ |
|
.Stack todo = .Stack(); |
todo->push(0); |
todo->push(root); |
root = UNDEFINED; |
Consumer child; |
while (child = todo->pop()) { |
foreach(child->children, Consumer cc) { |
todo->push(cc); |
} |
child->children = ({}); |
child->parent = UNDEFINED; |
destruct(child); |
} |
} |
|
|