Re: Glib::Object::Subclass, embedding and multiple interpreters




On Mar 30, 2005, at 1:19 PM, Eduardo M KALINOWSKI wrote:

However, the program can, in fact, have multiple Perl interpreters, running simultaneously. This in itself does not cause problems, but one thing happened when I tried to create a subclassed object deriving from Gtk2::Window, using Glib::Object::Subclass to do that.

Oh dear...  was that the sound of a large can of worms opening?


The first time the file that defines this new subclassed widget is loaded, everything works perfectly, and it can be used normally. However, if I try to load another interpreter, and to load the file again in this new interpreter, I get some error messages saying that the object cannot be registered, because it already it --- what makes sense, naturally.

Yes, because there's one glib type system being shared by all of the code in the program, and gtk2-perl creates new types with g_type_register_static().


My (first) question is if there is a way to check if the type is already registered, and if it is, to ignore the registration process. This probably would mean that I would not be able to use Glib::Object::Subclass and I would need to do things manually, but that's OK.

If you've peeked at the code of Glib::Object::Subclass, you know that it's just a thin syntactic wrapper around Glib::Type->register_object().

What you describe is pretty easy:

  package MyObject;

  eval {
      Glib::Type->list_ancestors (__PACKAGE__);
      # if we're alive here, the package is already registered.
  } or Glib::Type->register_object ('ParentClass',
                                    __PACKAGE__,
                                    signals => { ... },
                                    ...);

  # all normal method implementations follow...


However, this brings another question: would that work? Even if the registration process were skipped for the second time the object is defined, there might be references somewhere to things of the first interpreter, and then trying to use the subclassed object from another interpreter would not work.

To quote a jedi master, your insight serves you well.

When registering new classes, Glib::Type::register_object() calls g_type_register_static(), which tells the glib type system that this class will always be around.

Gtk2-Perl also leaks the class references on various types of GTypeClasses. For classes registered from perl code, the design decision was "well, perl packages simply exist from the time the parser hits them until the program exits, so we might as well keep the classes alive and not worry about cleaning them up." The reason for worrying about it was that GTypeClasses, once registered, can be instantiated and destroyed multiple times. The class registration is supposed to save all the info you need to instantiate the class (the size of the class and instance structures, the parent type, pointer to a function to initialize each structure). Figuring out how to weld that to perl bent my brain (at the time, the bindings barely worked and this was a fairly esoteric case), and given the assumption above, i decided that it was a non-issue if we just register once and ensure the class is never finalized. Of course, the mistake in that logic is the word "program"... perl packages live until the *interpreter* exits, and there can be more than one interpreter in a program.


But, given all that, let's examine how and if it would be possible to limp along.

The first interpreter to register a class will leave it registered with glib. The next interpreter comes along and tries to use the same object. (Let's ignore the pathological case of your program trying to use a perl-derived type when no interpreter is active.) What would cause this to blow up? Any stored perl data structures. About the only things we store from class registration are closures. For the most part, the rest is done with special names (e.g. "INIT_INSTANCE"), and even the things done with closures can be done with special names instead.

  Glib::Type->register_object
      ('SomeParentObject', 'Foo',
       signals => {
          # new signals...
          flurblize => {
class_closure => \&_flurblize, # saves a closure reference
          },
          frobnicate => {},  # doesn't store any perl data structures
# the method named do_frobnicate will be invoked
                           # as the class closure, if it exists

          # signal overrides
          parent_method1 => \&_override_1, # saves a closure reference
          parent_method2 => '_override_2', # ack!  still saves an SV,
# which just happens to be a string instead of a # reference to a subroutine. (see add_signals()
                           # in GType.xs)
# so, er, it looks like there's no way to override signal closures
          # without saving a reference.  the ugly workaround is to use
          # signal_connect in INIT_INSTANCE.  :-(
       },

       properties => [
           # this one stores only C data structures; the
           # GET_PROPERTY and SET_PROPERTY marshaling
           # happens at runtime.
Glib::ParamSpec->int ('a', 'A', 'A', 0, 10, 4, G_PARAM_READWRITE),
           # the new-fangled inline version stores subroutine references
           {
               pspec=>Glib::ParamSpec->int (...),
               getter => sub {},
               setter => sub {},
           }
       ],

interfaces => [ 'Coolio' ], # alters a class structure, but stores
                                    # no references
       );


Armed with this knowledge and the "Test for already-registered class" snippet from above, you should be able to experiment and find out if you can get it to work.


Slightly more evil, what if you have an object that outlives the interpreter? It will still have a perl data structure attached to it. I ran into this in a binding situation not long ago; i had a C library that implemented a cache of singletons, and one of the objects stored in that cache was a perl-derived type... the object didn't get destroyed until an atexit handler that ran after main() returned, well after the interpreter and its heap were gone, so the app segfaulted perl in shutdown when the object's finalizer tried to invoke some perl code. (I got around this by moving the implementation of the instance cache functionality to perl.)


And, rather more insidious: while the code snippet above shows you what does and doesn't store perl references, it doesn't show that nearly all of this stuff installs into glib pointers to functions defined in the shared objects that implement the bindings. Something to which i don't know the answer is "does perl unload the shared objects that it loaded for its extensions?" if so, then it's likely that the function pointers point to invalid memory... However, if you're building the interpreter into your program, you probably have the option to statically link the Gtk2 extension to it, don't you?


In summary, I don't think it's impossible, you'll just have to be really careful, and feedback from your experiments may have to be incorporated into the bindings.



There is a g_type_register_dynamic(), but it is for types defined in dynamically-loadable plugins. Basically, your app knows somehow (either through scanning a modules directory or reading a cache file or something) that the module coolthing.so defines and implements the GType CoolThing; using g_type_register_dynamic() lets the type system know that when somebody wants to instantiate an object of that type, it must load and initialize coolthing.so. I suppose it would be possible to implement a GTypeModule that launches a perl interpreter, but this was way beyond the scope of what i was prepared to take into consideration when trying to get the bindings simply to work at all, and this isn't the current conventional usage of embedded perl interpreters.


--
"There's a documentary that i wanted to watch on PBS. I heard about it in NPR. ... Oh my god, did i just say that?"
  -- elysse




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]