Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remember existing Ruby wrappers for GObjects #63

Open
mvz opened this issue Oct 25, 2015 · 12 comments
Open

Remember existing Ruby wrappers for GObjects #63

mvz opened this issue Oct 25, 2015 · 12 comments

Comments

@mvz
Copy link
Owner

mvz commented Oct 25, 2015

If a Ruby wrapper already exists for a given GObject, GirFFI should not create a new wrapper but instead return the existing one. Ruby-GNOME2 and PyGobject store the wrapper in the GObject's qdata.

Keeping this wrapper around allows storing the properties for a derived type as Ruby instance variables instead of extending the C struct of the parent type.

@kugel-
Copy link

kugel- commented Nov 11, 2016

I'm struggling with this one. Is there a solution on the horizon?

@mvz
Copy link
Owner Author

mvz commented Nov 12, 2016

I'm afraid I have nothing planned at the moment.

@kugel-
Copy link

kugel- commented Nov 12, 2016

I'm playing with ClassBase::direct_wrap to maintain a lookup table of previous object allocations (indexed by ptr.address). I can't seem to make it work though.

@mvz
Copy link
Owner Author

mvz commented Nov 12, 2016

Once this is working I can stop storing user-defined properties in C struct fields, which should reduce the some of the ref_count increases that happen in #82.

@kugel-
Copy link

kugel- commented Nov 12, 2016

I'd be willing to work on this but I don't know where to start. ClassBase::direct_wrap doesn't seem to be the right place. When I store the allocated object in a hash (and do the same in ClassBase#assign_pointer) and return that on subsequent allocations I get an error when calling the interface: (/home/kugel/dev/libpeas.git/tests/libpeas/.libs/lt-extension-rbffi:2318): GLib-GObject-CRITICAL **: Object class ExtensionRbFFI doesn't implement property 'object' from interface 'PeasActivatable'. Somehow the class information gets lost.

@kugel-
Copy link

kugel- commented Nov 12, 2016

The below, basic patch seems to help. I can observe that my instance is re-used and its instance variable survive (the native code creates the object and then immediately calls a interface vfunc). I'm sure it's not complete yet. I guess at least removing from @object_cache must be implemented to avoid reference cycles.

What do you think of it?

diff --git a/lib/gir_ffi/class_base.rb b/lib/gir_ffi/class_base.rb
index 4b401bc..0e71939 100644
--- a/lib/gir_ffi/class_base.rb
+++ b/lib/gir_ffi/class_base.rb
@@ -11,6 +11,13 @@ module GirFFI
     extend Forwardable

     GIR_FFI_BUILDER = Builders::NullClassBuilder.new
+    @object_cache = {}
+
+    def self.inherited(klass)
+      klass.class_eval do
+        @object_cache = {}
+      end
+    end

     attr_reader :struct
     def_delegators :@struct, :to_ptr
@@ -73,7 +80,11 @@ module GirFFI
     # do any casting to subtypes or additional processing.
     def self.direct_wrap(ptr)
       return nil if !ptr || ptr.null?
-      obj = allocate
+      # try to use existing object from cache
+      unless obj = @object_cache[ptr.address]
+        obj = allocate
+        @object_cache[ptr.address] = obj
+      end
       obj.__send__ :assign_pointer, ptr
       obj
     end
@@ -100,7 +111,12 @@ module GirFFI
     end

     def assign_pointer(ptr)
+      _self = self
       @struct = self.class::Struct.new(ptr)
+      self.class.class_eval do
+        @object_cache[ptr.address] = _self
+      end
+      @struct
     end
   end
 end

@mvz
Copy link
Owner Author

mvz commented Nov 12, 2016

Let's back up a little bit. Can you show me what you need this feature for? My original intention was to use it to be able to store properties as Ruby instance variables, but with the recent enhancements of the user-defined properties that seems less urgent. So, can you show me a bit more of what you're trying to achieve?

@mvz
Copy link
Owner Author

mvz commented Nov 12, 2016

Your patch looks reasonable, by the way.

@kugel-
Copy link

kugel- commented Nov 13, 2016

Can you show me what you need this feature for?

I'll describe my project, hopefully that explain this and other requirements.

If you don't know libpeas: It's a library originated in the GNOME world that implements a plugin interface and loaders for non-C languages. C-based programs can use this, other ones too since libpeas also provides introspection data for itself. Programs using this library can easily support written in other languages, most notably python and lua at the moment. I'm working on adding Ruby to the list. Some prominent users of libpeas are gedit, eog, gitg and Geany (through a plugin that I developed).

So, for a libpeas loader there are some requirements: 1) Usable objects can be created from native code; the native code works with the GObject (or derived) pointer. 2) The objects implement GObject interfaces, 3) the loaders have some bindings to the GTK stack, preferably GI-based as libpeas does not provide such by itself. The bindings can be gir_ffi or ruby-gnome2 but I'm focussing on gir_ffi.

For 1, there is a specialized function that the programs call (peas_engine_create_extension()), but once created this way the plugin code is only executed through interface methods, bypassing libpeas, where the programs call it directly (e.g. peas_activatable_activate() which is a method of the PeasActivatable interface).

For 2, Ruby context is only entered through the interface methods (setup by gir_ffi). Between those invocations the proxy object (Ruby object here) must maintain state.

My current state is that I call into Ruby when an extension is created. The backing loader implementation of peas_engine_create_extension() calls create_extension() in a Ruby helper script, which in turn does the usual allocate and send :initialze pattern. The loader then extracts the GObject * through #to_ptr.address

I've pushed my current state onto github, see https://github.com/kugel-/libpeas/tree/ruby. The tests give some idea how the library is used, for example here: https://github.com/kugel-/libpeas/blob/ruby/tests/libpeas/extension-rbffi.c#L52

@mvz
Copy link
Owner Author

mvz commented Nov 14, 2016

@kugel- thanks, I'll take a look.

@mvz
Copy link
Owner Author

mvz commented Jan 6, 2017

Between those invocations the proxy object (Ruby object here) must maintain state.

Just a note: My intent was always that subclasses defined in Ruby should still store all their state in properties, which are stored in C data. This is a little more complicated than using instance variables, but it should work. Ruby-GNOME uses the other approach and stores even properties as ivars.

@kugel-
Copy link

kugel- commented Oct 2, 2017

I'd like to come back to this. What do you think is missing from the patch I posted above?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants