sparklemotion/nokogiri — pull request #1002 by JakeAustwick (2013-11-14 02:50:39+00:00)
Title: Clean all occurrences of _private on GC when libxml-ruby is loaded.

libxml-ruby uses the global libxml2 callback xmlDeregisterNodeDefault.

This callback looks in the _private field for every deleted libxml2
node. If the _private field is set, it is treated as a Ruby VALUE ptr.
The callback NULLs the Ruby object's data pointer and mark & free fn
pointers.

When Nokogiri wraps a libxml2 document it puts a nokogiriTuple ptr
in the _private field. We can't let the libxml-ruby callback be
invoked on a libxml2 document before NULLing the document _private
field.

When Nokogiri wraps other libxml2 nodes it puts VALUE ptrs in the
_private field. This makes the libxml-ruby callback generally safe for
these node types.

There is a caveat - it is unsafe to access VALUE ptrs during GC sweep.

This commit tests whether the xmlDeregisterNodeDefault callback is set
during document cleanup. If set, we traverse the document and clean up
all _private fields.

The previous solution was to unset xmlDeregisterNodeDefault callback.
However, this doesn't work in multithreaded environments.