34 Active/inactive DGS nodes
There used to be a concept of active or inactive agents. Instead there's no longer such a thing as an "agent" and there's no concept of nodes being "inactive".
There used to be support for start/stop handlers for when a node transitioned between inactive and active states:
$indep int32 x
start
{
// x has become reachable from an active agent
}
stop
{
// x is no longer reachable from an active agent
};
There were significant restrictions on what could be done in these start/stop handlers: It was not allowable to:
- read/write any independent or dependent variable in the same CSpace
- change the active/inactive state of any agent in the same CSpace
- call operator() on an agent
These start/stop handlers can no longer be defined.
Sometimes dependents are read for the first time outside the context of a "display agent", such as in mouse or key event handlers which are nominally able to mutate state and therefore won't be running in the context of the calc function of some agent. So with a design using a concept of active agents these calculations aren't cached.
When to delete edges
Edges used to be deleted according to reachability from an active agent. Instead an edge is deleted if and only if the outnode becomes hard-dirty. A node can be deleted if it is hard-dirty and it has no outnodes. The eviction system must ensure a node has no in-nodes or out-nodes before it can be evicted.
I've developed an algorithm based on this idea and tested it in an "emulation" for 6 hours. I was planning on having it also automatically calculate which nodes are reachable from active agents (storing this as a flag in each node, allowing for the start/stop callbacks). But this looks complicated and it's unclear whether it's even useful. As we have discussed, there's very little you can do in a start/stop handler. Note as well that $cache functions don't have a concept of supporting such callbacks.
RULE 1: $indep nodes behave as though they are always clean and do not have any in-nodes
(they are always clean because their Get() function is always correct)
RULE 2: Hard-dirty nodes never have any in-nodes.
Implementation note: In ReadBarrier() there is no need to save away the previous set of
innodes when the node is hard-dirty because there won't be any:
void ReadBarrier() const
{
...
if (inputChanged)
{
assert(innodes.empty()); // Follows by RULE 2
...
}
...
}
RULE 3: The outnodes of a (soft or hard) dirty node are always soft-dirty
In general an edge n1 ---> n2 is detached if and only if n2 is marked as hard-dirty.
When to delete nodes
It might be thought it's generally appropriate to allow a node to be deleted when it is hard-dirty, but we do not make that assumption.
In such a case there are no in-nodes. However there may still be some out-nodes. The out-nodes are necessarily soft-dirty. The hard-dirty node is useful to the extent that it is recording the last value of the input on which its out-nodes were based.
Of course if a node is hard-dirty and has no out-nodes then it serves no useful purpose at all and should be deleted if that will save memory.
We turn this observation into a rule:
RULE 4: A node is able to be deleted (as far as the DGS is concerned) if and only if it is hard-dirty and there are no out-nodes
Issues
- The potential for eviction of calculated values has numerous repercussions for applications. cxView allows for the fact that views can be evicted from the view hierarchy by the evictor. There's a way to try to avoid eviction of objects having changes to transient state that would be lost if the object was evicted. But this doesn't account for where the inputs to a view are evicted (which inevitably marks the view as dirty). Views are calculated objects and most generally there's always a chance that they can be marked as dirty. For that reason I provide a mechanism to allow evicted views to receive an OnDestroyView() notification and this allows a view being destroyed to take appropriate action if it has the keyboard focus or captured the mouse etc.
- If an object is registered using RegisterNonGcObject (because it's deleted explicitly - perhaps it's an object managed by wxWidgets) then you have to be careful if it has models/$indeps/$deps/$cache functions. Generally speaking the DGS can store pointers to the object so you can expect a crash if you delete it. Currently only $indeps / $deps provide a way to force eviction to make it possible to delete the object when you want to.