After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 634123 - Libxml++ breaks libxml2 usage in the same process
Libxml++ breaks libxml2 usage in the same process
Status: RESOLVED FIXED
Product: libxml++
Classification: Bindings
Component: General
2.32.x
Other Linux
: Normal critical
: ---
Assigned To: Christophe de Vienne
Christophe de Vienne
Depends on:
Blocks:
 
 
Reported: 2010-11-05 21:41 UTC by a.pignotti
Modified: 2010-11-18 09:15 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Patch [1/4] (2.68 KB, patch)
2010-11-05 21:43 UTC, a.pignotti
none Details | Review
Patch [2/4] (11.74 KB, patch)
2010-11-05 21:43 UTC, a.pignotti
none Details | Review
Patch [3/4] (2.83 KB, patch)
2010-11-05 21:43 UTC, a.pignotti
none Details | Review
Patch [4/4] (1.95 KB, patch)
2010-11-05 21:44 UTC, a.pignotti
none Details | Review
combined.patch (16.11 KB, patch)
2010-11-08 08:36 UTC, Murray Cumming
needs-work Details | Review
Update combined patch (15.63 KB, patch)
2010-11-08 15:31 UTC, a.pignotti
none Details | Review
0001-Make-libxml-compatible-with-libxml2-usage3.patch (15.72 KB, patch)
2010-11-14 13:12 UTC, Murray Cumming
none Details | Review

Description a.pignotti 2010-11-05 21:41:23 UTC
Libxml++ on library load sets specialized handlers to create and destroy C++ wrappers for each libxml node. This breaks the usage of vanilla libxml2 in the same process. Moreover, on library unloading xmlCleanupParser is called and this causes bad crashes if libxml++ is used in a plugin for a program which itself uses libxml2.

For reference http://mail.gnome.org/archives/libxmlplusplus-list/2010-November/msg00000.html

I'm attaching a patchset for the first issue (Creation and destruction of C++ wrappers)
Comment 1 a.pignotti 2010-11-05 21:43:04 UTC
Created attachment 173918 [details] [review]
Patch [1/4]
Comment 2 a.pignotti 2010-11-05 21:43:30 UTC
Created attachment 173919 [details] [review]
Patch [2/4]
Comment 3 a.pignotti 2010-11-05 21:43:50 UTC
Created attachment 173920 [details] [review]
Patch [3/4]
Comment 4 a.pignotti 2010-11-05 21:44:14 UTC
Created attachment 173921 [details] [review]
Patch [4/4]
Comment 5 Murray Cumming 2010-11-05 22:32:37 UTC
> Moreover, on library unloading xmlCleanupParser is called and
> this causes bad crashes if libxml++ is used in a plugin for a program which
> itself uses libxml2.

Does this only happen during explicit unloading via dlclose()? Quite apart from everything else, that seems like something that we should move to an explicit deinit() method that should be called only by apps at the end of their main().
Comment 6 a.pignotti 2010-11-05 22:43:39 UTC
xmlCleanupParser is called inside the destructor of a static C++ object so it should be called when the lib is unloaded in any case, both explicity or implicity (for example when unloading the last library that uses it)

To avoid changing the behavior of the lib for existing apps i suggest adding a settable flag. The default behavior would be calling xmlCleanupParser on unload. Setting the flag should avoid that.
Comment 7 Murray Cumming 2010-11-06 08:26:48 UTC
Actually, calling xmlCleanupParser is so bad, and useful only for valgrind checks, that I am happy to change that behaviour.
Comment 8 a.pignotti 2010-11-06 18:46:19 UTC
Murray, could you please review the patches? This issue is pretty much a blocker for the release of the new version of lightspark.
Comment 9 Murray Cumming 2010-11-07 22:18:12 UTC
It's the weekend. Have a little patience. I can't believe it's that urgent.

I first want to solve (separately and simply) the main problem that you found - use of xmlCleanupParser. Then I'll look at the rest, hopefully tomorrow.
Comment 10 Murray Cumming 2010-11-08 08:36:13 UTC
Created attachment 174048 [details] [review]
combined.patch

I've removed the xmlCleanupParser() call in master.

And here is the combined patch, against the current master. The separation of the commits doesn't seem particularly useful.
Comment 11 Murray Cumming 2010-11-08 08:49:39 UTC
Review of attachment 174048 [details] [review]:

::: libxml++/document.h
@@ -173,0 +173,7 @@
+  ///Construct the right C++ instances for a given element
+  static void create_wrapper(_xmlNode* node);
+  ///Recursively destroy the created C++ instances
... 4 more ...

You seem to use casts to make sure that the correct free_wrappers() method is called. Wouldn't it be simpler to just do the check inside the function, assuming that they share a "base" struct that makes that possible?

::: libxml++/nodes/node.cc
@@ -33,3 +34,3 @@
 Element* Node::get_parent()
 {
-  return cobj()->parent && cobj()->parent->type == XML_ELEMENT_NODE ? 
+  if(cobj()->parent && cobj()->parent->type == XML_ELEMENT_NODE)

I'd prefer simple checks at the start instead of nesting:

if(!thing)
  return 0;

And please don't use NULL in C++ code. Particularly when !thing is simpler.

@@ -72,3 +91,3 @@
-        if(name.empty() || name == (const char*)child->name)
-          children.push_back(reinterpret_cast<Node*>(child->_private));
-      }
+        if(child->_private==NULL)
+          Document::create_wrapper(child);
+        children.push_back(reinterpret_cast<Node*>(child->_private));

You have used reinterpret_cast<> here though we use static_cast<> elsewhere, I think everywhere, when casting from void* to a C++ type. Please keep it consistent.

@@ -103,2 +113,5 @@
   _xmlNode* node = xmlAddChild(impl_, child);
   if(node)
+  {
+    if(node->_private==NULL)
+      Document::create_wrapper(node);

If we do the if() check inside create_wrapper() then we don't need to do it repeatedly every time we call it.

::: libxml++/validators/dtdvalidator.cc
@@ -103,3 +108,3 @@
   if(dtd_)
   {
-    xmlFreeDtd(dtd_->cobj());
+    //Make a local copy as the wrapper is destroyed first

This comment should make it clear that the "this" instance itself is destroyed at this point. It's effectively a "delete this", so we need to be very careful not to do anything after this point.

Actually, I am concerned: The release_underlying() methods are called from destructors and also from regular methods, which makes me worry about two things:
1. When called from a destructor, isn't this effectively going to call delete on something that has just had delete called on it?
2. When called from a regular method isn't this just going to break that instance by deleting it?

These might not be new problems, of course.
Comment 12 a.pignotti 2010-11-08 15:25:34 UTC
(In reply to comment #11)
> ::: libxml++/validators/dtdvalidator.cc
> @@ -103,3 +108,3 @@
>    if(dtd_)
>    {
> -    xmlFreeDtd(dtd_->cobj());
> +    //Make a local copy as the wrapper is destroyed first
> 
> This comment should make it clear that the "this" instance itself is destroyed
> at this point. It's effectively a "delete this", so we need to be very careful
> not to do anything after this point.
> 
> Actually, I am concerned: The release_underlying() methods are called from
> destructors and also from regular methods, which makes me worry about two
> things:
> 1. When called from a destructor, isn't this effectively going to call delete
> on something that has just had delete called on it?
> 2. When called from a regular method isn't this just going to break that
> instance by deleting it?
> 
> These might not be new problems, of course.
Humm.. it looks to me like the 'this' is not being destroyed. It's the Dtd object contained in DtdValidator that will be destroyed.
Comment 13 a.pignotti 2010-11-08 15:31:47 UTC
Created attachment 174062 [details] [review]
Update combined patch
Comment 14 a.pignotti 2010-11-08 15:32:18 UTC
I've fixed the patch as suggested
Comment 15 Murray Cumming 2010-11-08 15:34:02 UTC
(In reply to comment #12)
> Humm.. it looks to me like the 'this' is not being destroyed. It's the Dtd
> object contained in DtdValidator that will be destroyed.

Ah, OK then.
Comment 16 Murray Cumming 2010-11-08 15:42:55 UTC
It looks generally good. I'm asking on the mailing list too, in case someone knows of a problem with this approach. It's a large change so I want to be cautious.

I do guess that Document::create_wrapper() should be in Node instead of Document. And create_cpp_instance() might be more obvious. But I can do that myself later.
Comment 17 a.pignotti 2010-11-10 15:18:26 UTC
There has been any suggestion from the ML?
Comment 18 Murray Cumming 2010-11-14 13:12:54 UTC
Created attachment 174431 [details] [review]
0001-Make-libxml-compatible-with-libxml2-usage3.patch

The patch with compilation errors corrected. I am now testing this.
Comment 19 Murray Cumming 2010-11-14 15:56:22 UTC
I've pushed that an an additional small change. I'll keep testing that. Could you please confirm that the code in git master really fixes your problems, please.
Comment 20 a.pignotti 2010-11-14 17:10:43 UTC
I'm now testing git master with no issue. Do you think it's possible to release a new version to push this fix to distros?
Comment 21 Murray Cumming 2010-11-18 09:15:39 UTC
This is now in the unstable libxml++ 2.33.1 tarball release. Thanks.

People should watch our for problems with this.