forked from KolibriOS/kolibrios
143 lines
5.2 KiB
Plaintext
143 lines
5.2 KiB
Plaintext
|
Reference counting
|
||
|
------------------
|
||
|
|
||
|
Overview
|
||
|
--------
|
||
|
|
||
|
DOM Nodes are reference counted, so as to ensure they are only destroyed
|
||
|
when nothing is using them. Each node has a reference count member
|
||
|
variable, which is a count of external references upon the node. Links
|
||
|
between nodes in the DOM tree (internal references) are not counted, as
|
||
|
they are implicitly available by consulting the relevant pointers.
|
||
|
|
||
|
Destruction semantics
|
||
|
---------------------
|
||
|
|
||
|
A simplistic DOM tree might look like the following:
|
||
|
|
||
|
Node1
|
||
|
| ^
|
||
|
| |
|
||
|
+-------------|-+---------------+
|
||
|
+-|-------------+-|-------------+ |
|
||
|
| | | | | |
|
||
|
v | v | v |
|
||
|
Node2<--------->Node3<--------->Node4
|
||
|
| ^
|
||
|
| |
|
||
|
+-----|-+-------+
|
||
|
+-|-----+-------+ |
|
||
|
| | | |
|
||
|
v | v |
|
||
|
Node5<--------->Node6
|
||
|
|
||
|
Thus, each node possesses the following links:
|
||
|
|
||
|
a) A link to its parent
|
||
|
b) A link to each of its children
|
||
|
c) A link to the sibling immediately prior to it
|
||
|
d) A link to the sibling immediately after it
|
||
|
|
||
|
None of these links are reference counted, as the reference can be
|
||
|
determined implicitly from the pointer value (i.e. a non-NULL pointer
|
||
|
implies a reference).
|
||
|
|
||
|
A node becomes eligible for destruction when:
|
||
|
|
||
|
a) its reference count variable equals 0
|
||
|
b) its parent node pointer equals NULL
|
||
|
|
||
|
I.E. There exist no external references upon the node and the node has
|
||
|
been detached from the tree.
|
||
|
|
||
|
Note that the presence of children or siblings attached to a node has no
|
||
|
impact upon its eligibility for destruction, as these links are "weak".
|
||
|
|
||
|
Destruction process
|
||
|
-------------------
|
||
|
|
||
|
The node destruction process proceeds as follows:
|
||
|
|
||
|
1) Any children of the node are detached from it and an attempt is
|
||
|
made to destroy them.
|
||
|
2) The node is destroyed.
|
||
|
|
||
|
If, when attempting to destroy children of the node, a child is found
|
||
|
to have a non-zero reference count (i.e. an external reference is
|
||
|
being held upon the child), the child (and its children) is not
|
||
|
destroyed. The child is added to the list of nodes pending deletion
|
||
|
and will be destroyed once its reference count reaches zero.
|
||
|
|
||
|
Example
|
||
|
-------
|
||
|
|
||
|
This example uses the DOM tree depicted above, and a system state as
|
||
|
follows:
|
||
|
|
||
|
a) A NodeList collection references Node6. There are no other active
|
||
|
collections. The NodeList has a reference count of 1.
|
||
|
b) Node2 (and its subtree) has been removed from the document and
|
||
|
is referenced solely by the client code that caused it to be
|
||
|
removed from the document.
|
||
|
|
||
|
The client code unreferences Node2, thus reducing its reference count to
|
||
|
zero. It is now eligible for destruction. Destruction occurs as follows:
|
||
|
|
||
|
1) Node5 is detached from Node2 and an attempt is made to destroy it.
|
||
|
a) Node5 has no children and has a reference count of zero, so it
|
||
|
is destroyed.
|
||
|
2) Node6 is detached from Node2 and an attempt is made to destroy it.
|
||
|
a) Node6's reference count is non-zero, so it is added to the list
|
||
|
of nodes pending deletion.
|
||
|
3) Node2 has no further children, so it is destroyed.
|
||
|
|
||
|
The client code unreferences the NodeList:
|
||
|
|
||
|
1) The NodeList unreferences the node it's attached to (Node6).
|
||
|
Node6's reference count is now zero, so it is eligible for
|
||
|
destruction.
|
||
|
a) Node6 has no children, so it is destroyed (and removed from the
|
||
|
list of nodes pending deletion).
|
||
|
2) The NodeList is destroyed.
|
||
|
|
||
|
Destruction of Documents
|
||
|
------------------------
|
||
|
|
||
|
Assumptions:
|
||
|
|
||
|
1) Nodes within a document do not hold explicit references upon it.
|
||
|
2) Container data structures which address nodes in a document hold
|
||
|
an explicit reference upon the document.
|
||
|
[FIXME: and upon the root node of the subtree they address -- this
|
||
|
implies that the explicit reference is unnecessary, as the
|
||
|
addressed node will be added to the list of nodes pending
|
||
|
deletion]
|
||
|
3) A document has no parent (i.e. the parent pointer is always NULL).
|
||
|
4) A given node may be located in either the document tree or the
|
||
|
list of nodes pending deletion. It may not be located in both
|
||
|
data structures simultaneously.
|
||
|
5) Nodes in the list of nodes pending deletion are in use by the
|
||
|
client.
|
||
|
|
||
|
The above assumptions imply the following:
|
||
|
|
||
|
+ If a document has a non-zero reference count, it is in use by
|
||
|
the client. (1,2)
|
||
|
+ If the document's reference count reaches zero, it is no longer
|
||
|
in use and is eligible for deletion. (1,2,3)
|
||
|
+ The document destructor must attempt to forcibly delete the
|
||
|
contents of the document tree as the nodes do not hold a reference
|
||
|
upon the document. (1)
|
||
|
+ On destruction of a node, it must be removed from the list of nodes
|
||
|
pending deletion. (4)
|
||
|
+ The document may not be destroyed if the list of nodes pending
|
||
|
deletion is non-empty after the destructor has attempted to
|
||
|
destroy the document tree. (4,5)
|
||
|
|
||
|
Therefore, document destruction proceeds as follows:
|
||
|
|
||
|
1) Forcibly destroy the document tree.
|
||
|
2) If the list of nodes pending deletion is non-empty, stop.
|
||
|
3) The list of nodes pending deletion is empty, so destroy the
|
||
|
document.
|