After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 776092 - Assertion in simple GLib.Variant usage
Assertion in simple GLib.Variant usage
Status: RESOLVED FIXED
Product: pygobject
Classification: Bindings
Component: gobject
Git master
Other Linux
: Normal normal
: ---
Assigned To: Nobody's working on this now (help wanted and appreciated)
Python bindings maintainers
Depends on:
Blocks:
 
 
Reported: 2016-12-14 12:19 UTC by Dan Nicholson
Modified: 2016-12-22 07:33 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Handle multiple deinit of callable cache (1.99 KB, patch)
2016-12-21 18:15 UTC, Dan Nicholson
committed Details | Review
Handle exception unreffing Variant at exit (1.89 KB, patch)
2016-12-21 18:15 UTC, Dan Nicholson
committed Details | Review

Description Dan Nicholson 2016-12-14 12:19:33 UTC
Maybe my usage of GLib.Variant is too naive here, but I can trivially cause a critical assertion by running the following:

$ python3 -c 'from gi.repository import GLib; v = GLib.Variant("s", "foo")'

(process:27857): GLib-CRITICAL **: g_hash_table_destroy: assertion 'hash_table != NULL' failed

(process:27857): GLib-CRITICAL **: g_hash_table_destroy: assertion 'hash_table != NULL' failed
Exception ignored in: 

Interestingly, if I build pygobject against python2 (or use my distro's package), I don't see this assertion. I suppose this comes from the explicit unref in Variant's __del__, but I don't understand what happens exactly.

I can avoid this issue by deleting v before exit or sinking the ref (I guess really just taking another ref since pygobject does sink the ref and is_floating() tells me it's not). Anyway, here's the backtrace:

(gdb) r
Starting program: /usr/bin/python3 -c from\ gi.repository\ import\ GLib\;\ v\ =\ GLib.Variant\(\"s\",\ \"foo\"\)
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

(process:28487): GLib-CRITICAL **: g_hash_table_destroy: assertion 'hash_table != NULL' failed

Program received signal SIGTRAP, Trace/breakpoint trap.
_g_log_abort (breakpoint=breakpoint@entry=1) at /usr/src/packages/BUILD/glib2.0-2.50.2+dev22.0cb2266/./glib/gmessages.c:487
487	/usr/src/packages/BUILD/glib2.0-2.50.2+dev22.0cb2266/./glib/gmessages.c: No such file or directory.
(gdb) bt
  • #0 _g_log_abort
    at /usr/src/packages/BUILD/glib2.0-2.50.2+dev22.0cb2266/./glib/gmessages.c line 487
  • #1 g_logv
    at /usr/src/packages/BUILD/glib2.0-2.50.2+dev22.0cb2266/./glib/gmessages.c line 1296
  • #2 g_log
    at /usr/src/packages/BUILD/glib2.0-2.50.2+dev22.0cb2266/./glib/gmessages.c line 1337
  • #3 _callable_cache_deinit_real
    at ../../gi/pygi-cache.c line 689
  • #4 _callable_cache_init
    at ../../gi/pygi-cache.c line 744
  • #5 _function_cache_init
    at ../../gi/pygi-cache.c line 811
  • #6 pygi_method_cache_new
    at ../../gi/pygi-cache.c line 1014
  • #7 pygi_method_cache_new
    at ../../gi/pygi-cache.c line 1028
  • #8 _wrap_g_callable_info_invoke
    at remote 0x7ffff69325e8>,), kwargs=kwargs@entry=0x0) at ../../gi/pygi-invoke.c line 751
  • #9 _callable_info_call
  • #10 PyEval_EvalFrameEx
  • #11 PyEval_EvalFrameEx
    at ../Python/ceval.c line 4466
  • #12 PyEval_EvalFrameEx
    at ../Python/ceval.c line 4264
  • #13 PyEval_EvalFrameEx
    at remote 0x7ffff69325e8>), throwflag=<optimized out>) at ../Python/ceval.c line 2838
  • #14 function_call.lto_priv.435
    at ../Python/ceval.c line 3588
  • #15 function_call.lto_priv.435
    at ../Objects/funcobject.c line 632
  • #16 method_call.lto_priv.356
    at remote 0x7ffff69325e8>,), func=<function at remote 0x7ffff4858f28>) at ../Objects/abstract.c line 2058
  • #17 method_call.lto_priv.356
    at remote 0x7ffff69325e8>,), kw=0x0) at ../Objects/classobject.c line 347
  • #18 PyEval_CallObjectWithKeywords
  • #19 PyEval_CallObjectWithKeywords
  • #20 slot_tp_finalize
    at ../Objects/typeobject.c line 6251
  • #21 PyObject_CallFinalizerFromDealloc
    at remote 0x7ffff69325e8>) at ../Objects/object.c line 278
  • #22 PyObject_CallFinalizerFromDealloc
    at remote 0x7ffff69325e8>) at ../Objects/object.c line 295
  • #23 subtype_dealloc.lto_priv.2156
    at remote 0x7ffff69325e8>) at ../Objects/typeobject.c line 1139
  • #24 free_keys_object.lto_priv.1405
    at ../Objects/dictobject.c line 369
  • #25 dict_dealloc.lto_priv.222
    at ../Objects/dictobject.c line 1391
  • #26 module_dealloc.lto_priv.252
    at ../Objects/moduleobject.c line 398
  • #27 PyDict_SetItem
    at ../Objects/dictobject.c line 824
  • #28 PyDict_SetItem
  • #29 PyImport_Cleanup
    at ../Python/import.c line 469
  • #30 Py_Finalize
    at ../Python/pythonrun.c line 616
  • #31 Py_Main
    at ../Modules/main.c line 806
  • #32 main
    at ../Modules/python.c line 69
  • #33 __libc_start_main
    at libc-start.c line 287
  • #34 _start

Comment 1 Dan Nicholson 2016-12-14 12:25:02 UTC
I should mention that when I use GLib.Variant in a real program, I don't see this problem. It really only happens if I exit immediately after creating the variant and storing it in a variable.
Comment 2 Dan Nicholson 2016-12-21 18:15:36 UTC
Created attachment 342345 [details] [review]
Handle multiple deinit of callable cache

In python3, it seems that the callable cache deinit can be called
multiple times when the program is exiting. Make that safer by clearing
the various pointers in the structure using g_clear_pointer and
Py_CLEAR. A subsequent call will skip all the deinit by seeing NULL
pointers for the members.

This was causing a critical warning when destroying the arg name hash
table multiple times with the following trivial program:

$ python3 -c 'from gi.repository import GLib; v = GLib.Variant("s", "foo")'

(process:32378): GLib-CRITICAL **: g_hash_table_destroy: assertion 'hash_table != NULL' failed

(process:32378): GLib-CRITICAL **: g_hash_table_destroy: assertion 'hash_table != NULL' failed
Comment 3 Dan Nicholson 2016-12-21 18:15:40 UTC
Created attachment 342346 [details] [review]
Handle exception unreffing Variant at exit

Calling unref will cause gi and gi.repository.GLib to be imported.
However, if the program is exiting, then these modules have likely been
removed from sys.modules and will raise an exception. Assume that's the
case for ImportError and ignore the exception since everything will be
cleaned up, anyways.

This can be triggered with the following trivial program:

$ python3 -c 'from gi.repository import GLib; v = GLib.Variant("s", "foo")'
Exception ignored in:

Adding some debug code to show the full exception revealed this:

Traceback (most recent call last):
  • File "/home/dan/src/pygobject/build3/gi/overrides/GLib.py", line 265 in __del__
    self.unref()
ImportError: import of 'gi.repository.GLib' halted; None in sys.modules

Comment 4 Dan Nicholson 2016-12-21 18:22:28 UTC
These 2 patches fix things for me. I'm pretty sure they're right, and I ran this on some other pygi scripts I had around. The test suite still passes for me. That said, it's dealing with some internals of pygobject that I'm not really familiar with.
Comment 5 Simon Feltman 2016-12-22 01:45:23 UTC
Review of attachment 342345 [details] [review]:

Looks good, thanks. Pushed as:
https://git.gnome.org/browse/pygobject/commit/?id=54c623ba6396
Comment 6 Simon Feltman 2016-12-22 07:31:22 UTC
The following fixes have been pushed: