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

GC-resistant GObject subclasses #64

Closed
wants to merge 39 commits into from

Conversation

BlobCodes
Copy link
Contributor

This PR allows GObject subclasses to be GC-resistant.
Actually, the biggest part of this PR is allowing GObjects to be created from C (g_object_new) without fearing the GC, which requires creating the crystal object from C and retrieving it in the constructor, not creating the crystal object and then setting the pointer.
This time, the user does not need to create multiple classes, manage references (or something similar) becuase the PR uses toggle refs.
A toggle ref is basically a reference telling us whether there are other references besides it or not. If other references exist, we put a pointer to the object in some global array or remove it otherwise.

Additionally, the last two commits add support for defining one initialize method per class. The arguments of the method are exposed as construct-only GObject properties, so you can set them even from C. Because GObject properties alone are maybe not as versatile as some classes require it, you can also use the new RefProp annotation. Here's how this looks in action:

private class UserObjectWithCtor < GObject::Object
  getter string : String?
  getter test_union : Int32 | Bool

  private def initialize(*, @string : String?, @[GObject::RefProp] @test_union : Int32 | Bool = true, @[GObject::RefProp] private_class : UserObject? = nil, &)
  end
end

What you can see is:

  • Every initialize method needs to accept a block and be private, but that's just to ensure it is not called instead of the self.new method.
  • There's one GObject property (@string), which you would be able to set even in a gtk .ui file.
  • There are two args annotated with GObject::RefProp. For these args, only a Pointer(Void) to a variable is transferred via a GObject property. With this mechanism, most crystal types can be transferred over GObject for constructing.

You can now initialize it:

UserObjectWithCtor.new(some_c_property: 2, test_union: 19)

This self.new method allows setting all GObject properties (defined in C or Crystal) and all initialize arguments in one method.


As a little demonstration, I have created a blueprint for an app I am currently working on:

using Gtk 4.0;

template Mangaba-UI-ComicListItem : Gtk.Box {
  orientation: vertical;

  Gtk.Overlay {
    margin-bottom: 4;

    .Mangaba-UI-ComicCover {
      texture: bind Mangaba-UI-ComicListItem.texture;
      comic-size: Thumbnail;
      halign: center;
      styles ["card"]
    }

    [overlay]
    Gtk.CheckButton {
      halign: end;
      valign: end;
      visible: bind Mangaba-UI-ComicListItem.is-selection-mode;
      styles ["selection-mode"]
    }
  }

  Gtk.Label {
    label: bind Mangaba-UI-ComicListItem.title;
    wrap: true;
    justify: center;
    lines: 2;
    ellipsize: middle;
    wrap-mode: word_char;
    max-width-chars: 15;
  }

  Gtk.Label {
    label: bind Mangaba-UI-ComicListItem.subtitle;
    ellipsize: middle;
    max-width-chars: 15;
    styles ["dim-label", "caption"]
  }
}

Which results in this window:
Bildschirmfoto vom 2022-09-09 20-51-11


To allow this PR to work, multiple breaking changes had to be made and I expect most GObject subclasses to break.

The object needs an additional reference before it is passed  to GObject::Object.new because a full transfer means that the recipient owns a ref on the value.
This reference manager allows checking for memory leaks in the specs as its behaviour is deterministic.
BlobCodes added a commit to BlobCodes/gtk4.cr that referenced this pull request Sep 11, 2022
hugopl pushed a commit to hugopl/gtk4.cr that referenced this pull request Sep 11, 2022
@hugopl
Copy link
Owner

hugopl commented Sep 11, 2022

Nice, I'll try to review this in detail this week.

If I understood correctly the pros and cons are:
pros:

  • GC-resistant GObject subclasses 🎉 .

cons:

  • Crystal object can't have multiple initialize methods and the initialize method must follow some rules.
  • API breakage

@BlobCodes
Copy link
Contributor Author

BlobCodes commented Sep 11, 2022

Crystal object can't have multiple initialize methods and the initialize method must follow some rules.

Yes, but you can have multiple self.new methods - which in turn must call the initialize method, so it's not that big a deal.

The other ones - yes. Additionally I'd say being able to initialize from C is more important than GC-resistancy because you can use gtk builder.

@hugopl hugopl linked an issue Jun 30, 2023 that may be closed by this pull request
@hugopl
Copy link
Owner

hugopl commented Jul 6, 2023

I'll close this MR, despite of it include other features besides GC resistant objects, the main aim was GC resistance.

Closed in favor of #107

@hugopl hugopl closed this Jul 6, 2023
@BlobCodes BlobCodes deleted the gc-resistant-subclasses-4 branch September 2, 2023 13:36
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

Successfully merging this pull request may close these issues.

Support GC resistant GObject subclasses
2 participants