GNOME Bugzilla – Bug 374940
GType system should be completely unloadable
Last modified: 2011-02-18 16:14:05 UTC
Suppose you have the following architecture: 1) A main (glib-based) application, which uses 2) A pure C library (no glib) which supports plugins, such as 3) A plugin which uses libgsf Note that the C library is NOT linked against glib and does not use Gmodule (say, it uses libltdl from libtool instead) and does not have any understanding of GTypes. Also, the C library encapsulates its plugins. Furthermore, the main application does NOT know about libgsf (does not need it, does not link against it, does not want to be aware of it). Then the following can happen: a) main program initializes type syste calls C library b) C library loads plugin; plugin is dynamically linked against libgsf; libgsf's glib dependencies are resolved against the glib of the main program, sharing glib's type system c) libgsf registers libgsf specific types with glib d) plugin uses libgsf; everything is fine e) plugin is unloaded, libgsf is removed from memory (no longer needed), but, and this is the BUG, its types are still registered with gobject (and this is no easy fix; while we can have a destructor (fini-function) in libgsf, there seems to be no good mechanism for unregistering types from libgobject) f) later, plugin is needed again, loaded again, libgsf is again resolved against existing glib g) libgsf tries to register its types with glib, but since types of those names (!) already exist, gobject refuses (!) h) libgsf object creation fails, CRASH. Workaround: link everything against libgsf, break modularity, waste memory. Fixes: a1) allow deregistration of statically registered types in gobject a2) have proper libgsf cleanup that does a) b) have libgsf use dynamic registration (much harder to get right, but maybe easier to do than a1+a2) c) have proper class loader mechanism in libgobject This bug probably exists in tons of glib-based libraries. A testcase (with full autotools build) is available at https://gnunet.org/svn/bugGSF/
The abiword people have been discussing this problem. CC-ing Dom.
I ran into this problem with librsvg. There's not much (if anything) that libgsf can do here - this is a fundamental brokenness in gobject's type registration system that affect all gobject subclasses. See also bug 357406 and bug 362217 and http://mail.gnome.org/archives/desktop-devel-list/2006-November/msg00009.html
Not brokenness, but a well-known and documented limitation. a1) allow deregistration of statically registered types in gobject Thats nonsensical. The very point of _static_ registration is that it is, well, static b) have libgsf use dynamic registration (much harder to get right, but maybe easier to do than a1+a2) thats the way to go if you need dynamic registration, yes c) have proper class loader mechanism in libgobject not totally clear what that means, apart from using dynamic registration everywhere.
The problem here is very clear and can happen with /any/ library defining GTypes. I don't know what the proper solution is, but atexit() may be useful, since on glibc, it can be used to schedule a function to be called when a shared library is being unloaded.
I would strongly recommend against relying on atexit(). Its exact semantics when called from shared objects varies wildly from platform to platform.
Matthias Clasen wrote: > Not brokenness, but a well-known and documented limitation. Well, if a limitation is so severe that it means that certain things, which people want to do for good reasons, are impossible, I'd call it broken. > a1) allow deregistration of statically registered types in gobject > > Thats nonsensical. The very point of _static_ registration is that > it is, well, static I understand that. However, dynamic registration is broken (see below), and the way to fix it maybe to get rid of static registration and say that all registration is dynamic in some sense (as in, can be un-done), keeping the broken dynamic registration bit only for the case where it actually works. > b) have libgsf use dynamic registration (much harder to get > right, but maybe easier to do than a1+a2) > > thats the way to go if you need dynamic registration, yes Here is the issue with dynamic registration that I might not have been clear enough about: You can only use dynamic registration if you already HAVE a type registered. So to bootstrap the entire thing, you need static registration (to register the types that are required for dynamic registration). Now, if you are writing a plugin (where *everything* must be unloaded) and you cannot rely on anything to pre-exist (no static types available outside of the plugin), you cannot use the existing dynamic registration mechanism. I hope this helps.
> Well, if a limitation is so severe that it means that certain things, which > people want to do for good reasons, are impossible, I'd call it broken. "limitation" kind of implies "certain things are not possible". Otherwise it would not be a limitation... > You can only use dynamic registration if you already HAVE a type > registered. Not entirely clear what you mean here ? Are you talking about basic gobject types that you need to derive your dynamic types from ?
I'm talking about how to get a "GTypePlugin" instance in the first place.
ping? Any more feedback is appreciated. BTW, I was thinking about a new alternative solution besides a,b,c: d) simply ignore re-registration of already-registered types, to make gobject not refuse at (g), so as the crash in (h) doesn't happen. Is that feasible? Thanks.
Upgrade the latest affected glib version
IMHO, this is a bug in the module linking against libgsf. Glib has the limitation Matthias pointed out for some reason IIRC. So, if your plugin needs to link against libgsf, you should make sure, that your plugin isn't unloadable again. In the GTypePlugin use-case, one would call g_type_plugin_use() when registering the types. If you're using a different approach, you'll have to check how to do this with your plugin system. IMHO, this is NOTABUG (because it is a well-known and well-documented limitation). If people really consider that this should be fixed, this should not be critical, but an enhancement request only. Comments?
Given Sven's comment and the persistence of the issue, I assume that the Glib developers have no interest in fixing this "limitation". As a result, I have added out-of-process (fork, exec) execution support for plugins to GNU libextractor. So now I *can* unload plugins that use Glib code (by killing processes). For most plugins, this is optional (since it costs performance and should not be needed), but the Glib-based plugins are going to have the distinction of being out-of-process only until this issue is resolved. I assume you think this is a good thing. New Revision: 9987 Modified: Extractor/src/plugins/ole2_extractor.c Extractor/src/plugins/thumbnailgtk_extractor.c Log: Gnome thinks unloading plugins is an unnecessary feature, so we only run Gnome code out-of-process from now on Modified: Extractor/src/plugins/ole2_extractor.c =================================================================== --- Extractor/src/plugins/ole2_extractor.c 2010-01-13 13:46:37 UTC (rev 9986) +++ Extractor/src/plugins/ole2_extractor.c 2010-01-13 13:52:34 UTC (rev 9987) @@ -475,6 +475,21 @@ } + +const char * +EXTRACTOR_ole2_options () +{ + /* + Since the Gnome developers think that being unable to + unload plugins is an 'acceptable' limitation, we + require out-of-process execution for plugins depending + on libgsf and other glib-based plugins. + See also https://bugzilla.gnome.org/show_bug.cgi?id=374940 + */ + return "oop-only"; +} + + int EXTRACTOR_ole2_extract (const char *data, size_t size, Modified: Extractor/src/plugins/thumbnailgtk_extractor.c =================================================================== --- Extractor/src/plugins/thumbnailgtk_extractor.c 2010-01-13 13:46:37 UTC (rev 9986) +++ Extractor/src/plugins/thumbnailgtk_extractor.c 2010-01-13 13:52:34 UTC (rev 9987) @@ -41,6 +41,20 @@ } +const char * +EXTRACTOR_thumbnailgtk_options () +{ + /* + Since the Gnome developers think that being unable to + unload plugins is an 'acceptable' limitation, we + require out-of-process execution for plugins depending + on libgsf and other glib-based plugins. + See also https://bugzilla.gnome.org/show_bug.cgi?id=374940 + */ + return "oop-only"; +} + +
(In reply to comment #12) > Gnome thinks unloading plugins is an unnecessary feature Ah, so it's Mr. Gnome's fault, that evil person! (/me tired of strange generalizations by people that disagree with other people)
Renaimg the bug to actually reflect the wishes of the bug reporter for future references.
(In reply to comment #0) > e) plugin is unloaded, libgsf is removed from memory > (no longer needed), but, and this is the BUG, > its types are still registered with gobject > (and this is no easy fix; while we can have a > destructor (fini-function) in libgsf, there seems > to be no good mechanism for unregistering types > from libgobject) Registered GType IDs cannot be unregistered by design. There is no way to work around this intentional design decision, short of replacing the GObject type system (which is considerably unliekly to happen). For you particular case, you could look into making all libgsf type fully dynamic and unloadable type implementations. This *is* supported by the type system. The entirety of GType/GObject/GLib cannot be made unloadable however, so I'm closing this bug report (indiferent about wether people want to call this WONTFIX/NOTABUG/INVALID or whatever).
I agree that the way to fix this would be to replace the GObject type system. That was always what I was thinking was needed. libgsf cannot by itself be made unloadable without using at least one subtype of GTypePlugin that is static and hence the overall library would remain un-unloadable, so this is not a solution. Clearly the overall point is that from a program that does not link itself against GLib calling 'dlopen'on *any* library that links against GLib implies that one can NEVER SAFELY call 'dlclose' on that library later. The only solution is to fork() and use IPC if unloading is required or desired.