spk121 / guile-gi

Bindings for GObject Introspection and libgirepository for Guile
GNU General Public License v3.0
58 stars 7 forks source link

Lots of warnings at startup: invalid param spec type 'GParamObject' for value type 'GParam' #88

Open daym opened 4 years ago

daym commented 4 years ago

When starting up an application with a main window and main loop I get hundreds of warnings like the following:

GLib-GObject-WARNING **: 13:52:00.697: ../glib-2.62.6/gobject/gsignal.c:3381: invalid param spec type 'GParamObject' for value type 'GParam'

I've looked into it a bit. This is g_signal_emit_valist verifying that the varargs passed conform to the types of the signal parameters.

In this case, the type of the signal parameter is GParamObject, but the GValue of the specific vararg passed by the user (in this case that's g_object_dispatch_properties_changed, via g_object_notify, via _gdk_display_manager_add_display), after being mangled by G_VALUE_COLLECT_INIT macro, has type GParam. This part of g_signal_emit_valist is not in the fast path.

glib documents:

define G_VALUE_HOLDS_PARAM(value) Checks whether the given #GValue can hold values derived from type %G_TYPE_PARAM.

So the actual GValue value could be derived from GParam and it should be fine.

My source files do not directly install any custom user objects or signals.

Gtk used is gtk+-3.24.20, glib is glib-2.62.6.

The glib call in question uses g_param_spec_pool_lookup to get the pspec, and then uses g_object_notify_by_spec_internal to notify. The pool got an entry for the pspec by g_object_class_install_property, the latter of which should always use g_param_spec_object or similar ways to construct a GParamObject or similar derived class instance.

daym commented 4 years ago

It also happens with examples/editor.scm from master.

daym commented 4 years ago

And the "notify" signal (in glib) is the following:

   gobject_signals[NOTIFY] =
    g_signal_new (g_intern_static_string ("notify"),
                  G_TYPE_FROM_CLASS (class),
                  G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GObjectClass, notify),
                  NULL, NULL,
                  NULL,
                  G_TYPE_NONE,
                  1, G_TYPE_PARAM);

(especially note G_TYPE_PARAM here)

Should be fine; documentation states that G_VALUE_HOLDS_PARAM would be fine with receiving an item that's derived from GParam, too.

daym commented 4 years ago

I think GParamObject is supposed to house a reference-counted GObject. I suspect what this is trying to prevent is people abusing GValue (which is usually on the stack) to store a reference to a GObject--which usually is on the heap. GValue does not have a destructor, so the reference count of the referred-to-GObject would go bad in short order.

If that is true then that means this whole mechanism glib is using can't be used for signals or properties, which can likely be GObjects. At least not if GValue is used to access those. So I might be wrong, especially since g_signal_emit_valist is also used by g_signal_emit which probably doesn't have such limitations.

LordYuuma commented 4 years ago

Is this running a traditional Linux distro or GNU Guix?

daym commented 4 years ago

It's GNU Guix.

GLIb 2.62.6. Gtk+ 3.24.20.

I use this horrible start script so I don't have to reinstall guile-gi every time I change something:

XDG_DATA_DIRS=/gnu/store/7lcw9yr14gdw8nszj798gfl2h7if7grv-gajim-1.1.3/share:/home/dannym/.guix-profile/share:/run/current-system/profile/share:/home/dannym/.guix-profile/share:/run/current-system/profile/share
GIO_EXTRA_MODULES=/home/dannym/.guix-profile/lib/gio/modules:/run/current-system/profile/lib/gio/modules:/gnu/store/2wa4y9cgw9gm3f507jv3n49rprh2qf4v-dconf-0.34.0/lib/gio/modules
GTK_PATH=/gnu/store/4is168fn75ln9w9nl3xa4gff4fi35z83-libcanberra-0.30/lib/gtk-3.0:/gnu/store/ppm4ymy7gg34iairswb25s347shi843r-gtk+-3.24.20/lib/gtk-3.0
export GIO_EXTRA_MODULES
export XDG_DATA_DIRS
export GTK_PATH

# --search-paths
guix environment -l guix.scm --ad-hoc guile gdk-pixbuf adwaita-icon-theme shared-mime-info -- make
tools/uninstalled-env tools/run-guile "$@"

guile is guile 3.

LordYuuma commented 4 years ago

You are probably experiencing the same problem we experience in

https://github.com/spk121/guile-gi/blob/89babf64759be7b700477f9b7fb3a84c5c4731d7/test/marshall.scm#L751-L767

or something similar to it. Basically, it is possible to initialize GLib/GObject twice, which generates a <GObject> class, that does not spawn from G_TYPE_OBJECT, among other things, and that's just one possible misconfiguration. As far as I am aware, this is not a bug in Guile-GI, but in your specific GI setup. I once wrote otherwise perfectly fine C code, that results in exactly the same set of errors thanks to some weirdness of Guix/GLib.

There are some things you can do to mitigate them. Loading the GLib/GObject typelib before that of Gtk mitigates one class of errors. Pure environments also help, but they're no silver bullet, sadly. I haven't tried containers yet.

daym commented 4 years ago

Is there a bug report in Guix about it? That sounds horrible.

(I'm currently reviewing a glib update in Guix--maybe that would fix it, maybe not)

LordYuuma commented 4 years ago

Not as far as I'm aware. My MWE sadly got lost to the hell that is /tmp. There are a number of packages in Guix, that work around this and similar issue(s) by specifically setting GI_TYPELIB_PATH using "=" or "suffix" rather than "prefix". I think, this is because GObject-Introspection is a pseuso-requirement to get conflicting versions of GLib/GObject loaded in the first place.

LordYuuma commented 4 years ago

The warnings in editor.scm disappear when ("GObject" "2.0") is loaded first. Does this also work for you?

daym commented 4 years ago

I just tried--yes, this works for me.

Still worrying that this happened in the first place. What is going on?

LordYuuma commented 4 years ago

I believe this happens when calling init! from Gtk before the GObject stuff is initialized, which happens at some point between require and the end of typelib->module. Guile-GI potentially allows a different GObject typelib to be loaded, so we end up with UB in the params.

romgrk commented 4 years ago

Not sure if this applies but in case it's useful to you: in node-gtk, we use g_irepository_get_dependencies to load all dependencies before the required module is loaded. So our require function recursively requires dependencies before loading a module itself.

Simplified version:

function giRequire(ns, version) {
    if (moduleCache[ns])
        return moduleCache[ns]
    // ...
    loadDependencies(ns, version)
    // ...
}

function loadDependencies(ns, version) {
    const repo = GI.Repository_get_default()
    const dependencies = GI.Repository_get_dependencies.call(repo, ns, version)

    dependencies.forEach(dependency => {
        const [name, version] = dependency.split('-')
        giRequire(name, version)
    })
}
LordYuuma commented 4 years ago

For a little more context, I think I've isolated the wall of warnings pretty well with the following:

(use-modules (gi) (gi repository))

(require "GObject" "2.0")
;; Uncomment and warnings vanish
;; (load-by-name "GObject" "Object")

;; Swap comments and see warnings (dis)appear
(typelib->module (current-module) "Gtk" "3.0")
;; (require "Gtk" "3.0")
;; (load-by-name "Gtk" "init")

(init!)

I think in your case giRequire does the work, that typelib->module does in ours, only that we're lacking the recursive dependency stuff. We could get that into Scheme by loading the GI repository as you do, or we could write our own wrapper around g_irepository_get_dependencies. @spk121 WDYT?

Btw. I think your node module duplicates a bit of work. g_irepository_get_dependencies returns a transitive closure, so there should be no need to have corecursive calls.