Skip to content

Commit

Permalink
We don't use deregister anymore. Also update memory management comments.
Browse files Browse the repository at this point in the history
  • Loading branch information
Charlie Savage committed Feb 8, 2017
1 parent 2beb91d commit c41f577
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 38 deletions.
60 changes: 23 additions & 37 deletions ext/libxml/ruby_xml_node.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,31 @@ VALUE cXMLNode;

/* Memory management:
*
* The bindings create a one-to-one mapping between libxml nodes
* and Ruby nodes. If a libxml node is wrapped, the mapping is stored in the
* private_pointers hashtable.
*
* When a libxml document or top level node is freed, it will free
* all its children. Thus Ruby is responsible for:
*
* * Using the mark function to keep alive any documents Ruby is
* referencing via the document or child nodes.
* * Using the mark function to keep alive any top level, free
* standing nodes Ruby is referencing via the node or its children.
*
* In general use, this will cause Ruby nodes to be freed before
* a libxml document. When a Ruby node is freed, the hashtable entry is
* removed.
*
* In the sweep phase in Ruby 1.9.*, the document tends to be
* freed before the nodes. To support this, the bindings register
* a callback function with libxml that is called each time a node
* is freed. In that case, the data_ptr is set to null, so the bindings
* can recognize the situation.
* The bindings create a one-to-one mapping between ruby objects and
* libxml documents and libxml parent nodes (ie, nodes that do not
* have a parent and do not belong to a document). In these cases,
* the bindings manage the memory. They do this by installing a free
* function and storing a back pointer to the Ruby object from the xmlnode
* using the _private member on libxml structures. When the Ruby object
* goes out of scope, the underlying libxml structure is freed. Libxml
* itself then frees all child node (recursively).
*
* For all other nodes (the vast majority), the bindings create temporary
* Ruby objects the get freed once they go out of scope. Thus there can be
* more than one ruby object pointing to the same xml node. To mostly hide
* this from programmers on the ruby side, the #eql? and #== methods are
* overriden to check if two ruby objects wrap the same xmlnode. If they do,
* then the methods return true. During the mark phase, each of these temporary
* objects marks its owning document, thereby keeping the Ruby document object
* alive and thus the xmldoc tree.
*
* In the sweep phase of the garbage collector, or when a program ends,
* there is no order to how Ruby objects are freed. In fact, the ruby document
* object is almost always freed before any ruby objects that wrap child nodes.
* However, this is ok because those ruby objects do not have a free function
* and are no longer in scope (since if they were the document would not be freed).
*/

static void rxml_node_deregisterNode(xmlNodePtr xnode)
{
/* Has the document or node been wrapped and exposed to Ruby? */
if (xnode->_private == NULL)
return;

VALUE node = (VALUE)xnode->_private;
RDATA(node)->data = NULL;
}

static void rxml_node_free(xmlNodePtr xnode)
{
/* The ruby object wrapping the xml object no longer exists and this
Expand Down Expand Up @@ -1336,12 +1328,6 @@ static VALUE rxml_node_copy(VALUE self, VALUE deep)

void rxml_init_node(void)
{
/* Register callback for main thread */
xmlDeregisterNodeDefault(rxml_node_deregisterNode);

/* Register callback for all other threads */
xmlThrDefDeregisterNodeDefault(rxml_node_deregisterNode);

cXMLNode = rb_define_class_under(mXML, "Node", rb_cObject);

rb_define_const(cXMLNode, "SPACE_DEFAULT", INT2NUM(0));
Expand Down
3 changes: 2 additions & 1 deletion ext/libxml/ruby_xml_reader.c
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,8 @@ static VALUE rxml_reader_lookup_namespace(VALUE self, VALUE prefix)
* reader.expand -> node
*
* Returns the current node and its full subtree. Note the returned node
* is valid ONLY until the next read call.
* is valid ONLY until the next read call. If you would like to preserve
* the node, or search it via xpath, call reader.doc first.
*/
static VALUE rxml_reader_expand(VALUE self)
{
Expand Down

0 comments on commit c41f577

Please sign in to comment.